asm.js
asm.js
是JavaScript
語(yǔ)言中一個(gè)可以高度優(yōu)化的子集。通過避免JavaScript
引擎某些難以優(yōu)化的機(jī)制和模式(主要是垃圾回收和類型判斷)赫模,達(dá)到JavaScript
引擎運(yùn)行優(yōu)化的目的树肃。換言之瀑罗,正常的JavaScript
代碼會(huì)類型自動(dòng)裝換和垃圾自動(dòng)回收的,而編寫asm.js
風(fēng)格的代碼則表示程序員需要管理內(nèi)存和確定數(shù)據(jù)類型斩祭。
asm.js
不提供任何額外的語(yǔ)法,只要編寫asm.js
的代碼停忿,在支持asm.js
優(yōu)化的JavaScript
引擎中能被自動(dòng)識(shí)別,從而讓引擎實(shí)現(xiàn)自己的優(yōu)化席赂,而在不支持asm.js
的引擎中也能正常運(yùn)行。
手寫asm.js例子
首先解決的是JavaScript
中變量類型的問題
var a = 10;
var b = a;
var a = 10;
var b = a | 0;
上面兩段代碼的不同之處在于颅停,第一段代碼b的值類型在運(yùn)行的時(shí)候才能被確定,而第二段代碼中b總是會(huì)被當(dāng)作32位整型處理癞揉。同樣的還有對(duì)運(yùn)算的限制,如(a + b) | 0
把兩個(gè)變量的加運(yùn)算限制為更高效的整型加運(yùn)算喊熟。在支持asm.js
的JavaScript
引擎中柏肪,運(yùn)行上面的代碼會(huì)進(jìn)行底層優(yōu)化。
第二個(gè)要解決的問題是內(nèi)存分配芥牌,眾所周知烦味,JavaScript
不提供垃圾回收相關(guān)的API,在JavaScript
中講垃圾回收大多數(shù)開發(fā)者會(huì)想到將不用的變量設(shè)為null
壁拉,比如:
// 創(chuàng)建一個(gè)長(zhǎng)度為10000的數(shù)組
var a = new Array(10000).fill('hello');
a = null;
但對(duì)于垃圾回收a = null
只是告訴瀏覽器長(zhǎng)度為10000的數(shù)組已經(jīng)沒有被任何變量引用谬俄,可以回收其占用的內(nèi)存了,至于什么時(shí)候回收弃理,瀏覽器有自己的想法溃论。
asm.js
中所說的內(nèi)存分配和這個(gè)機(jī)制不同,asm.js
的內(nèi)存分配由程序員自己控制痘昌。使用ArrayBuffer
創(chuàng)建一個(gè)數(shù)據(jù)緩沖區(qū)钥勋,可以在這個(gè)緩沖區(qū)存儲(chǔ)和取值炬转,而不需要再付出內(nèi)存分配和垃圾回收的代價(jià)。ArrayBuffer
是不能直接操作的算灸,而是通過類型數(shù)組對(duì)象和DataView
(視圖)對(duì)象返吻,具體用法請(qǐng)參考 MDN | ArrayBuffer,下面是一個(gè)例子
// 創(chuàng)建一個(gè)64k的數(shù)據(jù)緩沖區(qū)
var heap = new ArrayBuffer(0x10000);
// 使用64位浮點(diǎn)值數(shù)組引用這個(gè)緩沖區(qū)
var arr = new Float64Array( heap )
下面是一個(gè)更復(fù)雜的例子乎婿,使用asm.js
風(fēng)格的代碼編寫一個(gè)函數(shù)計(jì)算兩數(shù)之間的相鄰數(shù)的乘積,存儲(chǔ)起來并計(jì)算總和
function ASM (heap) {
var arr = new Int32Array(heap);
function foo(x, y) {
x = x | 0;
y = y | 0;
var i = 0;
var p = 0;
var sum = 0;
for (i = x | 0; (i | 0) < (y | 0); p = (p + 8) | 0, i = (i + 1) | 0) {
sum = (sum + i * (i + 1)) | 0;
arr[p >> 3] = (i * (i + 1)) | 0;
}
return +sum;
}
return foo;
}
var heap = new ArrayBuffer(0x1000);
var foo = ASM(heap)
foo(0, 1024) // 357913600
通常用“模塊”機(jī)制將asm.js代碼封裝起來街佑,如上面的代碼谢翎,內(nèi)存區(qū)變量為私有變量,不可在外部更改沐旨。
上面代碼中,使用ArrayBuffer
分配內(nèi)存,使用Int32Array
規(guī)定數(shù)據(jù)類型酵熙,在使用數(shù)據(jù)時(shí)鉴吹,每個(gè)數(shù)據(jù)也都使用特殊的符號(hào)標(biāo)識(shí)數(shù)據(jù)類型,在支持asm.js
的引擎中谊迄,這些都是觸發(fā)優(yōu)化的信號(hào)闷供,而在不支持asm.js
的引擎,這些符號(hào)也是正常的運(yùn)算符而已统诺,不影響計(jì)算結(jié)果歪脏。
Emscripten
在實(shí)際運(yùn)用中,不大可能手寫asm.js
規(guī)范的代碼粮呢,寫起來異常麻煩并且容易出錯(cuò)婿失,所以通常asm.js
代碼通常是其他語(yǔ)言的編譯目標(biāo)代碼,比如使用Emscripten
將C / C++
代碼編譯成asm.js
啄寡。
安裝Emscripten
$ git clone https://github.com/juj/emsdk.git
$ cd emsdk
$ ./emsdk install latest
$ ./emsdk activate latest
$ source ./emsdk_env.sh
Emscripten的編譯用法非常簡(jiǎn)單
- 編寫一個(gè)C++程序hello.cc豪硅。
#include <iostream>
int main () {
std::cout << "Hello World" << std::endl;
}
- 使用以下命令將
C++
源碼編譯成asm.js
。
emcc hello.cc
輸出文件a.out.js
就是asm.js
規(guī)范的JavaScript
代碼挺物,默認(rèn)執(zhí)行main
函數(shù)。
WebAssembly
WebAssembly
字節(jié)碼是一種抹平了不同CPU
架構(gòu)的機(jī)器碼嵌溢,WebAssembly
字節(jié)碼不能直接在任何一種CPU
架構(gòu)上運(yùn)行赖草,但由于非常接近機(jī)器碼剪个,可以非常快的被翻譯為對(duì)應(yīng)架構(gòu)的機(jī)器碼绒疗,因此WebAssembly
運(yùn)行速度和機(jī)器碼接近吓蘑,這聽上去非常像Java
字節(jié)碼坟冲。
在WebAssembly
出現(xiàn)之前健提,瀏覽器只能運(yùn)行.js
后綴的編程代碼文件,JavaScript
是web應(yīng)用開發(fā)的唯一語(yǔ)言脐嫂,但是在支持WebAssembly
的瀏覽器上账千,現(xiàn)在能運(yùn)行.wasm
后綴的代碼文件了暗膜。
WebAssembly
幾乎不可能是手工編寫的桦山,一般由其他語(yǔ)言編譯而成,目前能編譯成WebAssembly
的高級(jí)語(yǔ)言有:
-
AssemblyScript: 語(yǔ)法和
TypeScript
一致会放,對(duì)前端來說學(xué)習(xí)成本低咧最,為前端編寫WebAssembly
最佳選擇矢沿; - c\c++: 官方推薦的方式;
- Rust: 語(yǔ)法復(fù)雜捣鲸、學(xué)習(xí)成本高闽坡,對(duì)前端來說可能會(huì)不適應(yīng);
-
Kotlin: 語(yǔ)法和
Java
、JS
相似外厂,語(yǔ)言學(xué)習(xí)成本低; - Golang: 語(yǔ)法簡(jiǎn)單學(xué)習(xí)成本低汁蝶。
Hello world
使用AssemblyScript
編譯成WebAssembly
,首先安裝
yarn global add AssemblyScript/assemblyscript
編寫源代碼demo.ts
export function foo (x: i32):i32 {
return x * x;
}
使用asc demo.ts -o demo.wasm
編譯代碼席爽,使用js代碼fetch
方法加載wasm
模塊
fetch('./demo.wasm')
.then(res => {return res.arrayBuffer()})
.then(WebAssembly.instantiate) // 編譯成當(dāng)前CPU架構(gòu)的機(jī)器碼并實(shí)例化
.then(module => { // module為WebAssembly模塊
console.log(module.instance.exports.foo(100))
})
總結(jié)
asm.js
和WebAssembly
都是底層優(yōu)化web程序性能的技術(shù),他們通常都是由其他語(yǔ)言編譯而成紫谷。asm.js
是JavaScript的一個(gè)子集笤昨,所以在不支持asm.js
優(yōu)化的瀏覽器上也能正常運(yùn)行握恳,它的文件類型是文本乡洼;WebAssembly
則是更新的技術(shù),提供了新的API拔稳,在不支持的瀏覽器上無(wú)法運(yùn)行巴比,它的文件類型是二進(jìn)制字節(jié)碼礁遵。這兩種技術(shù)雖然都是極高提升web程序性能的技術(shù)佣耐,但一般開發(fā)中不會(huì)使用到,只有在密集型計(jì)算稼病、圖形處理等計(jì)算場(chǎng)景才能發(fā)揮出它們的巨大優(yōu)勢(shì)然走。
作者簡(jiǎn)介:葉茂芍瑞,蘆葦科技web前端開發(fā)工程師,代表作品:口紅挑戰(zhàn)網(wǎng)紅小游戲洪己、蘆葦科技官網(wǎng)答捕。擅長(zhǎng)網(wǎng)站建設(shè)、公眾號(hào)開發(fā)拱镐、微信小程序開發(fā)沃琅、小游戲蜘欲、公眾號(hào)開發(fā),專注于前端框架郭脂、服務(wù)端渲染朱庆、SEO技術(shù)闷祥、交互設(shè)計(jì)、圖像繪制箱硕、數(shù)據(jù)分析等研究悟衩。
歡迎和我們一起并肩作戰(zhàn): web@talkmoney.cn
訪問 www.talkmoney.cn 了解更多