1) (data (i32.const 8) '1e000100010001e000~0l0i0b0/0r0t0/0t0l0s0f0.0t0s0') ... ;; Our global constants (not yet exported) (global $nBodyForces/FLOAT64ARRAY_ID i32 (i32.const 3)) (global $nBodyForces/G f64 (f64.const 6.674e-11)) (global $nBodyForces/bodySize i32 (i32.const 4)) (global $nBodyForces/forceSize i32 (i32.const 3)) ... ;; Memory management functions we’ll use in a minute (export 'memory' (memory

WebVR Parte 3: Desbloquear el potencial de WebAssembly y AssemblyScript

WebAssembly es definitivamente no un reemplazo de JavaScript como lengua franca de la web y del mundo.

WebAssembly (abreviado Wasm) es un formato de instrucción binaria para una máquina virtual basada en pila. Wasm está diseñado como un objetivo portátil para la compilación de lenguajes de alto nivel como C / C ++ / Rust, lo que permite la implementación en la web para aplicaciones de cliente y servidor '.WebAssembly.org



Es importante distinguir que WebAssembly no es un idioma. WebAssembly es como un archivo '.exe', o incluso mejor, un archivo '.class' de Java. El desarrollador web lo compila en otro idioma, luego lo descarga y lo ejecuta en su navegador.



WebAssembly proporciona a JavaScript todas las funciones que ocasionalmente queríamos tomar prestadas, pero nunca De Verdad quería poseer. Al igual que alquilar un barco o un caballo, WebAssembly nos permite viajar a otros idiomas sin tener que hacer elecciones extravagantes de 'estilo de vida lingüístico'. Esto ha permitido que la web se centre en cosas importantes como ofrecer funciones y mejorar la experiencia del usuario.

Más de 20 lenguajes se compilan en WebAssembly: Rust, C / C ++, C # /. Net, Java, Python, Elixir, Go y, por supuesto, JavaScript.



Si recuerdas el diagrama de arquitectura de nuestra simulación, delegamos toda la simulación a nBodySimulator, para que gestione el trabajador web.

Diagrama de arquitectura de simulación

Figura 1: Arquitectura general.

Si recuerdas del publicación de introducción , nBodySimulator tiene un step() función llamada cada 33ms. El step() La función hace estas cosas, enumeradas en el diagrama anterior:



  1. nBodySimulator's calculateForces() llamadas this.worker.postMessage() para iniciar el cálculo.
  2. workerWasm.js this.onmessage() recibe el mensaje.
  3. workerWasm.js ejecuta sincrónicamente nBodyForces.wasm nBodyForces() función.
  4. workerWasm.js responde usando this.postMessage() al hilo principal con las nuevas fuerzas.
  5. El hilo principal this.worker.onMessage() calcula los datos devueltos y las llamadas.
  6. nBodySimulator's applyForces() para actualizar las posiciones de los cuerpos.
  7. Finalmente, el visualizador vuelve a pintar.

Hilo de interfaz de usuario, hilo de trabajador web

Figura 2: Dentro de la función step () del simulador

En la publicación anterior , creamos el trabajador web que envuelve nuestros cálculos WASM. Hoy, estamos construyendo la pequeña caja etiquetada 'WASM' y moviendo datos hacia adentro y hacia afuera.

Por simplicidad, elegí AssemblyScript como lenguaje de código fuente para escribir nuestros cálculos. AssemblyScript es un subconjunto de TypeScript, que es un JavaScript escrito, por lo que ya lo conoce.

Por ejemplo, esta función de AssemblyScript calcula la gravedad entre dos cuerpos: :f64 en someVar:f64 marca la variable someVar como flotante para el compilador. Recuerde que este código se compila y ejecuta en un tiempo de ejecución completamente diferente al de JavaScript.

|_+_|

Esta función de AssemblyScript toma (x, y, z, mass) para dos cuerpos y devuelve una matriz de tres flotantes que describen el vector de fuerza (x, y, z) que los cuerpos se aplican entre sí. No podemos llamar a esta función desde JavaScript porque JavaScript no tiene idea de dónde encontrarla. Tenemos que 'exportarlo' a JavaScript. Esto nos lleva a nuestro primer desafío técnico.

Importaciones y exportaciones de WebAssembly

En ES6, pensamos en las importaciones y exportaciones en código JavaScript y usamos herramientas como Rollup o Webpack para crear código que se ejecuta en navegadores heredados para manejar // AssemblyScript - a TypeScript-like language that compiles to WebAssembly // src/assembly/nBodyForces.ts /** * Given two bodies, calculate the Force of Gravity, * then return as a 3-force vector (x, y, z) * * Sometimes, the force of gravity is: * * Fg = G * mA * mB / r^2 * * Given: * - Fg = Force of gravity * - r = sqrt ( dx + dy + dz) = straight line distance between 3d objects * - G = gravitational constant * - mA, mB = mass of objects * * Today, we're using better-gravity because better-gravity can calculate * force vectors without polar math (sin, cos, tan) * * Fbg = G * mA * mB * dr / r^3 // using dr as a 3-distance vector lets * // us project Fbg as a 3-force vector * * Given: * - Fbg = Force of better gravity * - dr = (dx, dy, dz) // a 3-distance vector * - dx = bodyB.x - bodyA.x * * Force of Better-Gravity: * * - Fbg = (Fx, Fy, Fz) = the change in force applied by gravity each * body's (x,y,z) over this time period * - Fbg = G * mA * mB * dr / r^3 * - dr = (dx, dy, dz) * - Fx = Gmm * dx / r3 * - Fy = Gmm * dy / r3 * - Fz = Gmm * dz / r3 * * From the parameters, return an array [fx, fy, fz] */ function twoBodyForces(xA: f64, yA: f64, zA: f64, mA: f64, xB: f64, yB: f64, zB: f64, mB: f64): f64[] y import. Esto crea un árbol de dependencia de arriba hacia abajo y habilita tecnología genial como ' temblor de árboles 'Y división de código .

En WebAssembly, las importaciones y exportaciones realizan tareas diferentes a las de una importación de ES6. Importaciones / exportaciones de WebAssembly:

En el código siguiente, abort() y env.abort son parte del entorno que debemos proporcionar al módulo WebAssembly. El env.trace y las funciones de amigos proporcionan mensajes de depuración a la consola. Tenga en cuenta que pasar cadenas dentro / fuera de WebAssembly no es trivial, ya que los únicos tipos de WebAssembly son números i32, i64, f32, f64, con referencias i32 a una memoria lineal abstracta.

Nota: Estos ejemplos de código cambian entre código JavaScript (el trabajador web) y AssemblyScript (el código WASM).

|_+_|

En nuestro código AssemblyScript, podemos completar la importación de estas funciones así:

|_+_|

Nota : La cancelación y el seguimiento se importan automáticamente .

Desde AssemblyScript, podemos exportar nuestra interfaz. A continuación, se muestran algunas constantes exportadas:

|_+_|

Y aquí está la exportación de nBodyForces.logI que llamaremos desde JavaScript. Exportamos el tipo // Web Worker JavaScript in workerWasm.js /** * When we instantiate the Wasm module, give it a context to work in: * nBodyForces: {} is a table of functions we can import into AssemblyScript. See top of nBodyForces.ts * env: {} describes the environment sent to the Wasm module as it's instantiated */ const importObj = { nBodyForces: { logI(data) { console.log('Log() - ' + data); }, logF(data) { console.log('Log() - ' + data); }, }, env: { abort(msg, file, line, column) { // wasm.__getString() is added by assemblyscript's loader: // https://github.com/AssemblyScript/assemblyscript/tree/master/lib/loader console.error('abort: (' + wasm.__getString(msg) + ') at ' + wasm.__getString(file) + ':' + line + ':' + column); }, trace(msg, n) { console.log('trace: ' + wasm.__getString(msg) + (n ? ' ' : '') + Array.prototype.slice.call(arguments, 2, 2 + n).join(', ')); } } } en la parte superior del archivo para que podamos usar el cargador de JavaScript de AssemblyScript en nuestro trabajador web para obtener los datos (ver a continuación):

|_+_|

Artefactos de WebAssembly: .wasm y .wat

Cuando nuestro AssemblyScript // nBodyForces.ts declare function logI(data: i32): void declare function logF(data: f64): void se compila en un WebAssembly // src/assembly/nBodyForces.ts // Gravitational constant. Any G could be used in a game. // This value is best for a scientific simulation. export const G: f64 = 6.674e-11; // for sizing and indexing arrays export const bodySize: i32 = 4 export const forceSize: i32 = 3 binario , hay una opción para crear también una versión de 'texto' que describa las instrucciones en el binario.

Artefactos de WebAssembly

Figura 3: Recuerde, AssemblyScript es un lenguaje. WebAssembly es un compilador y un tiempo de ejecución.

Dentro de nBodyForces() archivo, podemos ver estas importaciones y exportaciones:

|_+_|

Ahora tenemos nuestro Float64Array binary y un trabajador web para ejecutarlo. ¡Prepárate para el despegue! ¡Y algo de gestión de la memoria!

Para completar la integración, tenemos que pasar una matriz variable de flotantes a WebAssembly y devolver una matriz variable de flotantes a JavaScript.

Con JavaScript ingenuo y burgués, me propuse pasar estas matrices llamativas de tamaño variable dentro y fuera de un tiempo de ejecución multiplataforma de alto rendimiento. Pasar datos a / desde WebAssembly fue, con mucho, la dificultad más inesperada en este proyecto.

Sin embargo, con muchas gracias por la trabajo pesado realizado por el equipo de AssemblyScript , podemos usar su 'cargador' para ayudar:

|_+_|

El // src/assembly/nBodyForces.ts export const FLOAT64ARRAY_ID = idof(); ... /** * Given N bodies with mass, in a 3d space, calculate the forces of gravity to be applied to each body. * * This function is exported to JavaScript, so only takes/returns numbers and arrays. * For N bodies, pass and array of 4N values (x,y,z,mass) and expect a 3N array of forces (x,y,z) * Those forces can be applied to the bodies mass to update its position in the simulation. * Calculate the 3-vector each unique pair of bodies applies to each other. * * 0 1 2 3 4 5 * 0 x x x x x * 1 x x x x * 2 x x x * 3 x x * 4 x * 5 * * Sum those forces together into an array of 3-vector x,y,z forces * * Return 0 on success */ export function nBodyForces(arrBodies: Float64Array): Float64Array { // Check inputs const numBodies: i32 = arrBodies.length / bodySize if (arrBodies.length % bodySize !== 0) trace('INVALID nBodyForces parameter. Chaos ensues...') // Create result array. This should be garbage collected later. let arrForces: Float64Array = new Float64Array(numBodies * forceSize) // For all bodies: for (let i: i32 = 0; i i for (let j: i32 = i + 1; j significa que necesitamos usar un paquete de módulos como Rollup o Webpack. Para este proyecto, elegí Rollup por su simplicidad y flexibilidad y nunca miré hacia atrás.

Recuerde que nuestro trabajador web se ejecuta en un hilo separado y es esencialmente un nBodyForces.ts función con un nBodyForces.wasm declaración.

nBodyForces.wat crea nuestro módulo wasm con algunas funciones de gestión de memoria muy útiles. ;; This is a comment in nBodyForces.wat (module ;; compiler defined types (type $FUNCSIG$iii (func (param i32 i32) (result i32))) … ;; Expected imports from JavaScript (import 'env' 'abort' (func $~lib/builtins/abort (param i32 i32 i32 i32))) (import 'env' 'trace' (func $~lib/builtins/trace (param i32 i32 f64 f64 f64 f64 f64))) ;; Memory section defining data constants like strings (memory $0 1) (data (i32.const 8) '1e000100010001e000~0l0i0b0/0r0t0/0t0l0s0f0.0t0s0') ... ;; Our global constants (not yet exported) (global $nBodyForces/FLOAT64ARRAY_ID i32 (i32.const 3)) (global $nBodyForces/G f64 (f64.const 6.674e-11)) (global $nBodyForces/bodySize i32 (i32.const 4)) (global $nBodyForces/forceSize i32 (i32.const 3)) ... ;; Memory management functions we’ll use in a minute (export 'memory' (memory $0)) (export '__alloc' (func $~lib/rt/tlsf/__alloc)) (export '__retain' (func $~lib/rt/pure/__retain)) (export '__release' (func $~lib/rt/pure/__release)) (export '__collect' (func $~lib/rt/pure/__collect)) (export '__rtti_base' (global $~lib/rt/__rtti_base)) ;; Finally our exported constants and function (export 'FLOAT64ARRAY_ID' (global $nBodyForces/FLOAT64ARRAY_ID)) (export 'G' (global $nBodyForces/G)) (export 'bodySize' (global $nBodyForces/bodySize)) (export 'forceSize' (global $nBodyForces/forceSize)) (export 'nBodyForces' (func $nBodyForces/nBodyForces)) ;; Implementation details ... y nBodyForces.wasm administrar referencias de recolección de basura en el tiempo de ejecución del trabajador // workerWasm.js - our web worker /** * AssemblyScript loader adds helpers for moving data to/from AssemblyScript. * Highly recommended */ const loader = require('assemblyscript/lib/loader') copia nuestra matriz de parámetros en la memoria del módulo wasm require() copia la matriz de resultados del módulo wasm en el tiempo de ejecución del trabajador

Ahora podemos clasificar matrices flotantes dentro y fuera de onmessage() y completa nuestra simulación:

|_+_|

Con todo lo que hemos aprendido, repasemos nuestro proceso de trabajador web y WebAssembly. Bienvenido al nuevo navegador de fondo de la web. Estos son enlaces al código en GitHub:

  1. GET Index.html
  2. main.js
  3. nBodySimulator.js - pasa un mensaje a su trabajador web
  4. workerWasm.js - llama a la función WebAssembly
  5. nBodyForces.ts - calcula y devuelve una serie de fuerzas
  6. workerWasm.js - devuelve los resultados al hilo principal
  7. nBodySimulator.js - resuelve la promesa de fuerzas
  8. nBodySimulator.js - luego aplica las fuerzas a los cuerpos y les dice a los visualizadores que pinten

Desde aquí, ¡comencemos el espectáculo creando switch()! Nuestra próxima publicación crea un visualizador usando Canvas API, y la publicación final concluye con WebVR y Aframe.

Relacionado: Tutorial de WebAssembly / Rust: procesamiento de audio perfecto

Comprender los conceptos básicos

¿Puede WebAssembly reemplazar JavaScript?

WebAssembly no es un idioma, por lo que no puede reemplazar a JavaScript. Además, el desarrollo de funciones y la experiencia del usuario en WebAssembly es menos eficiente.

¿Por qué WebAssembly es más rápido?

WebAssembly es más rápido porque hace menos y fue diseñado para el rendimiento en lugar de la usabilidad del desarrollador.

¿Se puede compilar JavaScript en WebAssembly?

Sí, AssemblyScript se compila en WebAssembly y se siente como Typecript.

)) (export '__alloc' (func $~lib/rt/tlsf/__alloc)) (export '__retain' (func $~lib/rt/pure/__retain)) (export '__release' (func $~lib/rt/pure/__release)) (export '__collect' (func $~lib/rt/pure/__collect)) (export '__rtti_base' (global $~lib/rt/__rtti_base)) ;; Finally our exported constants and function (export 'FLOAT64ARRAY_ID' (global $nBodyForces/FLOAT64ARRAY_ID)) (export 'G' (global $nBodyForces/G)) (export 'bodySize' (global $nBodyForces/bodySize)) (export 'forceSize' (global $nBodyForces/forceSize)) (export 'nBodyForces' (func $nBodyForces/nBodyForces)) ;; Implementation details ...
y nBodyForces.wasm administrar referencias de recolección de basura en el tiempo de ejecución del trabajador // workerWasm.js - our web worker /** * AssemblyScript loader adds helpers for moving data to/from AssemblyScript. * Highly recommended */ const loader = require('assemblyscript/lib/loader') copia nuestra matriz de parámetros en la memoria del módulo wasm require() copia la matriz de resultados del módulo wasm en el tiempo de ejecución del trabajador

Ahora podemos clasificar matrices flotantes dentro y fuera de onmessage() y completa nuestra simulación:

|_+_|

Con todo lo que hemos aprendido, repasemos nuestro proceso de trabajador web y WebAssembly. Bienvenido al nuevo navegador de fondo de la web. Estos son enlaces al código en GitHub:

  1. GET Index.html
  2. main.js
  3. nBodySimulator.js - pasa un mensaje a su trabajador web
  4. workerWasm.js - llama a la función WebAssembly
  5. nBodyForces.ts - calcula y devuelve una serie de fuerzas
  6. workerWasm.js - devuelve los resultados al hilo principal
  7. nBodySimulator.js - resuelve la promesa de fuerzas
  8. nBodySimulator.js - luego aplica las fuerzas a los cuerpos y les dice a los visualizadores que pinten

Desde aquí, ¡comencemos el espectáculo creando switch()! Nuestra próxima publicación crea un visualizador usando Canvas API, y la publicación final concluye con WebVR y Aframe.

cómo usar el flujo tensorial
Relacionado: Tutorial de WebAssembly / Rust: procesamiento de audio perfecto

Comprender los conceptos básicos

¿Puede WebAssembly reemplazar JavaScript?

WebAssembly no es un idioma, por lo que no puede reemplazar a JavaScript. Además, el desarrollo de funciones y la experiencia del usuario en WebAssembly es menos eficiente.

¿Por qué WebAssembly es más rápido?

WebAssembly es más rápido porque hace menos y fue diseñado para el rendimiento en lugar de la usabilidad del desarrollador.

¿Se puede compilar JavaScript en WebAssembly?

Sí, AssemblyScript se compila en WebAssembly y se siente como Typecript.