Una introducción resumida en dibujos animados a WebAssembly

 

 

 

  • ¡Registro!
  • Implemente rápidamente. Implementar inteligentemente

  • Índice
    1. Un poco de historia del rendimiento
    2. Cómo se ejecuta JavaScript en el navegador
      1. Pros y contras del intérprete
      2. Pros y contras del compilador
    3. Comparemos: dónde se gasta el tiempo cuando se ejecuta JavaScript vs. Asamblea web
      1. Atractivo
      2. Analizando
      3. Compilando + Optimizando
      4. Reoptimizando
      5. ejecutando
      6. Recolección de basura
    4. ¿Cómo funciona WebAssembly?
      1. ¿Dónde encaja WebAssembly?
    5. Compilando en .wasm
    6. Cargando un módulo .wasm en JavaScript
    7. ¿Cuál es el estado de WebAssembly?
    8. Conclusión
      1. Otras lecturas

    WebAssembly es una forma de tomar código escrito en lenguajes de programación distintos de JavaScript y ejecutar ese código en el navegador. Cuando se habla de WebAssembly, la comparación de manzanas con manzanas es con JavaScript. Con WebAssembly, es posible ejecutar código en la web más rápido. Hay varias razones por las que el código de WebAssembly se ejecuta más rápido que su equivalente de JavaScript, pero es útil comparar los dos para poder comprender el impacto potencial que tendrá WebAssembly.

     

    Hoy en día hay mucho revuelo sobre WebAssembly en los círculos de JavaScript. La gente habla de lo increíblemente rápido que es y de cómo revolucionará el desarrollo web. Pero la mayoría de las conversaciones no entran en detalles de por qué es rápido. En este artículo, Lin Clark explica qué es exactamente lo que hace que WebAssembly sea rápido.

    Pero antes de comenzar, ¿qué es? WebAssembly es una forma de tomar código escrito en lenguajes de programación distintos de JavaScript y ejecutar ese código en el navegador.

    Cuando se habla de WebAssembly, la comparación de manzanas con manzanas es con JavaScript. Ahora, no quiero dar a entender que se trata de una situación u otra: que estás usando WebAssembly o JavaScript. De hecho, esperamos que los desarrolladores utilicen WebAssembly y JavaScript de la mano, en la misma aplicación. Pero es útil comparar los dos para poder comprender el impacto potencial que tendrá WebAssembly.

    Un poco de historia del rendimiento

    JavaScript se creó en 1995. No fue diseñado para ser rápido y, durante la primera década, no lo fue.

    Luego, los navegadores empezaron a volverse más competitivos.

    En 2008 comenzó un período que la gente llama la guerra del desempeño. Varios navegadores agregaron compiladores justo a tiempo, también llamados JIT. Mientras se ejecutaba JavaScript, el JIT podía ver patrones y hacer que el código se ejecutara más rápido en función de esos patrones.

    La introducción de estos JIT condujo a un punto de inflexión en el rendimiento del código que se ejecuta en el navegador. De repente, JavaScript se ejecutaba 10 veces más rápido.

    Con este rendimiento mejorado, JavaScript comenzó a usarse para cosas que nadie esperaba, como aplicaciones creadas con Node.js y Electron.

    Es posible que ahora estemos en otro de esos puntos de inflexión con WebAssembly.

    Antes de que podamos comprender las diferencias de rendimiento entre JavaScript y WebAssembly, debemos comprender el trabajo que realiza el motor JS.

    Cómo se ejecuta JavaScript en el navegador

    Cuando usted, como desarrollador, agrega JavaScript a la página, tiene un objetivo y un problema.

    • Objetivo: quieres decirle a la computadora qué hacer.
    • Problema: usted y la computadora hablan idiomas diferentes.

    Tú hablas un lenguaje humano y la computadora habla un lenguaje de máquina. Incluso si no piensa en JavaScript u otros lenguajes de programación de alto nivel como lenguajes humanos, realmente lo son. Han sido diseñados para la cognición humana, no para la cognición mecánica.

     

    Entonces, el trabajo del motor JavaScript es tomar el lenguaje humano y convertirlo en algo que la máquina entienda.

    Pienso en esto como en la película Arrival, donde hay humanos y extraterrestres que intentan hablar entre sí.

    En esa película, los humanos y los extraterrestres no pueden simplemente traducir de un idioma a otro, palabra por palabra. Los dos grupos tienen diferentes maneras de pensar sobre el mundo, lo que se refleja en su lenguaje. Y eso también se aplica a los humanos y a las máquinas.

    Entonces, ¿cómo se produce la traducción?

    En programación, generalmente existen dos formas de traducir al lenguaje de máquina. Puede utilizar un intérprete o un compilador.

    Con un intérprete, esta traducción se realiza prácticamente línea por línea, sobre la marcha.

    Por otro lado, un compilador trabaja con anticipación y escribe la traducción.

    Hay ventajas y desventajas en cada una de estas formas de manejar la traducción.

    Pros y contras del intérprete

    Los intérpretes ponen en marcha el código rápidamente. No es necesario realizar todo el paso de compilación antes de poder comenzar a ejecutar el código. Debido a esto, un intérprete parece una opción natural para algo como JavaScript. Es importante que un desarrollador web pueda tener ese ciclo de retroalimentación inmediata.

    Y esa es parte de la razón por la que los navegadores usaban intérpretes de JavaScript al principio.

    Pero la desventaja de usar un intérprete surge cuando ejecutas el mismo código más de una vez. Por ejemplo, si estás en un bucle. Luego tienes que hacer la misma traducción una y otra vez.

    Pros y contras del compilador

    El compilador tiene las contrapartidas opuestas. Se necesita un poco más de tiempo para iniciar porque tiene que pasar por ese paso de compilación al principio. Pero entonces el código en bucles se ejecuta más rápido, porque no necesita repetir la traducción para cada paso por ese bucle.

    Como una forma de deshacerse de la ineficiencia del intérprete (donde el intérprete tiene que seguir retraduciendo el código cada vez que recorre el bucle), los navegadores comenzaron a mezclar compiladores.

    Los diferentes navegadores hacen esto de maneras ligeramente diferentes, pero la idea básica es la misma. Agregaron una nueva parte al motor JavaScript, llamada monitor (también conocido como generador de perfiles). Ese monitor observa el código mientras se ejecuta y toma nota de cuántas veces se ejecuta y qué tipos se utilizan.

    Si las mismas líneas de código se ejecutan varias veces, ese segmento de código se denomina cálido. Si funciona mucho, se llama caliente. El código caliente se pasa por un compilador básico, lo que lo acelera un poco. El código activo se pasa por un compilador de optimización, lo que lo acelera más.

    Para obtener más información, lea el artículo completo sobre compilación justo a tiempo .

    Comparemos: dónde se gasta el tiempo cuando se ejecuta JavaScript vs. Asamblea web

    Este diagrama ofrece una imagen aproximada de cómo podría ser el rendimiento de inicio de una aplicación hoy en día, ahora que los compiladores JIT son comunes en los navegadores.

    "Este diagrama muestra dónde pasa el tiempo el motor JS para una aplicación hipotética. Esto no muestra un promedio. El tiempo que el motor JS dedica a realizar cualquiera de estas tareas depende del tipo de trabajo que realiza el JavaScript en la página. ... Pero podemos utilizar este diagrama para construir un modelo mental".

     

    Cada barra muestra el tiempo dedicado a realizar una tarea en particular.

    • Análisis: el tiempo que lleva procesar el código fuente y convertirlo en algo que el intérprete pueda ejecutar.
    • Compilación + optimización: el tiempo que se dedica al compilador de referencia y a la optimización del compilador. Parte del trabajo del compilador de optimización no está en el hilo principal, por lo que no se incluye aquí.
    • Reoptimización: el tiempo que el JIT dedica a reajustarse cuando sus suposiciones han fallado, tanto para volver a optimizar el código como para rescatar el código optimizado y volver al código base.
    • Ejecución: el tiempo que lleva ejecutar el código.
    • Recolección de basura: el tiempo dedicado a limpiar la memoria.

    Una cosa importante a tener en cuenta: estas tareas no ocurren en partes discretas ni en una secuencia particular. En cambio, estarán entrelazados. Se realizará un poco de análisis, luego algo de ejecución, luego algo de compilación, luego algo más de análisis, luego algo más de ejecución, etc.

    Este desglose del rendimiento es una gran mejora con respecto a los primeros días de JavaScript, que se habría parecido más a esto:

    Al principio, cuando era solo un intérprete ejecutando JavaScript, la ejecución era bastante lenta. Cuando se introdujeron los JIT, se aceleró drásticamente el tiempo de ejecución.

    La desventaja es la sobrecarga de monitorear y compilar el código. Si los desarrolladores de JavaScript siguieran escribiendo JavaScript de la misma manera que lo hacían entonces, los tiempos de análisis y compilación serían mínimos. Pero el rendimiento mejorado llevó a los desarrolladores a crear aplicaciones JavaScript más grandes.

    Esto significa que todavía hay margen de mejora.

    A continuación se ofrece una aproximación de cómo se compararía WebAssembly para una aplicación web típica.

    Existen ligeras variaciones entre los motores JS de los navegadores. Me estoy basando en esto en SpiderMonkey.

    Atractivo

    Esto no se muestra en el diagrama, pero una cosa que lleva tiempo es simplemente recuperar el archivo del servidor.

    Se necesita menos tiempo para descargar WebAssembly; hace el equivalente a JavaScript, porque es más compacto. WebAssembly fue diseñado para ser compacto y puede expresarse en forma binaria.

    Aunque el JavaScript comprimido con gzip es bastante pequeño, es probable que el código equivalente en WebAssembly sea más pequeño.

    Esto significa que lleva menos tiempo transferirlo entre el servidor y el cliente. Esto es especialmente cierto en redes lentas.

    Analizando

    Una vez que llega al navegador, la fuente de JavaScript se analiza en un árbol de sintaxis abstracta.

    Los navegadores a menudo hacen esto de manera perezosa, al principio solo analizan lo que realmente necesitan y simplemente crean apéndices para funciones que aún no han sido llamadas.

    A partir de ahí, el AST se convierte en una representación intermedia (llamada código de bytes) que es específica de ese motor JS.

     

    Por el contrario, WebAssembly no necesita pasar por esta transformación porque ya es un código de bytes. Sólo necesita ser decodificado y validado para asegurarse de que no contenga ningún error.

    Compilando + Optimizando

    Como expliqué antes, JavaScript se compila durante la ejecución del código. Debido a que los tipos en JavaScript son dinámicos, es posible que sea necesario compilar varias versiones del mismo código para diferentes tipos. Esto lleva tiempo.

    Por el contrario, WebAssembly comienza mucho más cerca del código de máquina. Por ejemplo, los tipos son parte del programa. Esto es más rápido por varias razones:

    • El compilador no tiene que dedicar tiempo a ejecutar el código para observar qué tipos se utilizan antes de comenzar a compilar el código optimizado.
    • El compilador no tiene que compilar diferentes versiones del mismo código en función de los diferentes tipos que observa.
    • Ya se han realizado más optimizaciones con anticipación en LLVM. Por lo tanto, se necesita menos trabajo para compilarlo y optimizarlo.

    Reoptimizando

    A veces, el JIT tiene que descartar una versión optimizada del código y volver a intentarlo.

    Esto sucede cuando las suposiciones que hace el JIT basándose en el código en ejecución resultan ser incorrectas. Por ejemplo, la desoptimización ocurre cuando las variables que entran en un bucle son diferentes a las de iteraciones anteriores, o cuando se inserta una nueva función en la cadena del prototipo. Estados para Whatsapp

    En WebAssembly, elementos como los tipos son explícitos, por lo que JIT no necesita hacer suposiciones sobre los tipos basándose en los datos que recopila durante el tiempo de ejecución. Esto significa que no tiene que pasar por ciclos de reoptimización.

    ejecutando

    Es posible escribir JavaScript que se ejecute de manera eficiente. Para hacerlo es necesario conocer las optimizaciones que realiza el JIT.

    Sin embargo, la mayoría de los desarrolladores no conocen los aspectos internos de JIT. Incluso para aquellos desarrolladores que conocen los aspectos internos de JIT, puede resultar difícil alcanzar el punto óptimo. Muchos patrones de codificación que la gente usa para hacer que su código sea más legible (como abstraer tareas comunes en funciones que funcionan en todos los tipos) se interponen en el camino del compilador cuando intenta optimizar el código.

    Debido a esto, la ejecución de código en WebAssembly es generalmente más rápida. Muchas de las optimizaciones que los JIT realizan en JavaScript simplemente no son necesarias con WebAssembly.

    Además, WebAssembly fue diseñado como un objetivo del compilador. Esto significa que fue diseñado para que lo generen los compiladores y no para que lo escriban programadores humanos.

    Dado que los programadores humanos no necesitan programarlo directamente, WebAssembly puede proporcionar un conjunto de instrucciones que son más ideales para las máquinas. Dependiendo del tipo de trabajo que esté haciendo su código, estas instrucciones se ejecutan entre un 10% y un 800% más rápido.

    Recolección de basura

    En JavaScript, el desarrollador no tiene que preocuparse por borrar variables antiguas de la memoria cuando ya no son necesarias. En cambio, el motor JS lo hace automáticamente utilizando algo llamado recolector de basura.

     

    Sin embargo, esto puede ser un problema si desea un rendimiento predecible. No controlas cuándo el recolector de basura hace su trabajo, por lo que puede llegar en un momento inconveniente.

    Por ahora, WebAssembly no admite ninguna recolección de basura. La memoria se gestiona manualmente (como ocurre en lenguajes como C y C++). Si bien esto puede dificultar la programación para el desarrollador, también hace que el rendimiento sea más consistente.

    En conjunto, estas son todas las razones por las que, en muchos casos, WebAssembly superará a JavaScript al realizar la misma tarea.

    Hay algunos casos en los que WebAssembly no funciona tan bien como se esperaba y también hay algunos cambios en el horizonte que lo harán más rápido. He cubierto estas características futuras con más profundidad en otro artículo.

    ¿Cómo funciona WebAssembly?

    Ahora que comprende por qué los desarrolladores están entusiasmados con WebAssembly, veamos cómo funciona.

    Cuando hablé anteriormente de los JIT, hablé de cómo comunicarse con la máquina es como comunicarse con un extraterrestre.

    Quiero echar un vistazo ahora a cómo funciona ese cerebro alienígena: cómo el cerebro de la máquina analiza y comprende la comunicación que llega a él.

    Hay una parte de este cerebro que se dedica al pensamiento, por ejemplo, a la aritmética y la lógica. También hay una parte del cerebro cerca de la que proporciona la memoria a corto plazo y otra parte que proporciona la memoria a largo plazo.

    Estas diferentes partes tienen nombres.

    • La parte que piensa es la Unidad Aritmético-Lógica (ALU).
    • La memoria a corto plazo la proporcionan los registros.
    • La memoria a largo plazo es la memoria de acceso aleatorio (o RAM).

    Las oraciones en código máquina se llaman instrucciones.

    ¿Qué sucede cuando una de estas instrucciones llega al cerebro? Se divide en diferentes partes que significan cosas diferentes.

    La forma en que se divide esta instrucción es específica del cableado de este cerebro.

    Por ejemplo, este cerebro siempre podría tomar los bits 4 a 10 y enviarlos a la ALU. La ALU descubrirá, basándose en la ubicación de unos y ceros, que necesita sumar dos cosas.

    Este fragmento se denomina "código de operación" o código de operación, porque le dice a la ALU qué operación realizar.

    Luego, este cerebro tomaría los siguientes dos fragmentos para determinar qué dos números debería sumar. Estas serían direcciones de los registros.

    Tenga en cuenta las anotaciones que agregué encima del código de máquina aquí, que nos facilitan entender lo que está sucediendo. Esto es lo que es la asamblea. Se llama código de máquina simbólico. Es una forma para que los humanos le den sentido al código de máquina.

    Puede ver aquí que existe una relación bastante directa entre el ensamblador y el código de máquina para esta máquina. Cuando tienes una arquitectura diferente dentro de una máquina, es probable que requiera su propio dialecto de ensamblaje.

    Así que no tenemos un solo objetivo para nuestra traducción. En lugar de ello, nos centramos en muchos tipos diferentes de código de máquina. Así como hablamos diferentes idiomas como personas, las máquinas hablan diferentes idiomas.

     

    Desea poder traducir cualquiera de estos lenguajes de programación de alto nivel a cualquiera de estos lenguajes ensambladores. Una forma de hacerlo sería crear un montón de traductores diferentes que puedan pasar de cada idioma a cada ensamblado.

    Eso será bastante ineficiente. Para solucionar esto, la mayoría de los compiladores colocan al menos una capa intermedia. El compilador tomará este lenguaje de programación de alto nivel y lo traducirá a algo que no sea de tan alto nivel, pero que tampoco funcione al nivel de código de máquina. Y eso se llama representación intermedia (IR).

    Esto significa que el compilador puede tomar cualquiera de estos lenguajes de nivel superior y traducirlo al lenguaje IR. A partir de ahí, otra parte del compilador puede tomar ese IR y compilarlo en algo específico para la arquitectura de destino.

    La interfaz del compilador traduce el lenguaje de programación de nivel superior al IR. El backend del compilador va desde IR al código ensamblador de la arquitectura de destino.

    ¿Dónde encaja WebAssembly?

    Se podría pensar en WebAssembly como uno más de los lenguajes ensambladores de destino. Eso es cierto, excepto que cada uno de esos lenguajes (x86, ARM, etc.) corresponde a una arquitectura de máquina particular.

    Cuando entregas código para que se ejecute en la máquina del usuario a través de la web, no sabes en qué arquitectura de destino se ejecutará el código.

    Entonces WebAssembly es un poco diferente a otros tipos de ensamblaje. Es un lenguaje de máquina para una máquina conceptual, no una máquina física real.

    Debido a esto, las instrucciones de WebAssembly a veces se denominan instrucciones virtuales. Tienen una asignación mucho más directa al código de máquina que el código fuente de JavaScript, pero no se corresponden directamente con el código de máquina particular de un hardware específico.

    El navegador descarga WebAssembly. Luego, puede realizar un breve salto desde WebAssembly al código ensamblador de esa máquina de destino.

    Para agregar WebAssembly a su página web, debe compilarlo en un archivo .wasm.

    Compilando en .wasm

    La cadena de herramientas del compilador que actualmente tiene mayor soporte para WebAssembly se llama LLVM. Hay varios front-end y back-end diferentes que se pueden conectar a LLVM.

    Nota : la mayoría de los desarrolladores de módulos WebAssembly codificarán en lenguajes como C y Rust y luego compilarán en WebAssembly, pero existen otras formas de crear un módulo WebAssembly. Por ejemplo, existe una herramienta experimental que le ayuda a crear un módulo WebAssembly utilizando TypeScript , o puede codificar en la representación de texto de WebAssembly directamente .

    Digamos que queremos pasar de C a WebAssembly. Podríamos usar la interfaz clang para pasar de C a la representación intermedia de LLVM. Una vez que está en el IR de LLVM, LLVM lo comprende, por lo que LLVM puede realizar algunas optimizaciones.

    Para pasar del IR de LLVM a WebAssembly, necesitamos un back-end. Hay uno que está actualmente en progreso en el proyecto LLVM. Ese back-end ya está casi terminado y debería finalizarse pronto. Sin embargo, puede resultar complicado conseguir que funcione hoy.

    Hay otra herramienta llamada Emscripten que es un poco más fácil de usar. Opcionalmente, también proporciona bibliotecas útiles, como un sistema de archivos respaldado por IndexDB.

     

    Independientemente de la cadena de herramientas que haya utilizado, el resultado final es un archivo que termina en .wasm. Veamos cómo puedes usarlo en tu página web.

    Cargando un módulo .wasm en JavaScript

    El archivo .wasm es el módulo WebAssembly y se puede cargar en JavaScript. A partir de este momento, el proceso de carga es un poco complicado.

    function fetchAndInstantiate(url, importObject) { return fetch(url).then(response = response.arrayBuffer() ).then(bytes = WebAssembly.instantiate(bytes, importObject) ).then(results = results.instance );}

    Puedes ver esto con más profundidad en nuestros documentos .

    Estamos trabajando para hacer este proceso más fácil. Esperamos realizar mejoras en la cadena de herramientas e integrarla con paquetes de módulos existentes como webpack o cargadores como SystemJS. Creemos que cargar módulos WebAssembly puede ser tan fácil como cargar módulos JavaScript.

    Sin embargo, existe una gran diferencia entre los módulos WebAssembly y los módulos JS. Actualmente, las funciones en WebAssembly solo pueden usar tipos de WebAssembly (enteros o números de punto flotante) como parámetros o valores de retorno.

    Para cualquier tipo de datos que sea más complejo, como cadenas, debe utilizar la memoria del módulo WebAssembly.

    Si ha trabajado principalmente con JavaScript, no le resultará familiar tener acceso directo a la memoria. Los lenguajes de mayor rendimiento, como C, C++ y Rust, tienden a tener administración de memoria manual. La memoria del módulo WebAssembly simula el montón que encontrarías en esos idiomas.

    Para hacer esto, usa algo en JavaScript llamado ArrayBuffer. El búfer de matriz es una matriz de bytes. Entonces los índices de la matriz sirven como direcciones de memoria.

    Si desea pasar una cadena entre JavaScript y WebAssembly, convierta los caracteres a su código de caracteres equivalente. Luego lo escribes en la matriz de memoria. Dado que los índices son números enteros, se puede pasar un índice a la función WebAssembly. Por tanto, el índice del primer carácter de la cadena se puede utilizar como puntero.

    Es probable que cualquiera que esté desarrollando un módulo WebAssembly para ser utilizado por desarrolladores web cree un contenedor alrededor de ese módulo. De esa manera, usted, como consumidor del módulo, no necesita saber acerca de la administración de memoria.

    Expliqué más sobre cómo trabajar con módulos WebAssembly en otro artículo.

    ¿Cuál es el estado de WebAssembly?

    El 28 de febrero, los cuatro principales navegadores anunciaron su consenso de que el MVP de WebAssembly está completo. Firefox activó la compatibilidad con WebAssembly de forma predeterminada aproximadamente una semana después de eso, y Chrome lo hizo la semana siguiente. También está disponible en versiones preliminares de Edge y Safari.

    Esto proporciona una versión inicial estable que los navegadores pueden comenzar a distribuir.

    Este núcleo no contiene todas las características que el grupo comunitario está planeando. Incluso en la versión inicial, WebAssembly será rápido. Pero debería ser aún más rápido en el futuro, mediante una combinación de correcciones y nuevas funciones. Detallo algunas de estas características en otro artículo.

    Conclusión

    Con WebAssembly, es posible ejecutar código en la web más rápido. Hay varias razones por las que el código WebAssembly se ejecuta más rápido que su equivalente JavaScript.

    • Descarga: es más compacto, por lo que la descarga puede ser más rápida
    • Análisis: decodificar WebAssembly es más rápido que analizar JavaScript
    • Compilación y optimización: lleva menos tiempo compilar y optimizar porque se han realizado más optimizaciones antes de enviar el archivo al servidor y el código debe compilarse varias veces para tipos dinámicos.
    • Reoptimización: no es necesario volver a optimizar el código porque hay suficiente información para que el compilador lo haga bien en el primer intento.
    • Ejecución: la ejecución puede ser más rápida porque las instrucciones de WebAssembly están optimizadas para la forma en que piensa la máquina.
    • Recolección de basura: WebAssembly actualmente no admite directamente la recolección de basura, por lo que no se dedica tiempo a GC

    Lo que está actualmente en los navegadores es el MVP, que ya es rápido. Será aún más rápido en los próximos años, a medida que los navegadores mejoren sus motores y se agreguen nuevas funciones a la especificación. Nadie puede decir con certeza qué tipos de aplicaciones podrían permitir estas mejoras de rendimiento. Pero si el pasado sirve de indicación, podemos esperar una sorpresa.

    Este artículo se ha vuelto a publicar en Hacks .

    Otras lecturas

    • Recuperar archivos eliminados de su árbol de trabajo de Git
    • Cómo el marketing cambió la programación orientada a objetos en JavaScript
    • SolidStart: una clase diferente de metamarco
    • Consejos y trucos útiles de DevTools

    (rb, ms, cm, il, mrn)Explora más en

    • Codificación
    • javascript
    • Navegadores





    Tal vez te puede interesar:

    1. ¿Deberían abrirse los enlaces en ventanas nuevas?
    2. 24 excelentes tutoriales de AJAX
    3. 70 técnicas nuevas y útiles de AJAX y JavaScript
    4. Más de 45 excelentes recursos y repositorios de fragmentos de código

    Una introducción resumida en dibujos animados a WebAssembly

    Una introducción resumida en dibujos animados a WebAssembly

    ¡Registro! Implemente rápidamente. Implementar inteligentemente Índice Un poco de historia del rendimie

    programar

    es

    https://aprendeprogramando.es/static/images/programar-una-introduccion-resumida-en-dibujos-animados-a-webassembly-914-0.jpg

    2024-05-20

     

    Una introducción resumida en dibujos animados a WebAssembly
    Una introducción resumida en dibujos animados a WebAssembly

    Si crees que alguno de los contenidos (texto, imagenes o multimedia) en esta página infringe tus derechos relativos a propiedad intelectual, marcas registradas o cualquier otro de tus derechos, por favor ponte en contacto con nosotros en el mail [email protected] y retiraremos este contenido inmediatamente

     

     

    Top 20