stamatios
← Voltar ao feed
Motor compacto com ponteiro no máximo ao lado de chave inglesa, interpretador Wasmi 2.0 muito mais rápido em Rust
Dev & Engenharia

Wasmi 2.0 é um dos interpretadores WebAssembly mais rápidos

resumo de ~3 min

Lançamento e resultados

Robin Freyler anunciou o Wasmi 2.0, nova versão do interpretador de WebAssembly desenvolvido pela Wasmi Labs, após oito meses de trabalho focado em desempenho de execução. Na média geométrica do conjunto wasmi-benchmarks em um Apple M2 Pro, a versão 2.0 roda cerca de 2,2x mais rápido que o Wasmi 1.0. O release também traz medição de fuel estável, suporte ao perfil determinístico do Wasm, uma CLI melhorada e o recurso de crate validate, que reduz o tamanho do binário.

Em comparações com os interpretadores portáteis mais rápidos - Wasm3, WAMR fast-interpreter, Wasmtime Pulley, Makepad Stitch e o próprio Wasmi 1.0 - em três plataformas (Apple M2 Pro, AMD EPYC 7763 e Intel Xeon Platinum 8370C), o autor conclui que o Wasmi 2.0 pertence claramente ao grupo dos interpretadores Wasm portáteis mais rápidos, mantendo também desempenho de inicialização praticamente igual ao da versão anterior.

Modos de despacho de instruções

O Wasmi 2.0 oferece quatro modos de despacho de instruções: Direct-Threaded Code (o mais rápido, usado por Wasm3 e Stitch, com ponteiros de função embutidos no IR interno e tail calls), Indirect-Threaded Code (10-15% mais lento, mas com uso de memória bem menor), Switch-Loop (o método do Wasmi 1.0, para plataformas sem suporte a tail calls) e Call-Loop (lento e ineficiente, existe apenas como consequência da matriz de recursos). A feature auto-dispatch seleciona automaticamente a configuração baseada em threaded-code quando possível.

Registradores acumuladores e cópias

O Wasmi 1.0 usava deslocamentos de pilha para operandos e resultados. O 2.0 introduz três registradores acumuladores (ireg, freg32, freg64) que guardam valores intermediários em registradores de hardware, eliminando decodificação e acesso à pilha. O custo desse desenho é a necessidade de instruções de cópia adicionais, mitigadas por instruções otimizadas para padrões comuns e por fusão de opcodes (variantes de operações que gravam o resultado também em um slot da pilha). Os acumuladores também atravessam fronteiras de controle de fluxo quando possível, sendo usados apenas para a cauda dos resultados de blocos, if e parâmetros de loop; uma tentativa de aplicar as mesmas regras a chamadas causou regressões e não foi incorporada.

Acesso a objetos de instância

O Wasmi 1.0 modelava objetos de instância com uma alocação de heap por tipo e resolução via handles no store, o que tornava instruções como global.get custosas. No 2.0, a InstanceEntity virou um tipo de tamanho dinâmico com um buffer contíguo de handles e ponteiros de entidade em cache, inicializados durante a instanciação. O Wasmi usa bytecode por módulo - todas as instâncias do mesmo módulo compartilham o mesmo IR, melhorando o consumo de memória - enquanto Wasm3 e Stitch usam bytecode por instância. Ainda assim, o redesenho permitiu alcançar o nível de desempenho deles no acesso a objetos de instância, dispensando o cache especial de (global 0) do Wasmi 1.0.

CodeMap sem travas e chamadas

O antigo CodeMap, protegido por mutex e realocado a cada crescimento, foi substituído por buckets append-only que nunca realocam. A alocação é serial, mas o acesso às funções já compiladas é concorrente e sem travas, com um estado atômico por FuncEntry que permite compilação tardia. Com ponteiros embutidos no bytecode, o call_internal não faz buscas. O Wasmi 2.0 empata ou supera Stitch e Wasm3 no benchmark de chamadas recursivas de Fibonacci, em parte porque usa pilhas de chamada e de valores separadas, evitando a sobrecarga de cópia dos concorrentes (Wasm3 ainda usa um pool de constantes por função, gerando cópias extras).

Células de 64 bits e o problema no Rust

As células da pilha passaram a ter largura fixa de 64 bits; valores simd usam duas células adjacentes. Assim, habilitar a feature simd não aumenta mais o consumo de memória nem causa a regressão de 5-10% (cerca de 8% no CoreMark do Wasmi 1.0).

A descoberta mais impactante veio de uma investigação: a otimização DestinationPropagation, habilitada por padrão no Rust 1.92, colapsava os dois caminhos de despacho de desvios condicionais em um único site de branch via csel, confundindo o preditor do processador. Stitch havia perdido cerca de 30% no CoreMark por causa disso; ao aplicar a correção, voltou a mais de 3000 pontos. No Wasmi 2.0, a mesma correção elevou o CoreMark de ~2800 para mais de 4200 - cerca de 50% de ganho, o que a tornou a "otimização" isolada mais importante da versão.

Próximos passos e sustentação do projeto

A meta para o Wasmi 3.0 é suportar todo o WebAssembly 3.0, o que exige implementar as propostas function-references, exception-handling e gc. O projeto foi patrocinado desde outubro de 2024 pela Stellar Development Foundation, financiamento que permitiu ao autor dedicar-se ao projeto em tempo integral por dois anos e que termina em outubro de 2026; ele busca nova patrocínio ou um cargo compatível para continuar o desenvolvimento.