2 pontos por GN⁺ 2024-04-15 | 1 comentários | Compartilhar no WhatsApp

As limitações do WebAssembly e a importância do tree-shaking

  • Apesar de muito interesse e expectativa, o WebAssembly teve apenas um sucesso limitado na web

    • Existem casos de sucesso como o Photoshop, mas no geral não há muitos projetos que usem WebAssembly
    • Em especial, o WebAssembly não é adequado para apps que usam muito o DOM
    • Uma das principais razões é a diferença entre os modelos de programação de JavaScript e WebAssembly
  • O WebAssembly não teve muito sucesso fora de linguagens como C ou Rust

    • Linguagens como C# têm a inconveniência de precisar fornecer junto runtimes como o garbage collector
    • Porém, espera-se que a situação melhore em breve, já que novos recursos do WebAssembly com suporte a reference types e garbage collection devem ser introduzidos

A capacidade de otimização de código dos compiladores é a chave para o sucesso do WebAssembly

  • Para o WebAssembly ter sucesso na web, os compiladores precisam conseguir gerar código pequeno e eficiente

    • É importante manter arquivos pequenos, na faixa de alguns kilobytes
    • Caso contrário, só resta depender de hype ou de uma base de usuários específica
  • No ecossistema JavaScript, a otimização do tamanho do código já é feita por meio de bundlers e afins

    • Tree-shaking é a técnica de incluir apenas as funções e os tipos de dados realmente usados no programa
  • Tree-shaking é um termo amplamente usado, embora seja uma metáfora inadequada tanto do ponto de vista horticultural quanto algorítmico

A situação do tree-shaking em outras linguagens

  • Em linguagens com runtimes pesados, como Go ou Python, o tree-shaking ainda não está otimizado

    • Mesmo o programa mais simples em Go, quando compilado para WebAssembly, passa de 2 MB
    • O Pyodide, de Python, também exige o download de cerca de 20 MB de arquivos
  • Em ambientes de servidor, o tamanho do binário não costuma ser um grande problema

    • Para ambientes restritos, como mobile, também são desenvolvidos toolchains mais leves separadamente, como MicroPython e TinyGo
  • Implementações de linguagens voltadas para a web inevitavelmente precisam ser diferentes das existentes

    • Isso porque interagir com o DOM é, por si só, um ambiente peculiar
    • No caso do ClojureScript, as diferenças em relação ao Clojure são documentadas separadamente

Discussão sobre algoritmos de tree-shaking

  • O compilador Hoot Scheme, em desenvolvimento pelo autor, atualmente gera cerca de 70 KB de código Wasm

    • Incluir apenas definições de funções (procedures) é relativamente fácil
    • Mas existem alguns pontos difíceis, como os abaixo
  • No modelo de avaliação de letrec*, os bindings são recursivos e ao mesmo tempo ordenados, o que dificulta a análise pelo compilador

    • No caso de record types, callbacks de vtable acabam mantendo muito código vivo
  • Ao usar funções altamente polimórficas, como display, muito código relacionado acaba sendo incluído

    • É melhor usar funções mais concretas, como write-string
  • Para um tree-shaking ideal, é necessária flow analysis

    • Se soubermos que um argumento bitvector não é passado para display, podemos remover o código relacionado
  • Em Python, isso é ainda mais difícil por causa de recursos dinâmicos como despacho dinâmico e __getattr__

    • A estrutura de módulos do Python também é um fator que complica o tree-shaking

Resumo

  • Com suporte a GC, o WebAssembly passa a permitir programação de DOM em linguagens além de JavaScript
  • Mas, para tornar o tamanho do resultado suficientemente pequeno, é necessário um investimento considerável no toolchain de cada linguagem
  • São necessários o desenvolvimento de toolchains separados com algoritmos de tree-shaking aplicados e a otimização das bibliotecas padrão

A opinião do GN⁺

  • Com o suporte a GC no WebAssembly, tornou-se possível usar várias linguagens no desenvolvimento web, mas parece haver muitas dificuldades em simplesmente levar os toolchains das linguagens existentes como estão. Parece que será preciso desenvolver implementações de linguagem e técnicas de otimização especializadas para o ambiente web.

  • Para que o tree-shaking funcione bem em linguagens de tipagem dinâmica, a análise estática parece ser indispensável. Mas linguagens como Python têm muitos recursos dinâmicos, então isso não deve ser fácil. Talvez uma alternativa seja criar desde o início uma nova linguagem mais favorável à análise estática.

  • Projetos experimentais como Hoot e TinyGo parecem ser boas referências. Mas talvez ainda seja cedo para aplicar esse tipo de projeto em produtos reais. Provavelmente não há outro caminho além de melhorias graduais.

  • Para projetos em que desempenho não é tão sensível e a velocidade de desenvolvimento é mais importante, algo como Pyodide pode valer a pena. Mas, para produtos em que a experiência do usuário é prioridade, no momento JavaScript ainda parece ser a melhor escolha.

  • Também dá para imaginar colocar no próprio WebAssembly algum recurso parecido com tree-shaking. Mas isso não deve ser simples, porque os requisitos variam conforme a linguagem. E, se surgir uma linguagem que dê ótimo suporte a tree-shaking, talvez passe a valer mais a pena programar diretamente nela. Fica a curiosidade sobre como será dividida a responsabilidade entre WebAssembly e linguagens de programação.

1 comentários

 
GN⁺ 2024-04-15
Comentários do Hacker News

Em resumo, é o seguinte:

  • No projeto OpenEtG, foram feitos os seguintes esforços para manter o tamanho do binário WASM escrito em Rust abaixo de 400 KB
    • usar aritmética de ponto fixo em vez de float
    • usar Vec em vez de HashMap
    • minimizar o uso de strings
    • usar um alocador pequeno (talc)
    • minimizar dependências (usar apenas rand e fxhash)
    • evitar variedade excessiva de genéricos
    • projetar algoritmos levando o tamanho em consideração
  • Tree-shaking é uma nomenclatura equivocada; no compilador Virgil, isso é chamado de Reachability Analysis. Durante o processo de compilação, a análise parte do ponto de entrada main e inclui no binário final apenas o código que pode ser alcançado.
  • Graças ao WasmGC, Java e Kotlin conseguem gerar binários WASM pequenos, na faixa de 2 a 3 KB. Porém, é preciso ter cuidado na escolha das APIs.
  • A manipulação do DOM com WASM ainda continua dependente de JS.
  • O termo Tree Shaking surgiu porque Dead Code Elimination já existia havia muito tempo.
  • O problema do tamanho de código no WASM acontece porque é necessário empacotar tanto o runtime da linguagem quanto a biblioteca padrão.
  • Para resolver isso, pode-se considerar bibliotecas compartilhadas e linkagem dinâmica.
    • Pyodide é um exemplo representativo que oferece suporte a linkagem dinâmica
    • se o navegador pré-carregasse runtimes de linguagens populares, as páginas web poderiam compartilhar esse runtime
  • A linguagem Zig é adequada para gerar binários WASM pequenos. Mas, abaixo de 100 KB, o tamanho não é um fator importante.
  • Um GC embutido não é importante para todos os aplicativos, e é melhor criar webapps sem GC.
  • O fator de sucesso para apps que usam WASM ainda é o ganho de desempenho.
  • Há muito tempo já se fazia programação de DOM em linguagens que não eram JS, por meio de linguagens que compilam para JS como ClojureScript, TypeScript e ReasonML.
  • Antes do WASM, linguagens baseadas em C já eram compiladas e usadas na web por meio de asm.js e emscripten.
  • Google Maps e Google Earth são exemplos representativos de apps que usam WASM.