Wasm é o novo CGI
(roborooter.com)- O Wasm pode ser visto não como o protocolo CGI, mas como o próximo modelo de aplicação a mudar a unidade de execução e a forma de implantação dos apps web, assim como o
cgi-binfez no passado - CGI, FastCGI, Rack/WSGI e serverless vêm todos redesenhando o processamento de requisições e a administração de servidores, com o objetivo comum de tornar aplicações de alto desempenho mais fáceis de criar e manter
- Módulos Wasm rodam em um runtime hospedeiro e, por meio de memória isolada e snapshots, podem manter um estado de execução limpo ao mesmo tempo em que reduzem o custo de inicialização
- A ausência de threads nativas, a impossibilidade de JIT, o custo de cópia de dados e a maturidade ainda limitada de Interface Types e module linking são as principais restrições do modelo de execução de servidores baseado em Wasm
- Quando funções serverless e edge functions se combinam com ambientes de execução Wasm, é possível reduzir cold starts e o custo de isolamento, além de compor módulos de várias linguagens de forma mais leve
Do CGI ao serverless: a evolução do modelo de aplicações web
- O ponto central do CGI não estava tanto no protocolo Common Gateway Interface em si, mas no modelo de aplicação de chamar por URL scripts ou executáveis colocados na pasta
cgi-bin - Esse modelo foi um dos primeiros formatos de aplicação web a transformar a web de um arquivo de documentos em uma rede de aplicações interativas
- Como a estrutura criava um novo processo para cada requisição, hoje isso se torna um grande gargalo por causa do custo de iniciar processos e interpretar scripts
FastCGI e a mudança dos servidores web por linguagem
- O FastCGI introduziu um modelo em que processos de longa duração tratam requisições CGI para reduzir os problemas de desempenho do CGI
- Em vez de iniciar um novo processo a cada requisição, o servidor web se comunica com um ou mais processos de longa execução
- Como aplicações CGI existentes eram feitas com a suposição de que o processo terminaria após a requisição, era fácil haver vazamento de recursos em um ambiente persistente
- Depois disso, servidores web baseados em linguagem se consolidaram como prática comum, e as aplicações passaram a ser construídas em torno do modelo de requisição/resposta
- Tornou-se comum implantá-las atrás de servidores web ricos em recursos e já comprovados, como Apache ou nginx, separando a aplicação de requisições lentas e dos detalhes do HTTP
- As implementações de servidor usam várias estratégias de gerenciamento de processos, como
forkpor requisição, threads do sistema operacional ou da linguagem, e modelos baseados em eventos ou reactor
- A interface Rack da comunidade Ruby influenciou também servidores de aplicação Python como Flask e a especificação WSGI
- Simplificando, a requisição é composta por método HTTP, hashmap de cabeçalhos e string de bytes de entrada ou stream
- A resposta é composta por código de status, objeto de cabeçalhos e string de bytes de resposta ou stream
Autoscaling na nuvem e os compromissos do serverless
- Em modelos baseados em servidores físicos ou virtuais, era preciso gerenciar diretamente quantos servidores estavam em execução
- Se o tráfego aumentasse, os servidores de aplicação poderiam ficar lentos
- Se o tráfego caísse, muitas máquinas ficariam ociosas
- O autoscaling da nuvem permite ajustar a quantidade de servidores de aplicação de acordo com CPU, uso de memória ou horário do dia
- Expandir para novas máquinas pode levar de 2 a 20 minutos, dependendo da aplicação e da configuração
- A expansão também depende dos recursos disponíveis na região de hospedagem em nuvem
- A computação serverless combinando Amazon Lambda com API Gateway criou um modelo em que se gerenciam funções em vez de servidores
- Garante um único processo, CPU isolada e memória isolada por requisição
- O processo pode ser reutilizado por até algumas horas, mas se não for usado é pausado ou removido
- A AWS consegue escalar para cima e para baixo em intervalos de segundos conforme o volume de requisições
- O serverless tem trade-offs claros
- Pelo custo de criar novos processos, parte das requisições sofre cold start quando há expansão da concorrência
- Como o processo pode ser pausado após a resposta, é difícil gerenciar conexões TCP persistentes entre requisições
- APIs de banco de dados baseadas em HTTP são populares nesse modelo porque suportam muitas conexões e escalam para cima e para baixo junto com as funções
- A característica de oferecer CPU e memória dedicadas por requisição pode ser vantagem ou desvantagem, dependendo da carga de trabalho
- Em algumas cargas, ela reduz a necessidade de gerenciar recursos e diminui preocupações com custo e escala
- Em outras, pode ser mais eficiente ter um único processo lidando com muitas requisições ou usar batching e cache com memória compartilhada
- O texto cita tanto um caso de migração de uma aplicação web baseada em CMS para serverless com redução de 90% no custo quanto um caso de migração de um serviço de análise de eventos para um modelo baseado em servidores, também com redução de 90% no custo
O modelo de execução do Wasm no servidor
- O Wasm foi desenvolvido inicialmente para executar código de alto desempenho no navegador e se conecta a tentativas anteriores como o asm.js
- WebAssembly é um formato binário de instruções para uma máquina virtual baseada em pilha, projetado como alvo portátil de compilação para várias linguagens de programação
- Ele permite a distribuição de aplicações cliente e servidor na web
- Foi projetado para ser codificado em um formato binário eficiente em tamanho e tempo de carregamento
- Busca execução próxima da velocidade nativa aproveitando recursos de hardware comuns entre diferentes plataformas
- Hoje já é possível compilar várias linguagens para instruções Wasm e executá-las tanto no navegador quanto no servidor
- O modelo de isolamento e segurança exigido pelo navegador também é útil para aplicações de servidor
- Ele permite isolar código não confiável de forma muito mais leve do que VMs ou contêineres Docker
- Ambientes serverless baseados em V8, como Node.js, Cloudflare Workers e Deno, aproveitam a capacidade de execução de Wasm acumulada no navegador
- Também existem ambientes nativos de Wasm como Fastly, Shopify e Suborbital
Módulos, host, memória e snapshots
- Como módulos Wasm são instruções para uma máquina virtual, eles precisam de um runtime para serem executados
- O runtime compila o Wasm genérico para a arquitetura local e fornece o ambiente de execução
- Alguns ambientes oferecem interfaces parecidas com a API POSIX de sistemas Linux
- Outros fornecem apenas funções específicas do sistema hospedeiro e executam as funções exportadas pelo módulo
- Programas WebAssembly são organizados em modules, e a VM que executa o módulo é chamada de host
- O módulo é a unidade de implantação, carregamento e compilação
- Ele reúne definições de tipos, funções, tabelas, memória e valores globais
- Pode declarar importações e exportações e oferecer inicialização na forma de segmentos de dados, segmentos de elementos e função
start
- As “memories” do Wasm são representadas como arrays contínuos de bytes, alocados pelo host no momento da instanciação
- Cada módulo convidado recebe isolamento de memória
- Essa memória funciona como a RAM da máquina virtual
- Ela pode ser fornecida vazia ou pré-preenchida com segmentos de dados
- Graças a esse modelo de isolamento, é possível pausar um módulo e salvar sua memória como segmento de dados
- O conceito é parecido com um snapshot de máquina virtual
- Também é possível iniciar várias cópias de um módulo pausado
- O Wizer instancia um módulo Wasm, executa a função de inicialização e depois reescreve o binário Wasm registrando o estado da instância
- Inicializa diretamente o estado dos valores globais
- Registra regiões de memória diferentes de zero
- Remove segmentos de dados existentes e os substitui por segmentos correspondentes às regiões de memória registradas
- Essa abordagem oferece uma execução limpa, como a de um novo processo, sem pagar o custo de inicialização de um processo novo
- É algo próximo de um CGI sem as desvantagens do CGI
- Em termos modernos, é algo próximo de um serverless sem cold start
Restrições e vantagens do Wasm
- O Wasm não tem threads em sua configuração nativa
- Operações bloqueantes precisam ser deslocadas para métodos do host
- O host pode oferecer wrappers para leitura e escrita em arquivos ou interfaces de rede, pausar o módulo ou fornecer handlers de callback
- É possível construir um modelo reactor ou bloqueante, mas até que a thread proposal seja aplicada, o desenho do ambiente de execução segue tendo grande peso
- Por razões de segurança, a geração dinâmica de código Wasm não é permitida, então compilação JIT não é possível
- O código em si não pode ser tratado como endereço no runtime
- Ambientes cuja performance depende de JIT, como V8 e CRuby, não podem rodar em uma VM Wasm ou precisam abrir mão do JIT
- Já foi proposta uma abordagem de “pre-jit”, na qual um runtime otimizado para scripts é gerado na etapa de build, mas ela não se difundiu amplamente
- Como a interface básica entre módulo Wasm e host é a memória, mover dados pode exigir cópia
- Compartilhar blocos de memória é possível, mas isso depende do modelo de memória do runtime e do que ele considera aceitável
- Na maioria dos runtimes, comunicação zero-copy entre o módulo e operações de I/O é tratada como algo difícil
- Mover dados em streaming para dentro e para fora de módulos Wasm pode ser mais lento do que fazer isso diretamente no host
- A VM Wasm oferece alto nível de controle sobre consumo de recursos
- Alguns runtimes conseguem impor limites de CPU contando instruções, como no conceito de fuel do Wasmtime
- A VM pode limitar memória e tempo de wall clock
- Interface Types e module linking podem reduzir o tamanho dos módulos e melhorar velocidade de I/O e usabilidade
- Interface Types já pode ser usado, mas ainda não é um padrão ratificado
- Module linking tem protótipos funcionais, mas ainda não há uma abordagem padrão
- No momento, o Wizer pode entrar em conflito com module linking; existem abordagens customizadas para resolver isso, mas ainda não há um vencedor claro
- Interface Types aponta para um modelo em que objetos neutros em relação à linguagem possam atravessar os limites de memória do Wasm sem custos altos de codificação e decodificação
- Hoje, o padrão mais comum é copiar JSON para dentro e para fora da memória
- Como módulos Wasm, por padrão, só acessam aquilo que lhes é fornecido, seu modelo de segurança é pequeno e claro
- Em geral, executar código Wasm não confiável é considerado relativamente seguro
- A superfície de ataque da VM é menor do que a de Docker ou de outros modelos de isolamento
- Como as instruções transformadas são executadas no hardware do host, ataques por temporização ainda são possíveis, embora existam mitigadores
- Também é possível compilar Wasm para binário nativo, mas a superfície de ataque cresce
Edge functions e execução de funções baseada em Wasm
- Se ambientes de execução e ferramentas de desenvolvimento para Wasm se tornarem amplamente usados, até linguagens de script poderão ser pressionadas a adotar runtimes Wasm e abordagens de preboot como o Wizer
- Em teoria, uma aplicação poderia ser retomada a partir de uma cópia em snapshot a cada requisição e executar mais rápido do que nos modelos atuais
- Até CLIs locais poderiam ser escritas em Ruby, distribuídas como módulos Wasm com snapshot e ligadas a um runtime Ruby em Wasm, alcançando tempos de inicialização próximos aos de utilitários em C++ e ficando restritas a operar apenas dentro do diretório do projeto
- A primeira grande mudança seria levar funções para a borda (edge), mais perto do usuário
- O processamento passaria a acontecer perto do usuário, e não perto do banco de dados
- Vercel Edge Functions é citado como exemplo baseado em V8 que compartilha vários desses princípios
- O next-auth pode controlar o acesso a páginas pré-renderizadas com base em tokens JWT de login
- Também pode fornecer conteúdo dinâmico personalizado composto com dados em cache na CDN
- Outra mudança seria substituir, em aplicações serverless, funções baseadas em processo por funções baseadas em Wasm
- A Suborbital permite criar diretamente um ambiente de execução de funções baseado em Wasm e também encadear funções Wasm em workflows
- Muitas plataformas de execução recomendam um único módulo por requisição e não exploram plenamente as possibilidades de memória compartilhada ou chamadas rápidas entre vários módulos
- Se Interface Types virar padrão, pode surgir um modelo de dados de middleware parecido com Rack
- O Wasm também pode rodar dentro de funções Lambda
- Isso permite usar versões snapshot de aplicações sobre infraestrutura existente
- A combinação é interessante, mas é tratada como uma mistura tecnológica que pode acabar desaparecendo
A direção do próximo modelo de aplicação web
- O Wasm é apresentado como uma tecnologia capaz de aumentar desempenho, facilitar segurança em nível de processo e reduzir os custos de build e execução de funções serverless
- Ele pode executar praticamente qualquer linguagem e, com module linking e Interface Types, pode reduzir bastante a latência entre funções
- A ideia central de ver o Wasm como o novo CGI está em que, quando as restrições do sistema mudam, coisas antes impossíveis passam a ser viáveis
1 comentários
Comentários no Hacker News
Não entendo bem o que diferencia WASM de tecnologias antigas como Java Applet, ActiveX, Silverlight e Macromedia Flash
Executar código compilado de terceiros não confiável no navegador parecia ser uma lição que já tínhamos aprendido, e isso parece uma estrutura de empurrar o custo computacional do servidor para o cliente em nome de melhorar a experiência do usuário
As implementações tinham muitas vulnerabilidades e, no fim, ficaram praticamente inutilizáveis no navegador, enquanto outras tecnologias aparentemente nem chegaram a fazer essa promessa
O JavaScript de fato cumpriu essa promessa, e hoje os navegadores executam JavaScript baixado de qualquer domínio sem perguntar ao usuário se ele confia ou não
O WASM roda sobre o motor de JavaScript e oferece garantias de segurança parecidas, então não há uma diferença fundamental em relação ao bytecode da JVM; a diferença prática é que o WASM provou sua segurança e a JVM não
Hoje o Google Chrome é seguro o bastante para que bilhões de pessoas executem WASM malicioso sem que seus celulares sejam comprometidos, e esse mesmo motor pode ser levado ao servidor para executar scripts de vários usuários dentro de um sandbox forte, compartilhando recursos
A alternativa é a virtualização: você pode compilar o código em um bloco de WASM e executá-lo em um grande servidor WASM, ou empacotar um binário amd64 com um kernel Linux reduzido e executá-lo em uma VM
No momento, não dá para dizer claramente que um lado venceu; cada abordagem tem suas vantagens e desvantagens
Diferentemente da JVM, o WASM oferece memória linear e não tem coleta de lixo por padrão, o que o torna um alvo de compilação melhor para um conjunto mais amplo de linguagens, como C/C++ compilado com Emscripten ou Rust
WASM é bytecode, e a maioria das implementações compartilha bastante do motor e do runtime JavaScript do host
A mudança do custo computacional entre cliente e servidor já faz parte, há muito tempo, do movimento da indústria entre clientes pesados e clientes leves, e esse pêndulo vai continuar oscilando
Existe uma especificação de validação que o bytecode Wasm precisa seguir, e esse subconjunto validado torna inerentemente impossíveis muitas das vulnerabilidades de segurança vistas nas tecnologias do passado
Ataques que dependem de falhas de hardware, como Heartbleed ou Rowhammer, ainda podem ser possíveis, mas por exemplo não dá para enganar a VM para interpretar um número como se fosse um ponteiro e referenciar algo fora da própria memória Wasm
O bytecode Wasm é bem simples de converter para código de máquina, então a implementação pode ser menor e mais rápida do que usar uma VM
Há uma especificação pública bem escrita, que não pertence a uma empresa específica e pode ser usada por qualquer um; além disso, foi adotado como padrão web, então não precisa de extensões de navegador
Computação no cliente já acontece com JavaScript, e o código Wasm pode ser eficiente de maneiras que o JavaScript não consegue, então isso vale ainda mais para ele
Applet tinha alguma mediação, ActiveX quase nenhuma, enquanto a “plataforma externa” do WASM é aproximadamente o runtime JavaScript, ao passo que a plataforma externa do Applet é algo mais próximo de execve(2)
O WASM pode servir para isso, mas nem sempre é esse o caso, e outras respostas já cobriram bem diferenças como sandboxing e isolamento melhores
Dizer que “a Amazon inaugurou a era da computação serverless com o Lambda” ignora que o Google App Engine saiu em 2008, 6 anos antes do Lambda
Produtos assim já existiam havia alguns anos e já tinham nome, então não entendo por que passaram a usar o termo “serverless” nem de onde ele veio
O App Engine também tinha workers em lote e workers web, e o Heroku também
Ambos eram produtos anteriores ao Docker, então talvez por isso as pessoas os vejam de forma diferente, mas acho que o Lambda também não foi lançado com Docker desde o início
A afirmação de que “por motivos de segurança, a geração dinâmica de código Wasm não é permitida, então a compilação JIT é impossível” não parece correta.
Para permitir coisas como hot reload limpo de código, isso parece quase uma funcionalidade essencial.
Considero essa lógica de segurança fraca.
O JS consegue fazer hot reload em tempo de execução e até geração de código mais agressiva sem quebrar a segurança, e também daria para imitar geração de código ou hot reload recarregando dinamicamente todo o runtime de Wasm e preservando a memória, embora a experiência do usuário ficasse tosca.
Não vejo por que isso teria de ser tecnicamente impossível e, se for uma medida de segurança, parece fácil demais de contornar.
O bytecode de WASM é conceitualmente muito parecido com coisas pensadas para compilação JIT, como .NET IL ou bytecode Java.
Não gosto muito de WASM porque parece um projeto sem direção forte nem vontade de fazê-lo dar certo no momento certo.
O nome sugere “assembly para a web”, isto é, linguagem de máquina para uma CPU virtual, mas na prática é uma representação intermediária para backends de compilador, e ainda há planos para funcionalidades de alto nível como suporte a garbage collection, então o conceito fica nebuloso.
Ainda faltam funcionalidades básicas como o hot reload mencionado antes, threading que não pareça gambiarra, integração direta com o DOM sem JavaScript, APIs gráficas/de cálculo com baixo overhead e acesso de baixo nível a áudio.
É difícil rodar grandes apps multimídia sem grandes concessões.
O Wasm não pode marcar memória como executável e, na prática, tem código e memória separados como numa arquitetura Harvard.
Além disso, também não é possível saltar para posições arbitrárias do código, e nem existem instruções de salto desse tipo.
Aqui, JIT significa compilar e executar código nativo durante a execução, o que vira uma grande vulnerabilidade de segurança no navegador ou no sandbox de Wasm.
Isso está embutido no design e no conjunto de instruções, então não é algo facilmente contornável; mais detalhes podem ser vistos em https://webassembly.org/docs/security/.
O Wasm é parecido com .NET IL ou bytecode Java no sentido de que o motor cuida do JIT, mas dar ao usuário permissão para gerar código nativo fora do runtime e saltar para lá seria perigoso.
Por isso, assim como no JIT, pode haver uma pequena “engasgada de aquecimento” na primeira execução de código WASM, embora isso tenha melhorado bastante nos últimos anos.
Além disso, pelo que sei, é possível criar dinamicamente blocos de WASM no navegador, instanciá-los e executá-los.
Não sei se isso também é possível em outros runtimes de WASM e, no navegador, ainda é preciso passar por JavaScript, mas isso de qualquer forma já é necessário para acessar qualquer “API web”.
WASM transforma uma VM dedicada a uma linguagem específica, como a VM de JavaScript, em uma VM de uso geral que pode ser usada em qualquer lugar onde uma VM de JavaScript seja usada.
Claro, não fica limitado só a isso.
“Uso geral” quer dizer que, com um compilador ou interpretador, dá para rodar quase qualquer coisa, inclusive JavaScript.
Como em geral é implementado como parte do motor de JavaScript, ele herda bastante das características dele, como sandboxing e acesso às APIs correspondentes.
A padronização desse acesso ainda está em andamento, mas no fim das contas o WASM deve conseguir fazer o que hoje só é possível com JavaScript, além de mais coisas que são difíceis ou impossíveis em JavaScript.
Também pode executar de forma mais rápida e mais suave.
O ponto central do WASM é remover muitas das limitações que existiam nos ambientes em que JavaScript era popular.
JavaScript é uma linguagem que divide opiniões, então ele deixa de ser a única opção para virar uma entre várias.
WASM já foi descrito como substituto de JavaScript, de Docker, de Java e de CGI; em resumo, é tudo isso e ainda mais.
Há ferramentas demais fazendo coisas parecidas, e as fronteiras entre elas são pouco claras.
No fim, mesmo quando você faz tudo funcionar, pessoalmente aquilo me parece frágil, e para quem não trabalha com isso profissionalmente é algo intimidador.
Hoje em dia, quando preciso fazer algo para a web, uso leptos; a experiência de desenvolvimento é muito melhor e, mesmo ainda não estando nem na versão 1.x, parece mais estável do que colar cinco ferramentas só para transpilar, ofuscar, minificar e empacotar um bundle de JS.
Este texto me fez lembrar de uma lei de software que eu ouvia bastante antigamente:
toda aplicação suficientemente grande e duradoura acaba reimplementando toda a stack de software em que roda, incluindo o sistema operacional, e a reimplementa de forma ruim.
Não sei bem a origem, mas costuma se aplicar com frequência.
“Todo programa em C ou Fortran suficientemente complexo contém uma implementação ad hoc, informalmente especificada, cheia de bugs e lenta de metade de Common Lisp.”
O padrão mais geral é chamado de Inner-Platform Effect.
Ampliando a premissa do título, para que o WASM seja o verdadeiro sucessor dessa linhagem, ele teria de ser tão fácil quanto subir e implantar uma aplicação PHP em qualquer hospedagem com stack LAMP.
Ainda não me parece tão fácil quanto a experiência de deploy de PHP.
Não sei o que você espera que mude na hospedagem.
Vejo isso de outra forma
Acho que o futuro é local-first
Um modelo em que o app roda quase todo dentro do navegador do usuário, sem quase depender do servidor
Apps como Figma, Linear e Superhuman usam esse modelo com muito sucesso, e o Stackblitz também até certo ponto
Se um app relativamente complexo como o Figma consegue rodar quase inteiramente dentro do navegador do usuário, então acho que a maioria dos apps também consegue
No lado do servidor, o papel principal é sincronizar dados entre diferentes instâncias quando o usuário usa o app em vários lugares
Ferramentas como Electric-SQL estão sendo criadas, mas ainda não amadureceram; quando bibliotecas desse tipo amadurecerem, essa área deve crescer bastante
Serverless em geral existe para empresas como Amazon e Azure ganharem dinheiro, e no fim vai virar algo como CGI
O WASM também pode dar certo, mas provavelmente principalmente dentro do navegador do usuário
A Microsoft usa WASM com C#/Blazor, mas não acho que o dotnet no navegador vá ficar tão rápido quanto JavaScript, então não me parece a abordagem certa
Só não se fala disso porque não escala para 1 trilhão de impressões de anúncio por segundo
Cada função serverless que alguém escreve adiciona mais 10 pés ao iate do Bezos
Se você estiver offline ou com um wi-fi instável, não consegue carregar os designs e, a menos que isso tenha mudado recentemente, se editar e depois fechar o navegador com o wi-fi desconectado, nada será salvo
E também não é uma ideia nem um pouco nova
Estamos desenvolvendo com Blazor WASM, e em termos de desempenho o dotnet não é um problema
Basicamente, isso é recriar a JVM e seu ecossistema?
Às vezes, recriar X refletindo as lições do passado é de fato uma ótima ideia
Por exemplo, quando começamos a investigar como rodar código C/C++ no navegador por volta de 2010, compilar C/C++ para a JVM era praticamente impossível
Na época, Java Applet ainda tinha relevância, então teria sido bom se isso fosse possível, mas o WASM ainda não existia; o Emscripten existia, e foi isso que passou por asm.js e acabou levando ao surgimento do WASM
Como alguém que mexeu com class loaders e escreveu assembly da JVM à mão em aulas de linguagens de programação nos anos 2000, nem sei se a JVM representa o auge dessa área
Ela de fato possibilitou um grande ecossistema, mas a interface da JVM com o mundo externo é realmente um desastre grosseiro
Há mais de 20 anos eu suspiro de frustração sempre que encontro alguma coisa relacionada à JVM
Comparar o empacotamento e o ecossistema de Rust com Python ou, pior ainda, C++ mostra que reinventar algo incorporando as lições das últimas décadas pode ser algo muito positivo
Ainda é muito difícil juntar dois arquivos wasm em um só e alinhar corretamente a fusão de memória
O modelo de componentes talvez consiga resolver isso, mas há tanta coisa desnecessariamente inchada ali que a adoção pelo Safari pode demorar bastante
Há muito tempo eu acho que vamos para um mundo em que o WASM substituirá o código de funções Lambda na nuvem
Tradicionalmente se considera que o WASM roda sobre a plataforma hospedeira, mas não há motivo para que isso precise ser assim
Por causa da natureza de sandbox do WASM, tecnicamente ele poderia rodar fora do sistema operacional ou em ring0 e contornar grande parte da sobrecarga do sistema operacional
Compilar para WASM simplifica bastante vários problemas de distribuição para o usuário e dá ao ambiente de hospedagem muito espaço para otimização
Pode até ser possível criar hardware sob medida para rodar WASM mais rápido
Executar automaticamente código arbitrário e não confiável vindo de fontes arbitrárias e não confiáveis é ruim, e isso nem pode ser considerado como preenchendo o mesmo papel de um CGI no servidor