Este artigo resulta, no todo ou em parte, de uma tradução do artigo «asm.js» na Wikipédia em inglês, na versão original. |
asm.js | |
---|---|
Surgido em | 21 de março de 2013 |
Criado por | Mozilla |
Influenciada por | JavaScript |
Influenciou | WebAssembly |
Sistema operacional | Multiplataforma |
Página oficial | asmjs |
asm.js é um subconjunto de JavaScript projetado para permitir que software de computador escrito em linguagens como C sejam executados como aplicativos da web, enquanto mantém características de desempenho consideravelmente melhores do que o JavaScript padrão, que é a linguagem típica usada para tais aplicativos.
o asm.js consiste em um subconjunto estrito de JavaScript, para o qual o código escrito em linguagens estaticamente tipadas com gerenciamento de memória manual (como C) é traduzido por um compilador de fonte para fonte como o Emscripten (baseado em LLVM). O desempenho é melhorado ao limitar os recursos da linguagem àqueles que podem ser otimizados com antecedência e outras melhorias de desempenho.
O Mozilla Firefox foi o primeiro navegador da web a implementar otimizações específicas para o asm.js, a partir da versão 22.
O asm.js foi substituído por WebAssembly. Consulte § Deprecação abaixo.
O asm.js permite melhorias significativas de desempenho para aplicativos da web, mas não tem como objetivo melhorar o desempenho do código JavaScript escrito à mão, nem permite nada além do desempenho aprimorado.
Destina-se a ter características de desempenho mais próximas das do código nativo do que o JavaScript padrão, limitando os recursos da linguagem àqueles passíveis de otimização antecipada e outras melhorias de desempenho. Ao usar um subconjunto de JavaScript, o asm.js é amplamente compatível com todos os principais navegadores da web, ao contrário de abordagens alternativas como o Google Native Client.
O asm.js normalmente não é escrito diretamente: em vez disso, como uma linguagem intermediária, ele é gerado por meio do uso de um compilador que pega o código-fonte em uma linguagem como C++ e gera asm.js.
Por exemplo, dado o seguinte código em C:
int f(int i) {
return i + 1;
}
Emscripten produziria o seguinte código em JS:
function f(i) {
i = i|0;
return (i + 1)|0;
}
Observe a adição de |0
e a falta de especificadores de tipo. Em JavaScript, os operadores bit a bit convertem seus operandos em inteiros assinados de 32 bits e fornecem resultados inteiros. Isso significa que um OR bit a bit com zero converte um valor em um inteiro (uma apresentação "conceitual" muito simples de operadores bit a bit pode não lidar com a conversão de tipo, mas toda linguagem de programação define operadores para sua própria conveniência, como o Javascript faz aqui) . Ao fazer isso para cada parâmetro, isso garante que, se a função for chamada de um código externo, o valor será convertido para o tipo correto. Isso também é usado no valor de retorno, neste caso, para garantir que o resultado da adição de 1 a i seja um número inteiro (caso contrário, ele poderia se tornar muito grande) e para marcar o tipo de retorno da função. Essas conversões são exigidas pelo asm.js, para que um compilador otimizador possa produzir código nativo ahead-of-time altamente eficiente. Nesse compilador otimizador, nenhuma conversão é executada quando o código asm.js chama outro código asm.js, pois os especificadores de tipo necessários significam que é garantido que os valores já terão o tipo correto. Além disso, em vez de realizar uma adição de ponto flutuante e converter para um número inteiro, ele pode simplesmente fazer uma operação de número inteiro nativo. Juntos, isso leva a benefícios em desempenho significativos.
Aqui está outro exemplo para calcular o comprimento de uma string:
size_t strlen(char *ptr) {
char *curr = ptr;
while (*curr != 0) {
curr++;
}
return (curr - ptr);
}
Isso resultaria no seguinte código em asm.js:
function strlen(ptr) {
ptr = ptr|0;
var curr = 0;
curr = ptr;
while ((MEM8|0) != 0) {
curr = (curr + 1)|0;
}
return (curr - ptr)|0;
}
No código gerado, a variável MEM8 é na verdade uma "visualização" byte a byte de um buffer tipado, que serve como o "heap" do código em asm.js.
Como o asm.js é executado em um navegador, o desempenho depende muito do navegador e do hardware. Os benchmarks preliminares de programas C compilados para asm.js estão geralmente em um fator de lentidão de 2 em relação à compilação nativa com Clang.
Muito desse ganho de desempenho em relação ao JavaScript normal é devido a 100% de consistência de tipo e virtualmente nenhuma coleta de lixo (a memória é gerenciada manualmente em uma grande matriz tipada). Este modelo mais simples, sem comportamento dinâmico, sem alocação ou desalocação de memória, apenas um conjunto estreito de inteiros bem definidos e operações de ponto flutuante permite um desempenho muito maior e potencial de otimização.
O benchmark da Mozilla de dezembro de 2013 mostrou melhorias significativas: "Firefox com otimizações float32 pode rodar todos esses benchmarks em cerca de 1,5× mais lento do que o nativo, ou melhor." A Mozilla aponta que o desempenho do código compilado nativamente não é uma medida única, mas sim um intervalo, com diferentes compiladores nativos (neste caso Clang e GCC) entregando código de desempenho diferente. "Na verdade, em alguns benchmarks, como Box2D, FASTA e cópia, asm.js é tão próximo ou mais próximo do Clang do que o Clang está do GCC. Em um caso, o asm.js até supera o Clang por pouco no Box2D."
O projeto Emscripten fornece ferramentas que podem ser usadas para compilar bases de código C e C ++ (ou qualquer outra linguagem que possa ser convertida para LLVM IR) em asm.js.
Todos os navegadores com suporte para ECMAScript 6 devem ser capazes de executar o código asm.js, pois é um subconjunto dessa especificação. No entanto, como os recursos foram adicionados nessa edição para habilitar o suporte completo para asm.js (Math.fround()
), navegadores mais antigos sem esses recursos podem encontrar problemas.
Algumas implementações de navegador são especialmente otimizadas para asm.js:
Quase todos os aplicativos atuais baseados em asm.js são aplicativos C/C++ compilados em asm.js usando Emscripten ou Mandreel. Com isso em mente, o tipo de aplicativo que terá como alvo o asm.js em um futuro próximo são aqueles que se beneficiarão da portabilidade de execução em um navegador, mas que têm um nível de complexidade para o qual um porte direto para JavaScript seria inviável.
Até agora, uma série de linguagens de programação, frameworks de aplicativos, programas, bibliotecas, jogos, motores de jogos e outros softwares já foram portados. Alguns deles são listados abaixo.
O asm.js se tornou obsoleto principalmente com a introdução do WebAssembly (wasm), que tem um formato de bytecode que é mais rápido de analisar. Os esforços para estender o JavaScript com mais recursos de baixo nível, como SIMD.js, também foram suspensos desde 2017.
o asm.js permanece útil principalmente como um "substituto" para o wasm, por meio de um programa escrito pela organização WebAssembly que converte o wasm em asm.js. Não há um conversor dedicado de asm.js para wasm, mas os compiladores TypeScript-para-wasm podem ser usados parcialmente. O emissor WebAssembly de referência binaryen costumava conter um módulo asm2wasm
, mas foi removido depois que o Emscripten parou de usá-lo.
AssemblyScript which compiles TypeScript to Binaryen IR; wasm2js which compiles WebAssembly to JS
v97: Remove asm2wasm, which supported Emscripten's fastcomp backend, after fastcomp was removed.(Ver também PR#3042.)