BT

Disseminando conhecimento e inovação em desenvolvimento de software corporativo.

Contribuir

Tópicos

Escolha a região

Início Artigos O Deno ama WebAssembly

O Deno ama WebAssembly

This item in japanese

Favoritos

Pontos Principais

  • O Deno e o Node.js executam JavaScript em tempo de execução baseados em C/C++ para ter alto desempenho;

  • O Deno é uma aplicação binária incompatível com módulos NPM, e que não tem uma maneira fácil de incorporar módulos nativos nas aplicações;

  • O WebAssembly fornece uma maneira de executar código de alto desempenho nas aplicações Deno;

  • O WebAssembly é um container seguro, portátil e leve para aplicações do lado servidor;

  • As ferramentas do compilador Rust oferecem excelente suporte para o WebAssembly.

O aguardado projeto Deno finalmente atingiu sua versão 1.0! O Deno foi criado pelo desenvolvedor do Node.js, Ryan Dahl, para abordar o que chamou de "10 coisas que lamento sobre o Node.js".

O Deno acabou com o NPM e os reprováveis node_modules. Ele é um binário único que executa aplicações escritas em TypeScript e JavaScript.

No entanto, embora o TypeScript e o JavaScript sejam adequados para a maioria das aplicações web, eles podem ser inadequados para tarefas mais intensas, como treinamento e inferência de rede neural, aprendizado de máquina e criptografia. Na verdade, o próprio Node.js muitas vezes precisa de bibliotecas nativas para essas tarefas, como o OpenSSL, no caso de criptografia.

Sem um sistema semelhante ao NPM para incorporar módulos nativos, como podemos escrever aplicações do lado do servidor que requerem desempenho nativo no Deno? O WebAssembly está aqui para ajudar! Neste artigo, iremos escrever funções de alto desempenho usando o Rust, depois, vamos compilá-las em WebAssembly, e executá-las dentro da aplicação Deno.

TL;DR

Clone ou faça um fork deste modelo simples de projeto do Deno no GitHub. Siga as instruções e terá a primeira função WebAssembly (criada em Rust) em execução no Deno em apenas 5 minutos.

Um pouco de história

O Node.js é muito bem-sucedido porque deu aos desenvolvedores o melhor dos dois mundos: A facilidade de uso do JavaScript, especialmente para aplicações assíncronas baseadas em eventos, e o alto desempenho do C/C++. As aplicações Node.js são escritas em JavaScript, mas são executadas em um tempo de execução em C/C++ nativo, incluindo o mecanismo Google V8 JavaScript e muitos módulos de biblioteca nativa. O Deno pretende repetir esta fórmula para o sucesso, mas no processo, suporta uma pilha de tecnologia moderna com o TypeScript e o Rust.

O Deno é um runtime simples, moderno e seguro para JavaScript e TypeScript, que usa o V8, e é construído em Rust. O site do Deno é deno.land.

Na famosa apresentação, "10 coisas que lamento sobre o Node.js", o criador do Node.js, Ryan Dahl, explicou a razão para recomeçar e criar o Deno como um concorrente, ou até mesmo, como sendo um substituto do Node.js. Os arrependimentos de Dahl estão centrados em como o Node.js gerencia os códigos e os módulos de terceiros.

  • O complexo sistema de build para vincular módulos C ao Node.js;
  • As complexidades desnecessárias do package.json, node_modules, index.js e outros artefatos NPM.

Como resultado, o Deno faz algumas escolhas muito conscientes e opinativas para gerenciar as dependências.

  • O Deno é um único executável binário;
  • Os aplicativos são criados em TypeScript ou JavaScript com dependências explicitamente declaradas no código como instruções de import, com link da URL completa para o código-fonte da dependência;
  • O Deno não é compatível com módulos Node.js.

Mas e as aplicações que exigem alto desempenho, como aplicações de IA como serviço que precisam executar modelos de rede neural complexos em frações de segundos? No Deno e no Node.js, muitas funções são chamadas por meio de uma API TypeScript ou JavaScript, mas são executadas como código nativo criado em Rust ou C. No Node.js, sempre há a opção de chamar bibliotecas nativas de terceiros a partir do API JavaScript. Mas será que podemos fazer isso com Deno?

Suporte para WebAssembly no Deno

O WebAssembly é uma máquina virtual leve projetada para executar bytecodes portáteis próximo à velocidade nativa. Podemos compilar funções Rust ou C/C++ para os bytecodes WebAssembly e acessar essas funções a partir do TypeScript. Para algumas tarefas, pode ser muito mais rápido do que executar funções equivalentes criadas no próprio TypeScript. Por exemplo, este estudo da IBM descobriu que o Rust e o WebAssembly podem melhorar a velocidade de execução do Node.js em 1200% a 1500% para certos algoritmos de processamento de dados.

O Deno usa o motor V8 interno do Google. O V8 não é apenas um runtime em JavaScript, mas também uma máquina virtual WebAssembly, que é compatível com o Deno pronto para uso. O Deno fornece uma API para sua aplicação TypeScript para chamar funções em WebAssembly.

Na verdade, alguns componentes Deno populares já estão implementados no WebAssembly. O módulo sqlite em Deno é criado compilando o código-fonte C do sqlite em WebAssembly usando Emscripten. O componente WASI do Deno permite que as aplicações WebAssembly acessem os recursos internos do sistema operacional, como o sistema de arquivos. Neste artigo, vamos ensinar a escrever aplicações Deno de alto desempenho em Rust e WebAssembly.

Configuração

O primeiro passo, claro, é instalar o Deno! Na maioria dos sistemas, é apenas um único comando.

$ curl -fsSL https://deno.land/x/install/install.sh | sh

Como estamos escrevendo funções no Rust, precisamos instalar também os compiladores e ferramentas desta linguagem.

$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

Finalmente, a ferramenta ssvmup automatiza o processo de construção e gera todos os artefatos tornando mais fácil as chamadas da aplicação Deno as funções do Rust. Novamente, em um único comando instalamos a dependência ssvmup.

$ curl https://raw.githubusercontent.com/second-state/ssvmup/master/installer/init.sh -sSf | sh

Nota: O ssvmup usa wasm-bindgen para gerar automaticamente o código "cola" entre o código-fonte JavaScript e Rust, para que possam se comunicar usando os tipos de dados nativos. Sem ele, os argumentos da função e os valores de retorno seriam limitados a tipos muito simples (ou seja, inteiros de 32 bits) suportados nativamente pelo WebAssembly. Por exemplo, strings ou arrays não seriam possíveis sem o ssvmup e o wasm-bindgen.

Hello world

Para começar, vamos dar uma olhada em um exemplo simples de hello world com base no hello world do Deno. Podemos obter o código-fonte e o modelo da aplicação hello world no GitHub.

A função Rust está no arquivo src/lib.rs e simplesmente acrescenta "hello" a uma string de entrada. Observe que a função say() está anotada com #[wasm_bindgen], permitindo que ssvmup gere o "encanamento" necessário para chamá-la do TypeScript.

#[wasm_bindgen]
pub fn say(s: &str) -> String {
  let r = String::from("hello ");
  return r + s;
}

A aplicação Deno existe no arquivo deno/server.ts. A aplicação importa a função Rust say() do arquivo pkg/functions_lib.js, que é gerado pela ferramenta ssvmup. O nome do arquivo functions_lib.js depende do nome do projeto Rust definido no arquivo Cargo.toml.

import { serve } from "https://deno.land/std/http/server.ts";
import { say } from '../pkg/functions_lib.js';
 
type Resp = {
    body: string;
}
 
const s = serve({ port: 8000 });
console.log("http://localhost:8000/");
for await (const req of s) {
  let r = {} as Resp;
  r.body = say (" World\n");
  req.respond(r);
}

Agora, vamos executar o ssvmup para construir a função Rust em uma função Deno WebAssembly.

$ ssvmup build --target deno

Depois que o ssvmup for concluído com êxito, podemos inspecionar o arquivo pkg/functions_lib.js para ver como a API Deno WebAssembly é usada para executar o arquivo WebAssembly compilado pkg/functions_lib.wasm.

Em seguida, execute o aplicativo Deno. O Deno requer permissões para ler o sistema de arquivos, pois precisa carregar o arquivo WebAssembly e para acessar a rede, pois precisa receber e responder às solicitações HTTP.

$ deno run --allow-read --allow-net --allow-env --unstable deno/server.ts

Nota: Se você instalou o Deno no passado e encontrou um erro neste momento, é provável que tenha sido devido a um conflito de versões da biblioteca em cache. Siga essas instruções para recarregar seu cache Deno.

No terminal, podemos acessar o aplicativo da web Deno para fazê-lo dizer olá por meio de uma conexão HTTP!

$ curl http://localhost:8000/
hello  World

Um exemplo mais complexo

O projeto do modelo inicial inclui vários exemplos elaborados para mostrar como passar dados complexos entre as funções do Deno TypeScript e Rust. Aqui estão algumas funções adicionais do Rust, em src/lib.rs. Observe que cada um é anotado com #[wasm_bindgen].

#[wasm_bindgen]
pub fn obfusticate(s: String) -> String {
  (&s).chars().map(|c| {
    match c {
      'A' ..= 'M' | 'a' ..= 'm' => ((c as u8) + 13) as char,
      'N' ..= 'Z' | 'n' ..= 'z' => ((c as u8) - 13) as char,
      _ => c
    }
  }).collect()
}
 
#[wasm_bindgen]
pub fn lowest_common_denominator(a: i32, b: i32) -> i32 {
  let r = lcm(a, b);
  return r;
}
 
#[wasm_bindgen]
pub fn sha3_digest(v: Vec<u8>) -> Vec<u8> {
  return Sha3_256::digest(&v).as_slice().to_vec();
}
 
#[wasm_bindgen]
pub fn keccak_digest(s: &[u8]) -> Vec<u8> {
  return Keccak256::digest(s).as_slice().to_vec();
}

Talvez o mais interessante seja a função create_line(). Ela leva duas strings JSON, cada uma representando uma estrutura Point, e retorna uma string JSON representando uma estrutura Line. Observe que as estruturas Point e Line são anotadas com Serialize e Deserialize para que o compilador Rust gere automaticamente o código necessário para oferecer suporte à conversão de e para strings JSON.

use wasm_bindgen::prelude::*;
use serde::{Serialize, Deserialize};
 
#[derive(Serialize, Deserialize, Debug)]
struct Point {
  x: f32,
  y: f32
}
 
#[derive(Serialize, Deserialize, Debug)]
struct Line {
  points: Vec<Point>,
  valid: bool,
  length: f32,
  desc: String
}
 
#[wasm_bindgen]
pub fn create_line (p1: &str, p2: &str, desc: &str) -> String {
  let point1: Point = serde_json::from_str(p1).unwrap();
  let point2: Point = serde_json::from_str(p2).unwrap();
  let length = ((point1.x - point2.x) * (point1.x - point2.x) + (point1.y - point2.y) * (point1.y - point2.y)).sqrt();
 
  let valid = if length == 0.0 { false } else { true };
  let line = Line { points: vec![point1, point2], valid: valid, length: length, desc: desc.to_string() };
  return serde_json::to_string(&line).unwrap();
}
 
#[wasm_bindgen]
pub fn say(s: &str) -> String {
  let r = String::from("hello ");
  return r + s;
}

A seguir, vamos examinar o programa JavaScript deno/test.ts, que mostra como chamar as funções Rust. Como podemos ver, o String e o /str são simplesmente strings em JavaScript, o i32 são números e o Vec<u8> ou o /[8] são JavaScript Uint8Array. Os objetos JavaScript precisam passar por JSON.stringify() ou JSON.parse() antes de serem passados ou retornados das funções Rust.


 
import { say, obfusticate, lowest_common_denominator, sha3_digest, keccak_digest, create_line } from '../pkg/functions_lib.js';
 
const encoder = new TextEncoder();
 
console.log( say("SSVM") );
console.log( obfusticate("A quick brown fox jumps over the lazy dog") );
console.log( lowest_common_denominator(123, 2) );
console.log( sha3_digest(encoder.encode("This is an important message")) );
console.log( keccak_digest(encoder.encode("This is an important message")) );
 
var p1 = {x:1.5, y:3.8};
var p2 = {x:2.5, y:5.8};
var line = JSON.parse(create_line(JSON.stringify(p1), JSON.stringify(p2), "A thin red line"));
console.log( line );

Depois de executar o ssvmup para construir a biblioteca Rust, executar deno/test.ts no runtime Deno produz a seguinte saída:

$ ssvmup build --target deno
... Building the wasm file and JS shim file in pkg/ ...
 
$ deno run --allow-read --allow-env --unstable deno/test.ts
hello SSVM
N dhvpx oebja sbk whzcf bire gur ynml qbt
246
Uint8Array(32) [
  87, 27, 231, 209, 189, 105, 251,  49,
  ... ...
]
Uint8Array(32) [
  126, 194, 241, 200, 151, 116, 227,
  ... ...
]
{
  points: [ { x: 1.5, y: 3.8 }, { x: 2.5, y: 5.8 } ],
  valid: true,
  length: 2.2360682,
  desc: "A thin red line"
}

Qual é o próximo passo

Agora podemos criar funções Rust e acessá-las a partir de uma aplicação Deno TypeScript. Podemos colocar muitas tarefas computacionalmente intensas em funções Rust e oferecer alto desempenho e serviços web seguros por meio do Deno. Exemplos de tais serviços incluem machine learning e reconhecimento de imagem.

Além disso, podemos acessar os recursos do sistema, como números aleatórios, variáveis de ambiente e o sistema de arquivos por meio da WebAssembly Systems Interface (WASI) da nossa aplicação Deno.

Sobre o autor

Dr. Michael Yuan é autor de cinco livros sobre engenharia de software. Seu livro mais recente, Building Blockchain Apps, foi publicado pela Addison-Wesley em dezembro de 2019. O Dr. Yuan é cofundador da Second State, uma startup que traz as tecnologias WebAssembly e Rust para aplicações em nuvem, blockchain e IA. O Second State permite que os desenvolvedores implantem funções Rust rápidas, seguras, portáteis e sem servidor no Node.js. Fique sabendo das novidades assinando o boletim informativo WebAssembly.Today.

Avalie esse artigo

Relevância
Estilo/Redação

Olá visitante

Você precisa cadastrar-se no InfoQ Brasil ou para enviar comentários. Há muitas vantagens em se cadastrar.

Obtenha o máximo da experiência do InfoQ Brasil.

HTML é permitido: a,b,br,blockquote,i,li,pre,u,ul,p

Comentários da comunidade

  • Parabéns

    by Fernando Custodio,

    Seu comentário está aguardando aprovação dos moderadores. Obrigado por participar da discussão!

    Parabéns!!! Deem uma olhada no meu site.. :)

    fwctecnologia.com/orcamento-aplicativo

  • among us

    by mary coca,

    Seu comentário está aguardando aprovação dos moderadores. Obrigado por participar da discussão!

    Thank you for the motivational article. It sparked my imagination. Very beneficial to me!
    among us

  • JavaScript

    by pinto math,

    Seu comentário está aguardando aprovação dos moderadores. Obrigado por participar da discussão!

    JavaScript motors were initially utilized exclusively in internet browsers, yet are presently center parts of certain servers and an assortment of uses. The most well known runtime framework for this use is Node.js. In spite of the fact that Java and JavaScript are comparable in name, sentence structure, and particular standard libraries, the two dialects are unmistakable and vary enormously in plan. CONTACT : sites.google.com/site/bestessaywritingservicere...

  • klaoie

    by mary lee,

    Seu comentário está aguardando aprovação dos moderadores. Obrigado por participar da discussão!

    This is such a great resource that you are providing and you give it away for free. I like reading blogs that recognize the significance of offering a good resource for nothing. nytimes wordle

  • good

    by Michael Gere,

    Seu comentário está aguardando aprovação dos moderadores. Obrigado por participar da discussão!

    I am truly inspired that there is such a great amount of data about this subject have been revealed and you've put forth a valiant Brain test effort with so much class.

HTML é permitido: a,b,br,blockquote,i,li,pre,u,ul,p

HTML é permitido: a,b,br,blockquote,i,li,pre,u,ul,p

BT