La recolección de elementos no utilizados de WebAssembly (WasmGC) ahora está habilitada de forma predeterminada en Chrome

Existen dos tipos de lenguajes de programación: los de programación de recolección de elementos no utilizados y los lenguajes de programación que requieren administración manual de la memoria. Algunos ejemplos de los anteriores son Kotlin, PHP o Java. Algunos ejemplos de estas últimas son C, C++ o Rust. Como regla general, es más probable que los lenguajes de programación de nivel superior cuenten con la recolección de elementos no utilizados como función estándar. En esta entrada de blog, el enfoque está en los lenguajes de programación de recolección de elementos no utilizados y cómo se pueden compilar en WebAssembly (Wasm). Pero ¿qué es la recolección de elementos no utilizados (a menudo denominada GC) para comenzar?

Navegadores compatibles

  • 119
  • 119
  • 120
  • x

Recolección de elementos no utilizados

En términos simplificados, la idea de la recolección de elementos no utilizados es el intento de recuperar la memoria que asignó el programa, pero ya no se hace referencia a ella. A esa memoria se la llama basura. Hay muchas estrategias para implementar la recolección de elementos no utilizados. Una de ellas es el recuento de referencias, en el que el objetivo es contar la cantidad de referencias a objetos en la memoria. Cuando no hay más referencias a un objeto, se puede marcar como ya no usado y, por lo tanto, listo para la recolección de elementos no utilizados. El recolector de elementos no utilizados de PHP usa el recuento de referencias, y el uso de la función xdebug_debug_zval() de la extensión Xdebug te permite conocer más detalles. Considera el siguiente programa PHP.

<?php
  $a= (string) rand();
  $c = $b = $a;
  $b = 42;
  unset($c);
  $a = null;
?>

El programa asigna un número al azar convertido en una cadena a una nueva variable llamada a. Luego, crea dos variables nuevas, b y c, y les asigna el valor de a. Después, reasigna b al número 42 y, luego, quita la configuración c. Por último, establece el valor de a en null. Si se anota cada paso del programa con xdebug_debug_zval(), puedes ver el contador de referencias del recolector de elementos no utilizados en funcionamiento.

<?php
  $a= (string) rand();
  $c = $b = $a;
  xdebug_debug_zval('a');
  $b = 42;
  xdebug_debug_zval('a');
  unset($c);
  xdebug_debug_zval('a');
  $a = null;
  xdebug_debug_zval('a');
?>

En el ejemplo anterior, se mostrarán los siguientes registros, en los que verás cómo la cantidad de referencias al valor de la variable a disminuye después de cada paso, lo que tiene sentido según la secuencia de código. (El número al azar será diferente, por supuesto).

a:
(refcount=3, is_ref=0)string '419796578' (length=9)
a:
(refcount=2, is_ref=0)string '419796578' (length=9)
a:
(refcount=1, is_ref=0)string '419796578' (length=9)
a:
(refcount=0, is_ref=0)null

La recolección de elementos no utilizados presenta otros desafíos, como los ciclos de detección, pero, para este artículo, basta con tener un nivel básico de comprensión del recuento de referencias.

Los lenguajes de programación se implementan en otros lenguajes de programación.

Puede parecer un origen, pero los lenguajes de programación se implementan en otros lenguajes de programación. Por ejemplo, el entorno de ejecución de PHP se implementa principalmente en C. Puedes consultar el código fuente de PHP en GitHub. El código de recolección de elementos no utilizados de PHP se encuentra principalmente en el archivo zend_gc.c. La mayoría de los desarrolladores instalan PHP a través del administrador de paquetes de su sistema operativo. Sin embargo, también pueden compilar PHP a partir del código fuente. Por ejemplo, en un entorno de Linux, los pasos ./buildconf && ./configure && make crearían PHP para el entorno de ejecución de Linux. Pero esto también significa que el entorno de ejecución de PHP puede compilarse para otros entornos de ejecución, como Wasm.

Métodos tradicionales de portación de lenguajes al entorno de ejecución de Wasm

Independientemente de la plataforma en la que se ejecuta PHP, las secuencias de comandos de PHP se compilan en el mismo código de bytes y se ejecutan con Zend Engine. Zend Engine es un entorno de ejecución y compilación para el lenguaje de programación PHP. Está compuesto por la máquina virtual (VM) de Zend, que está compuesta por el compilador de Zend y el ejecutor de Zend. Lenguajes como PHP que se implementan en otros lenguajes de alto nivel como C por lo general tienen optimizaciones que apuntan a arquitecturas específicas, como Intel o ARM, y requieren un backend diferente para cada arquitectura. En este contexto, Wasm representa una arquitectura nueva. Si la VM tiene un código específico de la arquitectura, como la compilación justo a tiempo (JIT) o por adelantado (AOT), el desarrollador también implementará un backend de JIT/AOT para la nueva arquitectura. Este enfoque tiene mucho sentido porque, con frecuencia, la parte principal de la base de código se puede volver a compilar para cada arquitectura nueva.

Dada lo bajo que es Wasm, es natural probar el mismo enfoque allí: volver a compilar el código de la VM principal con su analizador, compatibilidad con bibliotecas, recolección de elementos no utilizados y optimizador de Wasm, e implementar un backend JIT o AOT para Wasm si es necesario. Esto ha sido posible desde el MVP de Wasm y funciona bien en la práctica en muchos casos. De hecho, WordPress Playground es la compilación de PHP con Wasm. Obtén más información sobre el proyecto en el artículo Crea experiencias de WordPress en el navegador con WordPress Playground y WebAssembly.

Sin embargo, Wasm de PHP se ejecuta en el navegador en el contexto del lenguaje JavaScript del host. En Chrome, JavaScript y Wasm se ejecutan en V8, el motor de JavaScript de código abierto de Google que implementa ECMAScript, como se especifica en ECMA-262. Además, V8 ya tiene un recolector de elementos no utilizados. Esto significa que los desarrolladores que usan, por ejemplo, PHP compilado en Wasm, terminan enviando una implementación de recolector de elementos no utilizados del lenguaje con portabilidad (PHP) al navegador que ya tiene un recolector de elementos no utilizados, que es tan desperdiciado como parece. Aquí es donde entra en juego WasmGC.

El otro problema del enfoque anterior de permitir que los módulos de Wasm compilen su propia recolección de elementos no utilizados sobre la memoria lineal de Wasm es que no hay interacción entre el recolector de elementos no utilizados de Wasm y el recolector integrado de elementos no utilizados del lenguaje compilado a Wasm, que tiende a causar problemas como fugas de memoria e intentos de recolección ineficientes. Permitir que los módulos de Wasm reutilicen el GC integrado existente evita estos problemas.

Cómo migrar lenguajes de programación a nuevos entornos de ejecución con WasmGC

WasmGC es una propuesta del grupo de la comunidad WebAssembly. La implementación actual del MVP de Wasm solo es capaz de manejar con números, es decir, enteros y flotantes, en la memoria lineal, y con la propuesta de tipos de referencia que se está enviando, Wasm también puede conservar referencias externas. WasmGC ahora agrega tipos de montón de struct y array, lo que significa compatibilidad con la asignación de memoria no lineal. Cada objeto WasmGC tiene un tipo y una estructura fijos, lo que facilita que las VMs generen un código eficiente para acceder a sus campos sin el riesgo de las desoptimización que tienen los lenguajes dinámicos como JavaScript. De este modo, esta propuesta agrega compatibilidad eficiente para lenguajes administrados de alto nivel a WebAssembly, a través de tipos de montón de struct y array que permiten que los compiladores de lenguaje orientados a Wasm se integren con un recolector de elementos no utilizados en la VM host. En términos simplificados, esto significa que con WasmGC, la portabilidad de un lenguaje de programación a Wasm significa que el recolector de elementos no utilizados del lenguaje de programación ya no necesita ser parte del puerto, sino que se puede usar el recolector existente.

Para comprobar el impacto en el mundo real de esta mejora, el equipo de Wasm de Chrome compiló versiones de la comparativa de Fannkuch (que asigna estructuras de datos mientras funciona) a partir de C, Rust y Java. Los objetos binarios de C y Rust pueden ser de entre 6.1 K y 9.6 K, según los diversos indicadores del compilador, mientras que la versión de Java es mucho más pequeña, con solo 2.3 K. C y Rust no incluyen un recolector de elementos no utilizados, pero sí agrupan malloc/free para administrar la memoria. La razón por la que Java es más pequeño aquí es porque no necesita empaquetar ningún código de administración de memoria. Este es solo un ejemplo específico, pero muestra que los objetos binarios de WasmGC tienen el potencial de ser muy pequeños, incluso antes de cualquier trabajo importante de optimización en función del tamaño.

Ver un lenguaje de programación adaptado a WasmGC en acción

Wasm en Kotlin

Uno de los primeros lenguajes de programación que se transfirió a Wasm gracias a WasmGC es Kotlin en forma de Kotlin/Wasm. La demostración, que incluye código fuente cortesía del equipo de Kotlin, se muestra en la siguiente lista.

import kotlinx.browser.document
import kotlinx.dom.appendText
import org.w3c.dom.HTMLDivElement

fun main() {
    (document.getElementById("warning") as HTMLDivElement).style.display = "none"
    document.body?.appendText("Hello, ${greet()}!")
}

fun greet() = "world"

Es posible que te preguntes cuál es el punto, ya que el código Kotlin anterior básicamente consiste en las APIs de JavaScript OM convertidas a Kotlin. Comienza a tener más sentido en combinación con Compose multiplataforma, que permite a los desarrolladores compilar sobre la IU que quizás ya crearon para sus apps de Kotlin para Android. Realiza una exploración temprana de esto con la demostración del visualizador de imágenes de Kotlin/Wasm y explora su código fuente, como cortesía del equipo de Kotlin.

Dart y Flutter

Los equipos de Dart y Flutter de Google también están preparando la asistencia para WasmGC. El trabajo de compilación de Dart-to-Wasm está casi completo, y el equipo está trabajando en la compatibilidad de herramientas para entregar aplicaciones web de Flutter compiladas para WebAssembly. Puedes obtener más información sobre el estado actual del trabajo en la documentación de Flutter. La siguiente demostración es la vista previa de WasmGC de Flutter.

Más información sobre WasmGC

Esta entrada de blog apenas se adelantó y proporcionó una descripción general de alto nivel de WasmGC. Para obtener más información sobre la función, consulta estos vínculos:

Agradecimientos

Hero image de Gary Chan en Unsplash. Matthias Liedtke, Adam Klein, Joshua Bell, Alon Zakai, Jakob Kummerow, Clemens Backes, Emanuel Ziegler y Rachelw, revisaron este artículo.