WebAssembly определено е не заместител на JavaScript като lingua franca на мрежата и света.
WebAssembly (съкратено Wasm) е двоичен формат на инструкции за базирана на стека виртуална машина. Wasm е проектиран като преносима цел за компилация на езици на високо ниво като C / C ++ / Rust, позволявайки разполагане в мрежата за клиентски и сървърни приложения. ' - WebAssembly.org
Важно е да се разграничи, че WebAssembly не е език. WebAssembly е като „.exe“ - или дори по-добре - Java „.class“ файл. Той се компилира от уеб разработчика от друг език, след което се изтегля и стартира във вашия браузър.
WebAssembly предоставя на JavaScript всички функции, които от време на време искахме да заемем, но никога наистина ли исках да притежавам. Подобно на наемането на лодка или кон, WebAssembly ни позволява да пътуваме до други езици, без да се налага да правим екстравагантен избор на „езиков начин на живот“. Това позволи на мрежата да се съсредоточи върху важни неща като предоставяне на функции и подобряване на потребителското изживяване.
Повече от 20 езика се компилират в WebAssembly: Rust, C / C ++, C # /. Net, Java, Python, Elixir, Go и разбира се JavaScript.
Ако си спомняте архитектурната диаграма на нашата симулация, ние делегирахме цялата симулация на nBodySimulator
, така че тя управлява уеб работника.
Ако си спомняте от интро публикация , nBodySimulator
има step()
функция, извикана на всеки 33ms. step()
функция прави тези неща - номерирани в диаграмата по-горе:
calculateForces()
обаждания this.worker.postMessage()
за да започне изчислението.this.onmessage()
получава съобщението.nBodyForces()
функция.this.postMessage()
към основната нишка с новите сили.this.worker.onMessage()
маршали на върнатите данни и обаждания.applyForces()
за актуализиране на позициите на телата.
В предишната публикация , ние изградихме уеб работника, който обгръща нашите WASM изчисления. Днес изграждаме малката кутия с надпис „WASM“ и преместваме данни навътре и навън.
За простота избрах AssemblyScript като език на изходния код, за да напишем нашите изчисления. AssemblyScript е подмножество на TypeScript - което е типизиран JavaScript - така че вече го знаете.
Например тази функция AssemblyScript изчислява гравитацията между две тела: :f64
в someVar:f64
маркира променливата someVar като плувка за компилатора. Не забравяйте, че този код се компилира и изпълнява в съвсем различно време на изпълнение от 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[]
Тази функция AssemblyScript взема (x, y, z, маса) за две тела и връща масив от три поплавъка, описващи (x, y, z) вектор на сила, който телата прилагат един към друг. Не можем да извикаме тази функция от JavaScript, защото JavaScript няма идея къде да я намери. Трябва да го „експортираме“ в JavaScript. Това ни води до първото ни техническо предизвикателство.
В ES6 мислим за импортиране и експортиране в JavaScript код и използваме инструменти като Rollup или Webpack, за да създадем код, който да работи в стари браузъри за обработка import
и require()
. Това създава дърво на зависимостите отгоре надолу и дава възможност за готини технологии като „ разклащане на дървета ' и кодово разделяне .
В WebAssembly вносът и износът изпълняват различни задачи, отколкото импортирането на ES6. Внос / износ на WebAssembly:
trace()
И abort()
функции).В кода по-долу, env.abort
и env.trace
са част от средата, която трябва да предоставим на модула WebAssembly. nBodyForces.logI
и приятели функции предоставят съобщения за отстраняване на грешки в конзолата. Имайте предвид, че предаването на низове в / извън WebAssembly не е тривиално, тъй като единствените типове WebAssembly са числата i32, i64, f32, f64, с i32 препратки към абстрактна линейна памет.
Забележка: Тези примери за код превключват напред-назад между JavaScript кода (уеб работника) и AssemblyScript (кода WASM).
// 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 можем да завършим импортирането на тези функции по следния начин:
// nBodyForces.ts declare function logI(data: i32): void declare function logF(data: f64): void
Забележка : Прекъсване и проследяване се импортират автоматично .
От AssemblyScript можем да експортираме нашия интерфейс. Ето някои експортирани константи:
// 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
И тук е износът на nBodyForces()
които ще извикаме от JavaScript. Експортираме типа Float64Array
в горната част на файла, за да можем да използваме JavaScript loader на AssemblyScript в нашия уеб работник, за да получим данните (вижте по-долу):
// 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: .wasm и .wat
Когато нашият AssemblyScript nBodyForces.ts
се компилира в WebAssembly nBodyForces.wasm
двоичен , има опция за създаване и на „текстова“ версия, описваща инструкциите в двоичния файл.

Фигура 3: Не забравяйте, че AssemblyScript е език. WebAssembly е компилатор и време за изпълнение.
Вътре в nBodyForces.wat
файл, можем да видим тези внос и износ:
;; 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 WebVR Част 3: Отключване на потенциала на WebAssembly и AssemblyScript
WebAssembly определено е не заместител на JavaScript като lingua franca на мрежата и света.
WebAssembly (съкратено Wasm) е двоичен формат на инструкции за базирана на стека виртуална машина. Wasm е проектиран като преносима цел за компилация на езици на високо ниво като C / C ++ / Rust, позволявайки разполагане в мрежата за клиентски и сървърни приложения. ' - WebAssembly.org
Важно е да се разграничи, че WebAssembly не е език. WebAssembly е като „.exe“ - или дори по-добре - Java „.class“ файл. Той се компилира от уеб разработчика от друг език, след което се изтегля и стартира във вашия браузър.
WebAssembly предоставя на JavaScript всички функции, които от време на време искахме да заемем, но никога наистина ли исках да притежавам. Подобно на наемането на лодка или кон, WebAssembly ни позволява да пътуваме до други езици, без да се налага да правим екстравагантен избор на „езиков начин на живот“. Това позволи на мрежата да се съсредоточи върху важни неща като предоставяне на функции и подобряване на потребителското изживяване.
Повече от 20 езика се компилират в WebAssembly: Rust, C / C ++, C # /. Net, Java, Python, Elixir, Go и разбира се JavaScript.
Ако си спомняте архитектурната диаграма на нашата симулация, ние делегирахме цялата симулация на nBodySimulator
, така че тя управлява уеб работника.
Ако си спомняте от интро публикация , nBodySimulator
има step()
функция, извикана на всеки 33ms. step()
функция прави тези неща - номерирани в диаграмата по-горе:
calculateForces()
обаждания this.worker.postMessage()
за да започне изчислението.this.onmessage()
получава съобщението.nBodyForces()
функция.this.postMessage()
към основната нишка с новите сили.this.worker.onMessage()
маршали на върнатите данни и обаждания.applyForces()
за актуализиране на позициите на телата.
В предишната публикация , ние изградихме уеб работника, който обгръща нашите WASM изчисления. Днес изграждаме малката кутия с надпис „WASM“ и преместваме данни навътре и навън.
За простота избрах AssemblyScript като език на изходния код, за да напишем нашите изчисления. AssemblyScript е подмножество на TypeScript - което е типизиран JavaScript - така че вече го знаете.
Например тази функция AssemblyScript изчислява гравитацията между две тела: :f64
в someVar:f64
маркира променливата someVar като плувка за компилатора. Не забравяйте, че този код се компилира и изпълнява в съвсем различно време на изпълнение от 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[]
Тази функция AssemblyScript взема (x, y, z, маса) за две тела и връща масив от три поплавъка, описващи (x, y, z) вектор на сила, който телата прилагат един към друг. Не можем да извикаме тази функция от JavaScript, защото JavaScript няма идея къде да я намери. Трябва да го „експортираме“ в JavaScript. Това ни води до първото ни техническо предизвикателство.
В ES6 мислим за импортиране и експортиране в JavaScript код и използваме инструменти като Rollup или Webpack, за да създадем код, който да работи в стари браузъри за обработка import
и require()
. Това създава дърво на зависимостите отгоре надолу и дава възможност за готини технологии като „ разклащане на дървета ' и кодово разделяне .
В WebAssembly вносът и износът изпълняват различни задачи, отколкото импортирането на ES6. Внос / износ на WebAssembly:
trace()
И abort()
функции).В кода по-долу, env.abort
и env.trace
са част от средата, която трябва да предоставим на модула WebAssembly. nBodyForces.logI
и приятели функции предоставят съобщения за отстраняване на грешки в конзолата. Имайте предвид, че предаването на низове в / извън WebAssembly не е тривиално, тъй като единствените типове WebAssembly са числата i32, i64, f32, f64, с i32 препратки към абстрактна линейна памет.
Забележка: Тези примери за код превключват напред-назад между JavaScript кода (уеб работника) и AssemblyScript (кода WASM).
// 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 можем да завършим импортирането на тези функции по следния начин:
// nBodyForces.ts declare function logI(data: i32): void declare function logF(data: f64): void
Забележка : Прекъсване и проследяване се импортират автоматично .
От AssemblyScript можем да експортираме нашия интерфейс. Ето някои експортирани константи:
// 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
И тук е износът на nBodyForces()
които ще извикаме от JavaScript. Експортираме типа Float64Array
в горната част на файла, за да можем да използваме JavaScript loader на AssemblyScript в нашия уеб работник, за да получим данните (вижте по-долу):
// 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: .wasm и .wat
Когато нашият AssemblyScript nBodyForces.ts
се компилира в WebAssembly nBodyForces.wasm
двоичен , има опция за създаване и на „текстова“ версия, описваща инструкциите в двоичния файл.

Фигура 3: Не забравяйте, че AssemblyScript е език. WebAssembly е компилатор и време за изпълнение.
Вътре в nBodyForces.wat
файл, можем да видим тези внос и износ:
;; 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) '1e 0 0 0 1 0 0 0 1 0 0 01e 0 0 0~ 0l 0i 0b 0/ 0r 0t 0/ 0t 0l 0s 0f 0. 0t 0s 0') ... ;; 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 ...
Вече имаме нашите nBodyForces.wasm
двоичен файл и уеб работник, който да го стартира. Пригответе се за бластоф! И малко управление на паметта!
За да завършим интеграцията, трябва да предадем променлив масив от плувки на WebAssembly и да върнем променлив масив от плувки в JavaScript.
С наивен JavaScript буржоа, аз се заех да предам безразлично тези елегантни масиви с променлив размер в и извън кръстосана платформа с висока производителност. Предаването на данни към / от WebAssembly определено беше най-неочакваната трудност в този проект.
Въпреки това, с много благодарности за тежко повдигане, извършено от екипа на AssemblyScript , можем да използваме техния “loader”, за да помогнем:
// workerWasm.js - our web worker /** * AssemblyScript loader adds helpers for moving data to/from AssemblyScript. * Highly recommended */ const loader = require('assemblyscript/lib/loader')
require()
означава, че трябва да използваме модулен пакет като Rollup или Webpack. За този проект избрах Rollup заради неговата простота и гъвкавост и никога не погледнах назад.
Не забравяйте, че нашият уеб работник работи в отделна нишка и по същество е onmessage()
функция с switch()
изявление.
loader
създава нашия Wasm модул с някои допълнителни удобни функции за управление на паметта. __retain()
и __release()
управлява справките за събиране на боклук в работното време на изпълнение __allocArray()
копира нашия масив от параметри в паметта на модула wasm __getFloat64Array()
копира масива с резултати от модула wasm в работното време за изпълнение
Вече можем да маршалираме плаващи масиви в и извън nBodyForces()
и завършете нашата симулация:
// 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 }) } }
С всичко, което научихме, нека прегледаме нашия уеб работник и пътуването WebAssembly. Добре дошли в новия браузър-бекенд на мрежата. Това са връзки към кода на GitHub:
- ВЗЕМЕТЕ Index.html
- main.js
- nBodySimulator.js - предава съобщение на своя уеб работник
- workerWasm.js - извиква функцията WebAssembly
- nBodyForces.ts - изчислява и връща масив от сили
- workerWasm.js - предава резултатите обратно в основната нишка
- nBodySimulator.js - разрешава обещанието за сили
- nBodySimulator.js - след това прилага силите върху телата и казва на визуализаторите да рисуват
От тук, нека започнем шоуто, като създадем nBodyVisualizer.js
! Следващата ни публикация създава визуализатор с помощта на API на Canvas, а последната публикация завършва с WebVR и Aframe.
Свързани: Урок за WebAssembly / Rust: Перфектна обработка на аудио Разбиране на основите
Може ли WebAssembly да замени JavaScript?
WebAssembly не е език, така че не може да замени JavaScript. Освен това разработването на функции и потребителски опит в WebAssembly е по-малко ефективно.
Защо WebAssembly е по-бърз?
WebAssembly е по-бърз, защото прави по-малко и е проектиран за производителност, вместо за използваемост на разработчика.
Може ли JavaScript да се компилира в WebAssembly?
Да, AssemblyScript се компилира в WebAssembly и се чувства като Typescript.
WebAssembly определено е не заместител на JavaScript като lingua franca на мрежата и света.
WebAssembly (съкратено Wasm) е двоичен формат на инструкции за базирана на стека виртуална машина. Wasm е проектиран като преносима цел за компилация на езици на високо ниво като C / C ++ / Rust, позволявайки разполагане в мрежата за клиентски и сървърни приложения. ' - WebAssembly.org
Важно е да се разграничи, че WebAssembly не е език. WebAssembly е като „.exe“ - или дори по-добре - Java „.class“ файл. Той се компилира от уеб разработчика от друг език, след което се изтегля и стартира във вашия браузър.
WebAssembly предоставя на JavaScript всички функции, които от време на време искахме да заемем, но никога наистина ли исках да притежавам. Подобно на наемането на лодка или кон, WebAssembly ни позволява да пътуваме до други езици, без да се налага да правим екстравагантен избор на „езиков начин на живот“. Това позволи на мрежата да се съсредоточи върху важни неща като предоставяне на функции и подобряване на потребителското изживяване.
Повече от 20 езика се компилират в WebAssembly: Rust, C / C ++, C # /. Net, Java, Python, Elixir, Go и разбира се JavaScript.
Ако си спомняте архитектурната диаграма на нашата симулация, ние делегирахме цялата симулация на nBodySimulator
, така че тя управлява уеб работника.
Ако си спомняте от интро публикация , nBodySimulator
има step()
функция, извикана на всеки 33ms. step()
функция прави тези неща - номерирани в диаграмата по-горе:
calculateForces()
обаждания this.worker.postMessage()
за да започне изчислението.this.onmessage()
получава съобщението.nBodyForces()
функция.this.postMessage()
към основната нишка с новите сили.this.worker.onMessage()
маршали на върнатите данни и обаждания.applyForces()
за актуализиране на позициите на телата.
В предишната публикация , ние изградихме уеб работника, който обгръща нашите WASM изчисления. Днес изграждаме малката кутия с надпис „WASM“ и преместваме данни навътре и навън.
За простота избрах AssemblyScript като език на изходния код, за да напишем нашите изчисления. AssemblyScript е подмножество на TypeScript - което е типизиран JavaScript - така че вече го знаете.
Например тази функция AssemblyScript изчислява гравитацията между две тела: :f64
в someVar:f64
маркира променливата someVar като плувка за компилатора. Не забравяйте, че този код се компилира и изпълнява в съвсем различно време на изпълнение от 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[]
Тази функция AssemblyScript взема (x, y, z, маса) за две тела и връща масив от три поплавъка, описващи (x, y, z) вектор на сила, който телата прилагат един към друг. Не можем да извикаме тази функция от JavaScript, защото JavaScript няма идея къде да я намери. Трябва да го „експортираме“ в JavaScript. Това ни води до първото ни техническо предизвикателство.
В ES6 мислим за импортиране и експортиране в JavaScript код и използваме инструменти като Rollup или Webpack, за да създадем код, който да работи в стари браузъри за обработка import
и require()
. Това създава дърво на зависимостите отгоре надолу и дава възможност за готини технологии като „ разклащане на дървета ' и кодово разделяне .
В WebAssembly вносът и износът изпълняват различни задачи, отколкото импортирането на ES6. Внос / износ на WebAssembly:
trace()
И abort()
функции).В кода по-долу, env.abort
и env.trace
са част от средата, която трябва да предоставим на модула WebAssembly. nBodyForces.logI
и приятели функции предоставят съобщения за отстраняване на грешки в конзолата. Имайте предвид, че предаването на низове в / извън WebAssembly не е тривиално, тъй като единствените типове WebAssembly са числата i32, i64, f32, f64, с i32 препратки към абстрактна линейна памет.
Забележка: Тези примери за код превключват напред-назад между JavaScript кода (уеб работника) и AssemblyScript (кода WASM).
// 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 можем да завършим импортирането на тези функции по следния начин:
// nBodyForces.ts declare function logI(data: i32): void declare function logF(data: f64): void
Забележка : Прекъсване и проследяване се импортират автоматично .
От AssemblyScript можем да експортираме нашия интерфейс. Ето някои експортирани константи:
// 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
И тук е износът на nBodyForces()
които ще извикаме от JavaScript. Експортираме типа Float64Array
в горната част на файла, за да можем да използваме JavaScript loader на AssemblyScript в нашия уеб работник, за да получим данните (вижте по-долу):
// 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: .wasm и .wat
Когато нашият AssemblyScript nBodyForces.ts
се компилира в WebAssembly nBodyForces.wasm
двоичен , има опция за създаване и на „текстова“ версия, описваща инструкциите в двоичния файл.

Фигура 3: Не забравяйте, че AssemblyScript е език. WebAssembly е компилатор и време за изпълнение.
Вътре в nBodyForces.wat
файл, можем да видим тези внос и износ:
;; 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) '1e 0 0 0 1 0 0 0 1 0 0 01e 0 0 0~ 0l 0i 0b 0/ 0r 0t 0/ 0t 0l 0s 0f 0. 0t 0s 0') ... ;; 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 ...
Вече имаме нашите nBodyForces.wasm
двоичен файл и уеб работник, който да го стартира. Пригответе се за бластоф! И малко управление на паметта!
За да завършим интеграцията, трябва да предадем променлив масив от плувки на WebAssembly и да върнем променлив масив от плувки в JavaScript.
С наивен JavaScript буржоа, аз се заех да предам безразлично тези елегантни масиви с променлив размер в и извън кръстосана платформа с висока производителност. Предаването на данни към / от WebAssembly определено беше най-неочакваната трудност в този проект.
Въпреки това, с много благодарности за тежко повдигане, извършено от екипа на AssemblyScript , можем да използваме техния “loader”, за да помогнем:
// workerWasm.js - our web worker /** * AssemblyScript loader adds helpers for moving data to/from AssemblyScript. * Highly recommended */ const loader = require('assemblyscript/lib/loader')
require()
означава, че трябва да използваме модулен пакет като Rollup или Webpack. За този проект избрах Rollup заради неговата простота и гъвкавост и никога не погледнах назад.
Не забравяйте, че нашият уеб работник работи в отделна нишка и по същество е onmessage()
функция с switch()
изявление.
loader
създава нашия Wasm модул с някои допълнителни удобни функции за управление на паметта. __retain()
и __release()
управлява справките за събиране на боклук в работното време на изпълнение __allocArray()
копира нашия масив от параметри в паметта на модула wasm __getFloat64Array()
копира масива с резултати от модула wasm в работното време за изпълнение
Вече можем да маршалираме плаващи масиви в и извън nBodyForces()
и завършете нашата симулация:
// 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 }) } }
С всичко, което научихме, нека прегледаме нашия уеб работник и пътуването WebAssembly. Добре дошли в новия браузър-бекенд на мрежата. Това са връзки към кода на GitHub:
- ВЗЕМЕТЕ Index.html
- main.js
- nBodySimulator.js - предава съобщение на своя уеб работник
- workerWasm.js - извиква функцията WebAssembly
- nBodyForces.ts - изчислява и връща масив от сили
- workerWasm.js - предава резултатите обратно в основната нишка
- nBodySimulator.js - разрешава обещанието за сили
- nBodySimulator.js - след това прилага силите върху телата и казва на визуализаторите да рисуват
От тук, нека започнем шоуто, като създадем nBodyVisualizer.js
! Следващата ни публикация създава визуализатор с помощта на API на Canvas, а последната публикация завършва с WebVR и Aframe.
Свързани: Урок за WebAssembly / Rust: Перфектна обработка на аудио Разбиране на основите
Може ли WebAssembly да замени JavaScript?
WebAssembly не е език, така че не може да замени JavaScript. Освен това разработването на функции и потребителски опит в WebAssembly е по-малко ефективно.
Защо WebAssembly е по-бърз?
WebAssembly е по-бърз, защото прави по-малко и е проектиран за производителност, вместо за използваемост на разработчика.
Може ли JavaScript да се компилира в WebAssembly?
Да, AssemblyScript се компилира в WebAssembly и се чувства като Typescript.
)) (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 ...
Вече имаме нашите nBodyForces.wasm
двоичен файл и уеб работник, който да го стартира. Пригответе се за бластоф! И малко управление на паметта!
За да завършим интеграцията, трябва да предадем променлив масив от плувки на WebAssembly и да върнем променлив масив от плувки в JavaScript.
С наивен JavaScript буржоа, аз се заех да предам безразлично тези елегантни масиви с променлив размер в и извън кръстосана платформа с висока производителност. Предаването на данни към / от WebAssembly определено беше най-неочакваната трудност в този проект.
Въпреки това, с много благодарности за тежко повдигане, извършено от екипа на AssemblyScript , можем да използваме техния “loader”, за да помогнем:
// workerWasm.js - our web worker /** * AssemblyScript loader adds helpers for moving data to/from AssemblyScript. * Highly recommended */ const loader = require('assemblyscript/lib/loader')
require()
означава, че трябва да използваме модулен пакет като Rollup или Webpack. За този проект избрах Rollup заради неговата простота и гъвкавост и никога не погледнах назад.
Не забравяйте, че нашият уеб работник работи в отделна нишка и по същество е onmessage()
функция с switch()
изявление.
loader
създава нашия Wasm модул с някои допълнителни удобни функции за управление на паметта. __retain()
и __release()
управлява справките за събиране на боклук в работното време на изпълнение __allocArray()
копира нашия масив от параметри в паметта на модула wasm __getFloat64Array()
копира масива с резултати от модула wasm в работното време за изпълнение
Вече можем да маршалираме плаващи масиви в и извън nBodyForces()
и завършете нашата симулация:
// 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 }) } }
С всичко, което научихме, нека прегледаме нашия уеб работник и пътуването WebAssembly. Добре дошли в новия браузър-бекенд на мрежата. Това са връзки към кода на GitHub:
От тук, нека започнем шоуто, като създадем nBodyVisualizer.js
! Следващата ни публикация създава визуализатор с помощта на API на Canvas, а последната публикация завършва с WebVR и Aframe.
WebAssembly не е език, така че не може да замени JavaScript. Освен това разработването на функции и потребителски опит в WebAssembly е по-малко ефективно.
WebAssembly е по-бърз, защото прави по-малко и е проектиран за производителност, вместо за използваемост на разработчика.
Да, AssemblyScript се компилира в WebAssembly и се чувства като Typescript.