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 Osa 3: WebAssembly- ja AssemblyScript-potentiaalin avaaminen



WebAssembly on ehdottomasti ei korvaava JavaScripti, joka on verkon ja maailman kieli.

WebAssembly (lyhennetty Wasm) on pinopohjaisen virtuaalikoneen binaarinen käskymuoto. Wasm on suunniteltu kannettavaksi kohteeksi korkean tason kielten, kuten C / C ++ / Rust, kokoamiseen, mikä mahdollistaa käyttöönoton verkossa asiakas- ja palvelinsovelluksille. ' - WebAssembly.org



On tärkeää erottaa, että WebAssembly ei ole kieli. WebAssembly on kuin .exe - tai jopa parempi - Java .class-tiedosto. Verkkokehittäjä on koonnut sen toiselta kieleltä, sitten ladattu ja suoritettu selaimellasi.



WebAssembly antaa JavaScriptille kaikki ominaisuudet, joita toisinaan halusimme lainata, mutta emme koskaan Todella halusi omistaa. Aivan kuten veneen tai hevosen vuokraaminen, WebAssembly antaa meille mahdollisuuden matkustaa muille kielille tarvitsematta tehdä ylellisiä 'kielen elämäntapa' -valintoja. Tämä on antanut verkon keskittyä tärkeisiin asioihin, kuten ominaisuuksien toimittamiseen ja käyttökokemuksen parantamiseen.



Yli 20 kieltä kääntyy WebAssemble: Rust, C / C ++, C # /. Net, Java, Python, Elixir, Go ja tietysti JavaScript.

Jos muistat simulaation arkkitehtuurikaavion, delegoimme koko simulaation tilalle nBodySimulator, joten se hallinnoi verkkotyöntekijää.



Simulaation arkkitehtuurikaavio

Kuva 1: Kokonaisarkkitehtuuri.

Jos muistat intro post , nBodySimulator on step() toiminto kutsutaan 33 ms välein. step() function tekee nämä asiat - numeroitu yllä olevaan kaavioon:



  1. nBodySimulator's calculateForces() puhelut this.worker.postMessage() laskennan aloittamiseksi.
  2. workerWasm.js this.onmessage() saa viestin.
  3. workerWasm.js suorittaa synkronisesti nBodyForces.wasm's nBodyForces() toiminto.
  4. workerWasm.js vastaa this.postMessage() uusien voimien kanssa päälangalle.
  5. Päälangan this.worker.onMessage() edustajat palauttamat tiedot ja puhelut.
  6. nBodySimulator's applyForces() elinten sijaintien päivittämiseksi.
  7. Lopuksi visualisoija maalaa uudelleen.

Käyttöliittymälanka, verkkotyöläislanka

Kuva 2: Simulaattorin step () -toiminnon sisällä

Sisään edellinen viesti , rakensimme verkkotyöntekijän, joka käärii WASM-laskelmamme. Tänään rakennamme pienen laatikon nimeltä WASM ja siirrämme dataa sisään ja ulos.



Yksinkertaisuuden vuoksi valitsin AssemblyScript lähdekoodikielenä laskelmien laskemiseen. AssemblyScript on TypeScriptin osajoukko - joka on kirjoitettu JavaScript - joten tiedät sen jo.

Esimerkiksi tämä AssemblyScript-toiminto laskee painovoiman kahden kappaleen välillä: :f64 sisään someVar:f64 merkitsee muuttujan someVar kääntäjän kellukkeeksi. Muista, että tämä koodi on käännetty ja suoritettu täysin eri ajonaikana kuin JavaScript.



// 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[]

Tämä AssemblyScript-funktio vie (x, y, z, massa) kahdelle kappaleelle ja palauttaa kolmen kelluvan matriisin, joka kuvaa (x, y, z) voimavektoria, jota kappaleet käyttävät toisiinsa. Emme voi kutsua tätä toimintoa JavaScriptista, koska JavaScriptillä ei ole aavistustakaan, mistä se löytyy. Meidän on 'vietävä' se JavaScriptiin. Tämä tuo meidät ensimmäiseen tekniseen haasteeseemme.

WebAssembly-tuonti ja vienti

ES6: ssa ajattelemme tuontia ja vientiä JavaScript-koodissa ja käytämme työkaluja, kuten Rollup tai Webpack, koodin luomiseen, joka toimii vanhoissa selaimissa import ja require(). Tämä luo ylhäältä alas riippuvuuspuun ja mahdollistaa hienon tekniikan, kuten puiden ravistelu ”Ja koodin jakaminen .



WebAss kokoonpanossa tuonti ja vienti suorittavat erilaisia ​​tehtäviä kuin ES6-tuonti. WebAss Assemblyn tuonti / vienti:

Alla olevassa koodissa env.abort ja env.trace ovat osa ympäristöä, jonka meidän on tarjottava WebAssembly-moduulille. nBodyForces.logI ja ystävät -toiminnot tarjoavat virheenkorjausviestejä konsolille. Huomaa, että merkkijonojen siirtäminen sisään / ulos WebAssemble-sivustosta ei ole triviaalia, koska WebAssemble-ohjelmiston ainoat tyypit ovat i32, i64, f32, f64 ja i32-viitteet abstraktiin lineaariseen muistiin.

Huomautus: Nämä koodiesimerkit vaihtavat edestakaisin JavaScript-koodin (verkkotyöntekijä) ja AssemblyScriptin (WASM-koodi) välillä.

// 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(', ')); } } }

AssemblyScript-koodissamme voimme suorittaa näiden toimintojen tuonnin loppuun:

// nBodyForces.ts declare function logI(data: i32): void declare function logF(data: f64): void

Huomautus : Keskeytys ja jäljitys tuodaan automaattisesti .

AssemblyScriptistä voimme viedä käyttöliittymämme. Tässä on joitain vietyjä vakioita:

// 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

Ja tässä on nBodyForces(): n vienti jota kutsumme JavaScriptistä. Viemme tyypin Float64Array tiedoston yläosassa, jotta voimme käyttää AssemblyScriptin JavaScript-latainta web-työntekijässämme tietojen saamiseksi (katso alla):

// 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

WebAssembly-artefaktit: .wasm ja .wat

Kun AssemblyScript nBodyForces.ts on koottu WebAss kokoonpanoon nBodyForces.wasm binääri , on mahdollisuus luoda myös 'tekstiversio', joka kuvaa binäärisen ohjeen.

WebAssembly-artefaktit

Kuva 3: Muista, että AssemblyScript on kieli. WebAssembly on kääntäjä ja ajonaikainen.

nBodyForces.wat: N sisällä tiedosto, voimme nähdä nämä tuonti ja vienti:

;; 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 ...

Meillä on nyt nBodyForces.wasm binaarinen ja verkkotyöntekijä sen suorittamiseksi. Valmistaudu blastoffiin! Ja vähän muistinhallintaa!

Integraation loppuun saattamiseksi meidän on siirrettävä muuttuja kelluvia taulukoita WebAssemblylle ja palautettava vaihtuva joukko kellukkeita JavaScriptiin.

Naiivin JavaScript-porvarillisen avulla aloitin haluttomasti välittää nämä röyhkeät vaihtelevan kokoiset ryhmät alustojen väliseen korkean suorituskyvyn ajonaikaan. Tietojen siirtäminen WebAssemble-palveluun / siitä oli ylivoimaisesti odottamattomin vaikeus tässä projektissa.

Paljon kiitoksia AssemblyScript-tiimin tekemä raskas nosto , voimme käyttää heidän ”kuormaajaansa” auttamaan:

// workerWasm.js - our web worker /** * AssemblyScript loader adds helpers for moving data to/from AssemblyScript. * Highly recommended */ const loader = require('assemblyscript/lib/loader')

require() tarkoittaa, että meidän on käytettävä moduulipakettia, kuten Rollup tai Webpack. Tässä projektissa valitsin Rollupin sen yksinkertaisuuden ja joustavuuden vuoksi, enkä koskaan katsonut taaksepäin.

Muista, että verkkotyöntekijämme toimii erillisessä säikeessä ja on olennaisesti onmessage() toiminto switch() lausunto.

loader luo wasm-moduulimme, jossa on joitakin käteviä muistinhallintatoimintoja. __retain() ja __release() hallita roskakorin viitteitä työntekijän ajon aikana __allocArray() kopioi parametriryhmämme wasm-moduulin muistiin __getFloat64Array() kopioi tulostaulukon wasm-moduulista työntekijän ajonaikaan

Voimme nyt marsalkki kellua taulukot sisään ja ulos nBodyForces() ja suorita simulointimme loppuun:

// workerWasm.js /** * Web workers listen for messages from the main thread. */ this.onmessage = function (evt) { // message from UI thread var msg = evt.data switch (msg.purpose) { // Message: Load new wasm module case 'wasmModule': // Instantiate the compiled module we were passed. wasm = loader.instantiate(msg.wasmModule, importObj) // Throws // Tell nBodySimulation.js we are ready this.postMessage({ purpose: 'wasmReady' }) return // Message: Given array of floats describing a system of bodies (x,y,x,mass), // calculate the Grav forces to be applied to each body case 'nBodyForces': if (!wasm) throw new Error('wasm not initialized') // Copy msg.arrBodies array into the wasm instance, increase GC count const dataRef = wasm.__retain(wasm.__allocArray(wasm.FLOAT64ARRAY_ID, msg.arrBodies)); // Do the calculations in this thread synchronously const resultRef = wasm.nBodyForces(dataRef); // Copy result array from the wasm instance to our javascript runtime const arrForces = wasm.__getFloat64Array(resultRef); // Decrease the GC count on dataRef from __retain() here, // and GC count from new Float64Array in wasm module wasm.__release(dataRef); wasm.__release(resultRef); // Message results back to main thread. // see nBodySimulation.js this.worker.onmessage return this.postMessage({ purpose: 'nBodyForces', arrForces }) } }

Kaiken oppimamme avulla tarkistetaan verkkotyöntekijämme ja WebAssembly-matkamme. Tervetuloa uuteen verkkoselaimeen. Nämä ovat linkkejä GitHubin koodiin:

  1. GET Index.html
  2. main.js
  3. nBodySimulator.js - välittää viestin verkkotyöntekijälle
  4. workerWasm.js - kutsuu WebAssembly-toiminnon
  5. nBodyForces.ts - laskee ja palauttaa joukon voimia
  6. workerWasm.js - siirtää tulokset takaisin päälangalle
  7. nBodySimulator.js - ratkaisee joukkojen lupauksen
  8. nBodySimulator.js - kohdistaa sitten voimat kappaleisiin ja käskee visualisoijien maalata

Aloitetaan tästä luomalla nBodyVisualizer.js! Seuraava viesti luo visualisoijan käyttämällä Canvas-sovellusliittymää, ja viimeinen viesti kiteytyy WebVR: n ja Aframe: n kanssa.

Liittyvät: WebAssembly / Rust Tutorial: Pitch-perfect Audio Processing

Perustietojen ymmärtäminen

Voiko WebAssembly korvata JavaScriptin?

WebAssembly ei ole kieli, joten se ei voi korvata JavaScriptiä. Ominaisuuksien ja käyttökokemuksen kehittäminen WebAssembly-sovelluksessa on myös vähemmän tehokasta.

Miksi WebAssembly on nopeampi?

WebAssembly on nopeampi, koska se tekee vähemmän ja on suunniteltu suorituskykyyn kehittäjien käytettävyyden sijaan.

Voidaanko JavaScript kääntää WebAssembly-palveluun?

Kyllä, AssemblyScript kääntyy WebAss kokoonpanoon ja tuntuu kuin kirjoituskirja.

)) (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 ...

Meillä on nyt nBodyForces.wasm binaarinen ja verkkotyöntekijä sen suorittamiseksi. Valmistaudu blastoffiin! Ja vähän muistinhallintaa!

Integraation loppuun saattamiseksi meidän on siirrettävä muuttuja kelluvia taulukoita WebAssemblylle ja palautettava vaihtuva joukko kellukkeita JavaScriptiin.

Naiivin JavaScript-porvarillisen avulla aloitin haluttomasti välittää nämä röyhkeät vaihtelevan kokoiset ryhmät alustojen väliseen korkean suorituskyvyn ajonaikaan. Tietojen siirtäminen WebAssemble-palveluun / siitä oli ylivoimaisesti odottamattomin vaikeus tässä projektissa.

Paljon kiitoksia AssemblyScript-tiimin tekemä raskas nosto , voimme käyttää heidän ”kuormaajaansa” auttamaan:

// workerWasm.js - our web worker /** * AssemblyScript loader adds helpers for moving data to/from AssemblyScript. * Highly recommended */ const loader = require('assemblyscript/lib/loader')

require() tarkoittaa, että meidän on käytettävä moduulipakettia, kuten Rollup tai Webpack. Tässä projektissa valitsin Rollupin sen yksinkertaisuuden ja joustavuuden vuoksi, enkä koskaan katsonut taaksepäin.

Muista, että verkkotyöntekijämme toimii erillisessä säikeessä ja on olennaisesti onmessage() toiminto switch() lausunto.

loader luo wasm-moduulimme, jossa on joitakin käteviä muistinhallintatoimintoja. __retain() ja __release() hallita roskakorin viitteitä työntekijän ajon aikana __allocArray() kopioi parametriryhmämme wasm-moduulin muistiin __getFloat64Array() kopioi tulostaulukon wasm-moduulista työntekijän ajonaikaan

Voimme nyt marsalkki kellua taulukot sisään ja ulos nBodyForces() ja suorita simulointimme loppuun:

mikä on palvelinpuolen renderöinti
// workerWasm.js /** * Web workers listen for messages from the main thread. */ this.onmessage = function (evt) { // message from UI thread var msg = evt.data switch (msg.purpose) { // Message: Load new wasm module case 'wasmModule': // Instantiate the compiled module we were passed. wasm = loader.instantiate(msg.wasmModule, importObj) // Throws // Tell nBodySimulation.js we are ready this.postMessage({ purpose: 'wasmReady' }) return // Message: Given array of floats describing a system of bodies (x,y,x,mass), // calculate the Grav forces to be applied to each body case 'nBodyForces': if (!wasm) throw new Error('wasm not initialized') // Copy msg.arrBodies array into the wasm instance, increase GC count const dataRef = wasm.__retain(wasm.__allocArray(wasm.FLOAT64ARRAY_ID, msg.arrBodies)); // Do the calculations in this thread synchronously const resultRef = wasm.nBodyForces(dataRef); // Copy result array from the wasm instance to our javascript runtime const arrForces = wasm.__getFloat64Array(resultRef); // Decrease the GC count on dataRef from __retain() here, // and GC count from new Float64Array in wasm module wasm.__release(dataRef); wasm.__release(resultRef); // Message results back to main thread. // see nBodySimulation.js this.worker.onmessage return this.postMessage({ purpose: 'nBodyForces', arrForces }) } }

Kaiken oppimamme avulla tarkistetaan verkkotyöntekijämme ja WebAssembly-matkamme. Tervetuloa uuteen verkkoselaimeen. Nämä ovat linkkejä GitHubin koodiin:

  1. GET Index.html
  2. main.js
  3. nBodySimulator.js - välittää viestin verkkotyöntekijälle
  4. workerWasm.js - kutsuu WebAssembly-toiminnon
  5. nBodyForces.ts - laskee ja palauttaa joukon voimia
  6. workerWasm.js - siirtää tulokset takaisin päälangalle
  7. nBodySimulator.js - ratkaisee joukkojen lupauksen
  8. nBodySimulator.js - kohdistaa sitten voimat kappaleisiin ja käskee visualisoijien maalata

Aloitetaan tästä luomalla nBodyVisualizer.js! Seuraava viesti luo visualisoijan käyttämällä Canvas-sovellusliittymää, ja viimeinen viesti kiteytyy WebVR: n ja Aframe: n kanssa.

Liittyvät: WebAssembly / Rust Tutorial: Pitch-perfect Audio Processing

Perustietojen ymmärtäminen

Voiko WebAssembly korvata JavaScriptin?

WebAssembly ei ole kieli, joten se ei voi korvata JavaScriptiä. Ominaisuuksien ja käyttökokemuksen kehittäminen WebAssembly-sovelluksessa on myös vähemmän tehokasta.

Miksi WebAssembly on nopeampi?

WebAssembly on nopeampi, koska se tekee vähemmän ja on suunniteltu suorituskykyyn kehittäjien käytettävyyden sijaan.

Voidaanko JavaScript kääntää WebAssembly-palveluun?

Kyllä, AssemblyScript kääntyy WebAss kokoonpanoon ja tuntuu kuin kirjoituskirja.