- Ao portar linguagens com GC como Java, Kotlin, Dart, Python e C# para WebAssembly, o WasmGC usa as próprias estruturas de GC, arrays e sistema de tipos do Wasm, em vez de recompilar uma VM existente
- O port tradicional para WasmMVP facilita o reaproveitamento da VM e das otimizações existentes, mas traz o custo de embutir GC ou
malloc/freedentro da memória linear, além de limitações com referências na pilha, referências cíclicas e fragmentação - Com WasmGC, a VM gerencia objetos e memória diretamente, o que pode reduzir o tamanho do binário; no benchmark
fannkuch, ele ficou em 2.3K, menor que os 6.1~9.6K de C/Rust - Como o WasmGC é uma representação intermediária de nível mais alto, as otimizações do Binaryen
wasm-opte do runtime do V8 são importantes; há casos com média de 1.9× em benchmarks Java e cerca de 30% de melhora no Calc Engine do Google Sheets - A padronização e o suporte nos navegadores avançaram, mas como não se trata de compilar uma VM existente sem mudanças, ainda é necessário um novo trabalho de toolchain para baixar as estruturas da linguagem aos elementos primitivos do WasmGC
Duas formas de portar linguagens com GC para Wasm
- O port de linguagens com GC para WebAssembly se divide, em linhas gerais, em dois caminhos
- Port tradicional: compilar a VM existente da linguagem para o WasmMVP, o WebAssembly Minimum Viable Product lançado em 2017
- Port com WasmGC: compilar a linguagem para a estrutura de GC do Wasm definida na proposta GC
- O port tradicional é mais próximo do modelo usado ao levar uma linguagem para uma nova arquitetura de CPU: compartilha o parser da VM, suporte a bibliotecas, GC e otimizador, adicionando apenas um novo backend
- O port com WasmGC se parece mais com levar a linguagem não para uma nova arquitetura, mas para uma nova VM
- Assim como o J2CL compila Java para JavaScript, os objetos da linguagem são representados como objetos da VM de destino e gerenciados pelo GC dessa VM
- O WasmGC busca um nível mais baixo que JavaScript VM, JVM e CLR, mas ainda assim é mais alto que o WasmMVP por fornecer structs, arrays e relações de tipo gerenciados pela VM
Vantagens e limites do port tradicional para WasmMVP
- A maior vantagem do port tradicional é poder reaproveitar quase integralmente o código da VM, a implementação da linguagem e as otimizações existentes
- O resultado em WasmMVP usa as estruturas básicas do WasmMVP, como memória linear, tabelas e funções
- Em muitos casos, o Wasm roda dentro de VMs que já possuem GC, como navegadores, Node.js, workerd, Deno, e Bun
- Nesses ambientes, incluir uma implementação de GC dentro do binário Wasm causa aumento de tamanho desnecessário
- Mesmo linguagens como C, C++ e Rust, que usam memória linear, precisam embutir código de
malloc/freequando fazem alocações relevantes - O
dlmallococupa 6K, e mesmo oemmalloc, priorizando tamanho, passa de 1K
- Com WasmGC, a VM gerencia a memória automaticamente, então não é necessário embutir GC nem
malloc/freeno Wasm- No benchmark
fannkuchmostrado em um post anterior sobre WasmGC, ele ficou em 2.3K, enquanto C/Rust ficaram em 6.1~9.6K
- No benchmark
Coleta cíclica, referências na pilha e fragmentação
- No navegador, o Wasm interage com frequência com JavaScript e Web APIs, mas apenas com WasmMVP e reference types é difícil fazer coleta cíclica precisa em links bidirecionais entre Wasm e JS
- Links para objetos JS só podem ficar em tabelas Wasm
- Links de volta de JS para Wasm podem referenciar toda a instância Wasm como se fosse um único objeto grande
- O WasmGC define objetos Wasm reconhecidos pela VM, permitindo criar referências adequadas entre Wasm e JavaScript
- Linguagens com GC também precisam reconhecer referências na pilha, como variáveis locais no escopo de chamada
- Em ports tradicionais, o sandboxing do Wasm impede que o programa inspecione sua própria pilha
- Como alternativa, é possível usar shadow stack ou executar o GC apenas quando não houver referências na pilha, como entre turnos do event loop do JavaScript
- No futuro, o suporte a stack scanning no Wasm pode ajudar os ports tradicionais
- Hoje, a forma de lidar automaticamente com referências na pilha sem overhead é o WasmGC
- O
malloc/freeda memória linear do WasmMVP pode causar fragmentação de memória em programas de longa execução- Mesmo com memória livre total suficiente, uma alocação grande pode falhar se não houver um bloco contínuo grande
- A fragmentação pode fazer o módulo Wasm aumentar a memória com mais frequência, levando a overhead e erros de out-of-memory
- O WasmGC evita isso porque a VM pode mover objetos para compactar o heap de GC
Ferramentas de desenvolvimento e semântica da linguagem
- No port tradicional para WasmMVP, como os objetos ficam como bytes em memória linear, as ferramentas de desenvolvimento têm dificuldade para enxergar informações de tipo em alto nível
- No WasmGC, como a VM gerencia os objetos de GC, a integração com ferramentas fica mais fácil
- É possível ver heap snapshots de programas WasmGC na aba Memory do Chrome DevTools
- Em um exemplo de lista encadeada, aparecem o nome de tipo
$Nodee o campo$nextque referencia o próximo objeto - Também são fornecidas informações comuns de heap snapshot, como quantidade de objetos, shallow size e retained size
- O debugger do Chrome DevTools também funciona com objetos WasmGC
- O port tradicional recompila a VM existente, então costuma preservar mais facilmente a semântica esperada da linguagem
- No port com WasmGC, é preciso representar a estrutura da linguagem com novos tipos GC, como structs e arrays, então podem ser necessários compromissos de semântica por eficiência
- Campos de struct no WasmGC têm índice e tipo fixos, o que pode dificultar linguagens que queiram acesso mais dinâmico a campos
- O WasmGC atualmente não tem interior pointer, e espera-se que esse tipo de limitação melhore com o tempo
- Outros casos de compilação para VM de destino também envolvem escolhas de semântica
- O comportamento numérico do dart2js difere do da Dart VM
- As strings do IronPython se comportam como strings de C#
- Já o dart2wasm funciona sem compromissos na semântica numérica, porque o Wasm consegue representar de forma eficiente os tipos numéricos de que o Dart precisa
O trabalho de toolchain necessário para portar para WasmGC
- Um port para WasmGC não é apenas recompilar uma VM existente
- Parte do código pode ser reaproveitada, como lógica de parser ou otimizações AOT que não se integram diretamente ao GC de runtime
- Em geral, porém, é necessário muito código novo para baixar as estruturas da linguagem para structs e arrays do WasmGC
- Uma VM escrita em C, como a do Lua, pode ser compilada para Wasm em poucos minutos, mas um port do Lua para WasmGC exige decidir e implementar como representar as estruturas do Lua dentro das restrições do sistema de tipos do WasmGC
- A grande desvantagem do port com WasmGC é o esforço de toolchain
- O cenário ideal é que o sistema de tipos do WasmGC dê suporte eficiente a todas as linguagens e que cada linguagem implemente seu port para WasmGC
- Futuras adições ao sistema de tipos do WasmGC podem ajudar na primeira parte
- Compartilhar trabalho comum de toolchain pode reduzir o peso da segunda parte
Por que o WasmGC pode ser melhor otimizado
- O WasmGC é uma representação intermediária de nível mais alto que o WasmMVP, então há mais espaço para otimização
- Em uma função de exemplo que aloca dois objetos GC, grava
10em um campo e depois retorna o valor, o código pode ser reduzido logicamente areturn 10- No WasmMVP, a alocação vira uma chamada a
malloc, emallocé uma função complexa com efeitos colaterais na memória linear, então o otimizador tem dificuldade para garantir que a segunda alocação não altere o campo do primeiro objeto - No WasmGC, a alocação é expressa com a instrução
struct.new, que pode ser inferida como operação da VM, permitindo rastrear referências e valores de campos
- No WasmMVP, a alocação vira uma chamada a
- O WasmGC também fornece ponteiros de função explícitos com
ref.func, chamadas por eles comcall_ref, além de tipos de campo de structs e arrays - O motivo de o WasmMVP conseguir desempenho próximo ao nativo é que, em geral, a maior parte das otimizações já foi feita antes da geração de Wasm, por compiladores fortes como o LLVM
- O LLVM não oferece suporte a WasmGC, e muitas linguagens com GC também não usam LLVM, então o WasmGC precisa de outro modelo de otimização
Binaryen e a otimização com wasm-opt
- O WasmGC permite executar otimizações de propósito geral depois que o código já foi baixado para Wasm, o que permite que vários toolchains compartilhem um otimizador comum de Wasm para Wasm
- A equipe do V8 investiu no suporte a WasmGC no Binaryen, projeto de otimização de toolchains WebAssembly
- Cada toolchain pode usar o Binaryen por meio da ferramenta de linha de comando
wasm-opt
- Cada toolchain pode usar o Binaryen por meio da ferramenta de linha de comando
- O Binaryen já possui otimizações como inlining, propagação de constantes e eliminação de código morto para WasmMVP, e a maioria delas também se aplica ao WasmGC
- As principais otimizações adicionadas para WasmGC incluem:
- Escape analysis: mover alocações do heap para locais
- Devirtualization: transformar chamadas indiretas em chamadas diretas
- Eliminação global de código morto mais forte
- Whole-program type-aware content flow analysis, GUFA
- Cast optimizations: remoção de casts redundantes e movimentação de casts
- Type pruning
- Type merging
- Refinamento de tipo para locais, globais, campos e assinaturas
- As novas otimizações de GC do Binaryen e a forma de usá-las estão resumidas na documentação do Binaryen
- Medindo o desempenho Java na saída do J2Wasm, o
wasm-optmelhorou a velocidade em todos os benchmarks, com média de 1.9×
Otimizações de WasmGC no V8
- Linguagens com GC têm um modelo de desempenho diferente de linguagens como C, C++ e Rust, que dependem muito de inlining em tempo de compilação
- Linguagens com GC, como Java e Dart, normalmente dependem de a VM fazer inlining e otimizações em tempo de execução
- Em Java, todas as chamadas começam inicialmente como chamadas indiretas
- Classes filhas podem sobrescrever funções da classe pai, e isso continua valendo mesmo quando a chamada é feita por uma referência do tipo pai
- Converter chamadas indiretas em diretas ajuda, mas no código Java real há muitos caminhos difíceis de inferir estaticamente como chamadas diretas
- O V8 implementou speculative inlining para WasmGC
- Ele observa chamadas indiretas em tempo de execução
- Quando um ponto de chamada mostra comportamento simples, como poucos alvos de chamada, faz inlining com verificações de guarda
- O Calc Engine do Google Sheets é uma base de código Java que calcula fórmulas de planilha e antes era compilada para JavaScript com J2CL
- A equipe do V8 colaborou com o Sheets e com o J2CL para portar esse código para WasmGC
- Nesse código, o speculative inlining foi a otimização individual implementada no V8 para WasmGC com maior impacto
- Houve ganho de cerca de 30% em velocidade, maior do que a melhora somada de todas as outras otimizações mensuráveis
- Outras otimizações de WasmGC no V8 incluem load elimination, type-based optimizations, branch elimination, constant folding, escape analysis e common subexpression elimination
- As informações de tipo do WasmGC também são usadas em otimizações de runtime
- Se um
ref.testconfirma um tipo específico, então umref.castpara esse mesmo tipo deve ser bem-sucedido - Isso permite remover casts em padrões como downcast após
instanceofem Java
- Se um
- No WasmMVP, a divisão de papéis entre otimizações do toolchain e da VM era relativamente clara, mas no WasmGC essa separação pode se sobrepor mais por causa das características das linguagens com GC e da capacidade de otimização da representação em WasmGC
Estado atual e ponto de partida
- O WasmGC chegou à phase 4 no W3C e se tornou um padrão completo e finalizado
- O Chrome 119 inclui suporte a WasmGC
- O Firefox 120 era esperado para sair no fim do mesmo mês com suporte a WasmGC
- Na demo do Flutter, Dart compilado para WasmGC executa a lógica da aplicação, incluindo widgets, layout e animações
- Os toolchains com suporte a WasmGC incluem:
- O pequeno código-fonte citado na seção sobre ferramentas de desenvolvimento é um exemplo de programa WasmGC “hello world” escrito à mão
- Nele é possível ver a definição do tipo
$Nodee a criação comstruct.new
- Nele é possível ver a definição do tipo
- A wiki do Binaryen explica como fazer o compilador emitir código WasmGC mais amigável para otimização
- GC Implementation - Lowering Tips
- Também é possível consultar os passes e flags do Binaryen usados pelos toolchains de Java, Dart e Kotlin
1 comentários
Comentários do Hacker News
WASM parece ser um exemplo de thin waist, e com um coletor de lixo adicionado a estrutura passa de N×M para N+M. Ou seja, dá para pensar em N linguagens + M máquinas virtuais + G coletores de lixo, e o V8 já tem um coletor de lixo maduro
Eu me perguntei se existia alguma ferramenta para ir de WASM para a JVM, e havia uma no GitHub. Não cheguei a usar, só fui atrás porque a JVM também tem coletores de lixo maduros e paralelos
Como eu não esperava que o WASMGC chegasse tão rápido, agora também fico na expectativa de WASM Threads para paralelismo de verdade, e não só paralelismo de entrada e saída
Se surgir a chance de resolver bem assíncrono, paralelismo e coleta de lixo, o WASM ficará ainda mais forte e talvez deixe de ser uma fonte de confusão ou dificuldade para desenvolvedores. Também vejo esse como o motivo de o WASI ser importante: a oportunidade de definir uma API tão estável quanto o POSIX
1: https://www.oilshell.org/blog/2022/02/diagrams.html
2: https://github.com/cretz/asmble
Espero que as ideias e explicações sobre como a representação intermediária de WASM foi mapeada para bytecode da JVM ajudem quem quiser criar uma implementação mais oficial. No momento, não há planos para suporte a WASM GC
“Mais de 20 empresas de ferramentas de programação oferecem cerca de 26 linguagens de programação no .NET, incluindo C++, Perl, Python, Java, COBOL, RPG e Haskell”
Fonte: https://news.microsoft.com/2001/10/22/massive-industry-and-d...
Não tanto porque a academia seja especialmente ousada com a JVM, mas porque programas na JVM foram amplamente usados e frequentemente de formas que pressionavam uma única técnica de coleta de lixo
[^1]: https://www.baeldung.com/jvm-garbage-collectors. A JVM da Azul também tem outros coletores de lixo separados. Esses são só os que eu conheço, então certamente há vários que deixei de fora
Parece ter sido dito em comparação com corrotinas, mas corrotinas são sequenciais. A ordem de execução pode ser arbitrária, mas dá para depender do fato de que não executam ao mesmo tempo
Se o wasm adotar threads, isso provavelmente será mais uma infeliz situação de “Worse is Better”. Threads estão entre os piores modelos de concorrência que já criamos. Claro, exceto talvez algo como concurrent COMEFROM
As ferramentas WASM para Julia podem permitir criar apps que suportem ou precisem disso. Por exemplo, há uma demonstração de compilação de um resolvedor de EDO para WASM em https://tshort.github.io/WebAssemblyCompiler.jl/stable/examp...
Para funcionar de imediato é necessário o Chrome v119, porque essa foi a versão que ativou pela primeira vez o suporte a coleta de lixo. Há mais detalhes na página inicial do compilador WASM https://tshort.github.io/WebAssemblyCompiler.jl/stable/
Espero que o novo tipo
Memory{T}permita compilar ainda mais códigoDá uma sensação de que “wasm é o novo LLVM”. Fico curioso se mais alguém sente isso também
Entendo o que estão tentando fazer e já vi demonstrações mostrando o poder disso, mas, na maior parte, ainda parece tudo muito de baixo nível tecnicamente, e em muitos casos acaba sendo bem trabalhoso para usar na prática
Queria saber quando vocês acham que isso vai estar pronto para uma adoção mais ampla, a ponto de ser escolhido como alvo no lugar das opções atuais
Por causa do nome, esse é um mal-entendido comum: o LLVM foi pensado inicialmente como uma máquina virtual, mas na prática nunca foi isso e ainda não é. A própria primeira frase da homepage deixa isso claro: https://llvm.org/
Basicamente, é um conjunto de bibliotecas em C++ que implementa uma representação intermediária que muda ao longo do tempo, além de ser uma ferramenta para ajudar na escrita de compiladores
Uns 10 anos atrás, havia um projeto no Google mirando uma área parecida com a do WASM, e acho que ele caiu justamente nesse mal-entendido. Tratava o LLVM como se fosse uma máquina virtual. Provavelmente era algo como PNaCl
É um pouco parecido com o fato de o LuaJIT ter fixado o Lua na versão 5.1. Lua não era um padrão, mas ainda assim você pode congelar uma versão específica para fins de reimplementação. Só que há um problema óbvio nessa abordagem: quem reimplementa não conhece necessariamente todos os bugs que também estão sendo congelados no tempo junto com ela
Já levantei a sobrancelha para alguns dos “compromissos” do WASM, mas uma coisa é indiscutível: WASM realmente é uma máquina virtual
Assisti à apresentação sobre WASM GC, e quem construiu isso falou com franqueza sobre os compromissos. Por exemplo, que no começo seriam necessários casts em tempo de execução e que a sobrecarga medida estava em um nível razoável. Esse tipo de postura, na verdade, passa confiança
https://old.reddit.com/r/ProgrammingLanguages/comments/17crk...
Sinceramente, não entendo bem wasm. Não está claro para mim qual problema exatamente estamos tentando resolver
Fico na dúvida se o objetivo é escrever aplicações em linguagens que não sejam JavaScript nem código transpilado para JavaScript. Não sei se isso inclui a parte de interagir com a camada de renderização do DOM no navegador. Pelo que entendo, na prática isso não dá para fazer, e, se for isso mesmo, então o objetivo seria executar aplicações em um canvas dedicado dentro do DOM, como o Flash fazia. Se for esse o caso, não sei quão grande é esse nicho
Em outra thread, também falaram de usar wasm no Cloudflare Workers. Imagino que isso seja porque ele funciona por meio do interpretador JavaScript. Mas, se as pessoas querem escrever Cloudflare Workers com runtimes arbitrários, então por que a Cloudflare simplesmente não adiciona suporte a outros runtimes? Parece que eles têm capacidade para isso
Ou então fico pensando se, além da ligação com o ecossistema JavaScript, existe algo em wasm que seja intrinsecamente excelente como bytecode
Sinto que perdi algum material introdutório que explicava por que essa tecnologia é interessante, mas a essa altura até dá um pouco de vergonha perguntar
O LLVM não oferece uma “sandbox de segurança fácil por padrão” sem uma quantidade considerável de código. O wasmer até tem backend LLVM, mas, para mim, esse é o maior diferencial do WASM
Vai na mesma direção do LLVM, mas ainda está longe de chegar lá
Por mais impressionante que seja e por mais possibilidades que abra, isso sempre me faz pensar no fato de que os navegadores de hoje são extremamente complexos, e a barreira de entrada para construir um diretamente é quase alta demais
Não entendo por que a plataforma de hipermídia mais bem-sucedida e acessível do mundo precisaria ser fácil de implementar. É natural que o sistema online mais bem-sucedido do mundo seja complexo e cheio de recursos. É por isso que usamos isso, e é por isso que venceu
É impressionante que isso tenha sido de fato construído e lançado. Há anos se ouve falar do plano de colocar GC no WASM, e eu não tinha certeza se isso realmente aconteceria
Fico curioso sobre o quanto isso ajudará linguagens que miram WASM e têm o problema de binários grandes por incluírem o runtime. Se bem me lembro, o Blazor precisava de algo como 1 MB até para um hello world, então não sei se o WasmGC ajudaria nisso
A preocupação, claro, é que, se forem recursos necessários só para .NET, talvez não haja muito incentivo para adicioná-los. Nesse ponto, algo como um
includeque permitisse armazenar em cache uma versão específica do GC e carregar outros assemblies WASM a partir dela, por mais doloroso que fosse, talvez fosse mais útil[0] - https://github.com/WebAssembly/gc/issues/77
C e Rust não incluem um coletor de lixo, mas ainda precisam embutir
malloc/freepara gerenciamento de memória. O Java é menor nesse caso porque não precisa embutir nenhum código de gerenciamento de memóriahttps://developer.chrome.com/blog/wasmgc/
No caso do Blazor, isso só ajudaria com o GC. Se bem me lembro, o Blazor ainda precisa enviar todo o runtime do dotnet junto
O novo suporte a wasm do Kotlin parece bem promissor. Há uma versão experimental do Compose Multiplatform que pode mirar navegadores, e ela deve usar WASM
O Compose Multiplatform é basicamente o Google Jetpack Compose para Android com suporte adicionado para outras plataformas
Até alguns dias atrás, o suporte a iOS estava em alfa e deveria entrar em beta no ano que vem. O suporte a Android e desktop agora já está estável. Quando isso tudo estiver estável, na prática será possível escrever aplicações de UI que rodem em quase qualquer plataforma
O compilador wasm deve ser lançado junto com o próximo grande release do Kotlin, o Kotlin 2.0, que inclui o novo compilador k2. Isso parece viável para o começo do próximo ano. O k2 atualmente está em beta e pode ser ativado no Kotlin 1.9.x
Uma vantagem do ecossistema Kotlin Multiplatform é que já existem muitas bibliotecas que funcionam em várias plataformas. Então o compilador wasm deve rapidamente virar parte desse ecossistema e herdar boas bibliotecas. Em geral, basta ajustar a configuração de build para mirar esse ecossistema e implementar os comportamentos específicos de plataforma que estiverem faltando
Outra coisa interessante nessa área é usar e conectar bibliotecas escritas em linguagens diferentes. Por exemplo, é bem provável que muitas partes específicas de plataforma dependam de bibliotecas C ou Rust já existentes. Em muitos casos, podem até ser as mesmas bibliotecas usadas pelo Kotlin Native
Também queria saber se ele usa os mesmos widgets em desktop, web, iOS e Android. No Android eu entendo que é nativo. Nas outras plataformas, não sei se ele imita o estilo local. A pergunta principal é qual é a sensação de um app em Compose Multiplatform fora do Android
Por exemplo, pessoalmente acho o Dart na web muito travado, e o Electron no desktop também. O Dart no desktop é aceitável, mas os apps em geral têm botões e espaçamentos grandes, então parecem mais um app Android rodando num emulador do que um aplicativo realmente nativo
Também fico curioso se Kotlin é uma linguagem recomendável para um projeto novo
Queria saber se alguém consegue explicar por que este post do blog e o anúncio do Chrome não mencionam Go
Go também usa coleta de lixo, mas fica a impressão de que não conseguiria aproveitar os benefícios dessa mudança
Fico me perguntando se faria sentido o runtime expor uma biblioteca de alocação embutida que C/C++/Rust pudesse usar
Em vez de empacotar sua própria biblioteca de alocação, o programa poderia optar por usar essa biblioteca
As primitivas de GC do Wasm foram projetadas para obter o maior ganho possível com o menor acréscimo possível. Quando outras primitivas, como SIMD, também são adicionadas, é porque elas são as “primitivas aproximadamente mínimas” necessárias para atingir aquela funcionalidade. Aqui, o que se precisa são blocos de construção tão pequenos quanto possível
Porém, uma API de alocador de memória como malloc/free não é uma funcionalidade mínima. Um alocador de memória é, na prática, um conjunto de políticas e decisões de design, e os algoritmos decorrem disso. O bloco de construção fundamental nessa área é um “pedaço de memória linear de posse do alocador”, e isso é justamente a memória linear que o WASM já tinha desde o primeiro dia
Pode fazer sentido que runtimes individuais ofereçam isso. Por exemplo, passando parte da memória linear para um import embutido do wasm e deixando um alocador implementado pelo runtime cuidar disso. Mas acho muito improvável que isso seja padronizado. Além disso, a maioria das ferramentas para linguagens Wasm sem GC já está alinhada com a forma de portar bases de código existentes e seus alocadores. No fim, isso tende a aumentar a quantidade de código a manter para uma implementação pontual, então talvez não valha o esforço investido
Se não for determinístico, diferenças no comportamento do programa podem se tornar importantes. Por exemplo, se algum navegador incluir um alocador mais “inteligente” que reutilize melhor a memória, um programa que em outros navegadores falharia por falta de memória devido à fragmentação da memória linear poderia funcionar nesse navegador. Historicamente, os navegadores têm se esforçado muito para padronizar o comportamento justamente para evitar esse tipo de diferença
Padronizar o comportamento de uma implementação de malloc/free também é complicado por vários motivos. Primeiro, há muita complexidade, como a forma de lidar com a free list, quais tamanhos de chunk usar etc. É coisa demais para colocar na especificação. Segundo, ao padronizar um comportamento específico, você impede melhorias, e ideias melhores para malloc/free continuam surgindo até hoje
Por esses motivos, sou cético quanto à possibilidade de isso entrar na especificação do wasm
Em contraste, o GC evita esses problemas. Como os valores de ponteiro não são observáveis, não há o que especificar
alloce funções relacionadas como funções do runtime para o código tradicional chamar, e então qualquer GC poderia ser construído em cima dissoSou um pouco cético em relação a essa direção. Ela está aumentando bastante a complexidade do WebAssembly
Coletores de lixo são abstrações que vazam. Alguns suportam ponteiros internos e outros não. Alguns suportam trabalho paralelo com memória compartilhada e outros não. Alguns exigem compactação, o que torna a C FFI mais difícil, e outros não. Alguns precisam ser profundamente integrados aos mecanismos usados por processos/threads verdes e pilhas escaláveis, e outros não
Se olharmos para linguagens como Erlang, JavaScript, Python e Go, dá para ver que escolhas no nível da linguagem se refletem até certo ponto no coletor de lixo
A ideia de uma máquina virtual genérica/de uso geral que suporte várias linguagens já foi tentada várias vezes com JVM, CLR, Parrot etc., e o sucesso foi limitado. Fico curioso sobre o que seria diferente desta vez
JVM/CLR primeiro criaram a máquina virtual, a especificação de instruções da linguagem intermediária e o runtime, e depois tentaram distribuí-los o mais amplamente possível, esperando adoção universal no lado do cliente. Ou seja, esperavam que a “VM de linguagem intermediária” fosse tão atraente que se espalharia por toda a indústria. Essa expectativa só se confirmou em parte. JVM/CLR se espalharam em desktops e servidores, mas falharam em navegadores web com Java Applet e Microsoft Silverlight, e também não tiveram ampla adoção em plataformas móveis
O WASM segue a ordem oposta. Ele parte de algo que já foi distribuído e adotado em toda a indústria, isto é, o JavaScript, e a partir daí constrói a “máquina virtual, a especificação de instruções da linguagem intermediária e o runtime”
Nessa perspectiva, a chamada “linguagem de brinquedo” JavaScript foi um cavalo de Troia de 20 anos para primeiro se disseminar amplamente em todos os clientes. Agora é como se a indústria estivesse dizendo: “Alguém percebeu que já existe um runtime universal, o JavaScript, em servidor+desktop+navegador+mobile? Vamos criar um runtime de linguagem intermediária e acelerá-lo”
Também havia alguns problemas técnicos. A Sun JVM não tinha ponteiros brutos, então era difícil ser um alvo de alto desempenho para linguagens baseadas em ponteiros como C/C++, e a MS CLR não podia ser usada no macOS. O CLR mínimo do Silverlight é uma exceção. Mas, mais do que essas limitações técnicas, a ordem temporal em que o JavaScript foi distribuído como um cavalo de Troia inofensivo tem maior poder explicativo
O Wasm é mais de baixo nível do que a maioria dos formatos de bytecode citados acima. Isso continua valendo mesmo com o Wasm GC, que adiciona structs e arrays com tipagem estática, e até com a proposta de function-references para importar funções tipadas. O Wasm GC também tem suporte explícito para o ponteiro etiquetado
i31refPor esses motivos, o Wasm GC é mais geral do que as tentativas citadas, justamente por ser mais de baixo nível
Criar uma VM “universal” ou “genérica” que suporte várias linguagens de forma equivalente e fluida não é um objetivo explícito do WASM-GC. A expectativa é que cada implementação possa precisar de seus próprios hacks e semânticas especiais sobre o suporte básico fornecido pelo WASM, e isso é considerado aceitável porque FFI e interoperabilidade entre linguagens são vistos como um problema completamente separado
A coleta de lixo do JavaScript vai continuar existindo e parece ser a mais persistente entre os exemplos mencionados
Em muitos casos, outras linguagens e aplicações/bibliotecas terão de se adaptar a ela, mesmo que seja inferior
o wasm fornece funcionalidade de GC para que linguagens de runtime com memória gerenciada possam ter como alvo o ambiente wasm sem sofrer por causa das limitações de um modelo de memória restrito e, secundariamente, para reduzir o tamanho dos bundles
é possível que o wasm passe a suportar parâmetros de GC mais ajustáveis para combinar melhor com as características da linguagem convidada. Além disso, ao contrário dos runtimes de linguagens de propósito geral usados como comparação, os implementadores de linguagens não têm sequer a opção de criar um runtime totalmente personalizado
Várias preocupações sobre a viabilidade atual do WASM GC são discutidas aqui. Tradução em inglês pelo Google Tradutor:
https://habr-com.translate.goog/ru/articles/757182/?_x_tr_sl...
Original:
https://habr.com/ru/articles/757182/
É um texto do autor do TeaVM, que há 10 anos trabalha para fazer código Java e JVM rodar com eficiência no navegador. https://teavm.org/
A conversão existente de Java para JavaScript no TeaVM já tem bom desempenho mesmo usando o GC de JS do navegador. Vai ser interessante ver se o WASM GC, quando estiver mais maduro, ficará ainda mais rápido que isso
Deixando algumas notas sobre os problemas abordados no texto: a questão de precisar de uma shadow stack manual é resolvida no WasmGC. Como o link diz, funciona da mesma forma que em JS
A ausência de try-catch já foi resolvida pela proposta de tratamento de exceções do Wasm, que já foi lançada nos navegadores: https://github.com/WebAssembly/exception-handling/blob/main/...
As verificações de null também são em grande parte resolvidas pelo WasmGC. A especificação define tipos locais não anuláveis, e a VM pode otimizar usando sinais, como a técnica mencionada no texto. Por exemplo, o Wizard faz isso
A inicialização de classes é, como o texto diz, um problema difícil. J2Wasm e Binaryen estão trabalhando para otimizar isso com análise estática no nível da toolchain. Um PR recente que escrevi trouxe avanço nessa direção: https://github.com/WebAssembly/binaryen/pull/6061
O overhead de vtable mencionado no texto pode ser um problema. No entanto, ainda não há boas medições. Existem algumas ideias de soluções pós-MVP para dispatch de métodos, mas ainda nada concreto
Sobre verificações de null e trap, houve discussões sobre variantes de instruções de GC que lançam exceções em vez de trap. Mas, nas medições, isso ainda não apareceu como um grande problema, então a prioridade é baixa
O autor está certo ao dizer que stack walking, sinais e controle de memória são áreas importantes
No geral, graças ao WasmGC e ao tratamento de exceções, o Java que o J2Wasm exporta hoje está em uma posição muito boa. Em geral, é mais rápido que o J2CL, que compila Java para JavaScript. Ainda assim, claramente há espaço para melhorias