2 puntos por GN⁺ 2024-04-15 | 1 comentarios | Compartir por WhatsApp

Los límites de WebAssembly y la importancia del tree-shaking

  • WebAssembly, a pesar de mucho interés y expectativas, solo ha tenido un éxito limitado en la web

    • Hay casos exitosos como Photoshop, pero en general no hay muchos proyectos que aprovechen WebAssembly
    • En especial, WebAssembly no es adecuado para apps que usan mucho el DOM
    • Una de las principales razones es la diferencia entre los modelos de programación de JavaScript y WebAssembly
  • WebAssembly no ha tenido mucho éxito fuera de lenguajes como C o Rust

    • Lenguajes como C# tienen la incomodidad de tener que incluir también un runtime como el recolector de basura
    • Sin embargo, se espera que la situación mejore porque pronto se incorporarán nuevas funciones de WebAssembly que soportan tipos por referencia y recolección de basura

La capacidad de optimización de código del compilador es la clave del éxito de WebAssembly

  • Para que WebAssembly tenga éxito en la web, los compiladores deben poder generar código pequeño y eficiente

    • Es importante mantener tamaños de archivo pequeños, del orden de unos pocos kilobytes
    • Si no, no queda otra que depender del hype o de una base de usuarios específica
  • En el mundo de JavaScript, la optimización del tamaño del código se logra mediante bundlers y herramientas similares

    • Tree-shaking es una técnica que incluye solo las funciones y tipos de datos que realmente se usan en el programa
  • Tree-shaking es un término ampliamente usado, aunque sea una metáfora inadecuada tanto en sentido horticultural como algorítmico

Estado actual del tree-shaking en otros lenguajes

  • En lenguajes con runtimes pesados como Go o Python, el tree-shaking todavía no está bien optimizado

    • Incluso el programa más simple en Go supera los 2 MB al compilarse a WebAssembly
    • Pyodide de Python también requiere descargar archivos de alrededor de 20 MB
  • En entornos de servidor, el tamaño del binario no suele ser un gran problema

    • Para entornos limitados como móviles, también se desarrollan toolchains más ligeras por separado, como MicroPython y TinyGo
  • Las implementaciones de lenguajes para la web inevitablemente tienen que diferir de las existentes

    • Porque interactuar con el DOM es en sí mismo un entorno particular
    • En el caso de ClojureScript, las diferencias con Clojure se documentan por separado

Discusión sobre el algoritmo de tree-shaking

  • El compilador Hoot Scheme que está desarrollando el autor actualmente genera código Wasm de alrededor de 70 KB

    • Incluir solo definiciones de funciones (procedimientos) es relativamente fácil
    • Pero hay varios puntos difíciles como los siguientes
  • En el modelo de evaluación de letrec*, los bindings son recursivos y además tienen orden, lo que dificulta el análisis del compilador

    • En el caso de los tipos de registro, los callbacks de la vtable hacen que mucho código permanezca vivo
  • Si se usan funciones con alto grado de polimorfismo como display, se incluye mucho código relacionado

    • Es mejor usar funciones concretas como write-string
  • Para un tree-shaking óptimo se necesita flow analysis

    • Si se sabe que no se pasa un argumento bitvector a display, se puede eliminar el código relacionado
  • En Python es todavía más difícil debido al despacho dinámico y a funciones dinámicas como __getattr__

    • La estructura de módulos de Python también complica el tree-shaking

Resumen

  • Gracias al soporte de GC, en WebAssembly será posible programar el DOM con lenguajes distintos de JavaScript
  • Pero para reducir lo suficiente el tamaño del resultado, se requiere una inversión considerable en el toolchain de cada lenguaje
  • Se necesitan un toolchain independiente que aplique algoritmos de tree-shaking y optimización de la biblioteca estándar

Opinión de GN⁺

  • Con el soporte de GC en WebAssembly, se podrán usar varios lenguajes en el desarrollo web, pero parece que hay muchas dificultades para llevarse sin más los toolchains existentes. Da la impresión de que harán falta implementaciones de lenguajes y técnicas de optimización especializadas para la web.

  • Para que el tree-shaking funcione bien en lenguajes de tipado dinámico, parece indispensable el análisis estático. Pero en lenguajes como Python, que tienen muchas funciones dinámicas, no parece nada fácil. Quizá incluso podría ser una opción crear desde el principio un lenguaje más favorable al análisis estático.

  • Proyectos experimentales como Hoot o TinyGo parecen buenas referencias. Pero todavía podría ser demasiado pronto para aplicar este tipo de proyectos en productos reales. Probablemente no quede otra que mejorar de forma gradual.

  • Si se trata de un proyecto donde el rendimiento no es tan sensible y lo importante es desarrollar rápido, algo como Pyodide podría valer la pena. Pero si el producto prioriza la experiencia de usuario, por ahora JavaScript parece la mejor opción.

  • También podría pensarse en incorporar funciones como tree-shaking dentro del propio WebAssembly. Pero no sería fácil porque los requisitos varían según el lenguaje. Además, si aparece un lenguaje que soporte muy bien el tree-shaking, tal vez convenga más programar directamente en ese lenguaje. Da curiosidad ver cómo se repartirá el rol entre WebAssembly y los lenguajes de programación.

1 comentarios

 
GN⁺ 2024-04-15
Opinión de Hacker News

En resumen, es lo siguiente:

  • En el proyecto OpenEtG, se hicieron esfuerzos como los siguientes para mantener el tamaño de los binarios WASM escritos en Rust por debajo de 400KB
    • usar aritmética de punto fijo en lugar de float
    • usar Vec en lugar de HashMap
    • minimizar el uso de cadenas de texto
    • usar un asignador pequeño (talc)
    • minimizar las dependencias (solo usar rand y fxhash)
    • evitar la diversidad de genéricos
    • diseñar algoritmos teniendo en cuenta el tamaño
  • Tree-shaking es una denominación incorrecta, y en el compilador Virgil se le llama Reachability Analysis. Durante el proceso de compilación, se recorre desde el punto de entrada main y solo el código alcanzable se incluye en el binario final.
  • Gracias a WasmGC, Java y Kotlin pueden generar binarios WASM pequeños, de unos 2-3KB. Pero hay que tener cuidado con la elección de APIs.
  • La manipulación del DOM con WASM sigue dependiendo de JS.
  • La razón por la que surgió el término Tree Shaking es que Dead Code Elimination ya existía desde hace mucho tiempo.
  • El problema del tamaño del código en WASM surge porque hay que empaquetar tanto el runtime del lenguaje como la biblioteca estándar.
  • Para resolver esto, se pueden considerar las bibliotecas compartidas y el enlazado dinámico.
    • Pyodide es un ejemplo representativo que soporta enlazado dinámico
    • si el navegador precargara runtimes de lenguajes populares, las páginas web podrían compartir ese runtime
  • El lenguaje Zig es adecuado para generar binarios WASM pequeños. Pero si es menos de 100KB, el tamaño no es un factor importante.
  • Un GC integrado no es importante para todas las apps, y es preferible crear webapps sin GC.
  • El factor que sigue determinando el éxito de las apps que usan WASM es la mejora de rendimiento.
  • Desde hace mucho tiempo se programa el DOM en lenguajes distintos de JS mediante lenguajes que compilan a JS como ClojureScript, TypeScript y ReasonML.
  • Incluso antes de WASM, ya se compilaban y usaban en la web lenguajes basados en C mediante asm.js y emscripten.
  • Google Maps y Google Earth son apps representativas que usan WASM.