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).[2] 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.[3]
O asm.js foi substituído por WebAssembly. Consulte § Deprecação abaixo.
Design
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.[4] Ao usar um subconjunto de JavaScript, o asm.js é amplamente compatível com todos os principais navegadores da web,[5] ao contrário de abordagens alternativas como o Google Native Client.
Geração de código
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[curr>>0]|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.
Desempenho
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.[6]
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."[7] 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."[7]
Implementações
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.[2]
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:
- Mozilla Firefox foi o primeiro navegador da web a implementar otimizações específicas do asm.js, começando com o Firefox 22.[3] OdinMonkey, o compilador asm.js da Mozilla usado no Firefox, é um componente do IonMonkey, o compilador JIT do SpiderMonkey.
- A Microsoft está implementando suporte para asm.js em Chakra, o mecanismo JavaScript usado pelo Microsoft Edge, realizando validação para produzir código JIT altamente otimizado.[8]
- As otimizações do motor JavaScript V8 do Google Chrome no Chrome 28 fizeram benchmarks do asm.js mais de duas vezes mais rápido do que as versões anteriores do Chrome,[9] embora o V8 do Chrome não use compilação antecipada.
Adoção
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.[10] Alguns deles são listados abaixo.
Linguagens de programação
Frameworks de aplicativos
- pepper.js: Portes de diversos apps PNaCl (earth, voronoi, bullet, etc.)[15]
- Qt: portes de vários demos da Qt, mais os aplicativos do KDE, como o Kate[16]
Programas e bibliotecas
Motores de jogo
Jogos
Emuladores
- EM-DOSBox: um porte do DOSBox para o Emscripten[36]
- Start9.io: uma plataforma de emulação da web visando múltiplas arquiteturas de jogos
- JSMESS: um porte do emulador MESS para muitos consoles de jogos e sistemas de computadores[37]
Matemática
Deprecação
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.[39] Os esforços para estender o JavaScript com mais recursos de baixo nível, como SIMD.js, também foram suspensos desde 2017.[40]
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.[41] O emissor WebAssembly de referência binaryen costumava conter um módulo asm2wasm
, mas foi removido depois que o Emscripten parou de usá-lo.[42]
Ver também
Referências
Ligações externas