Web程序性能優(yōu)化——asm.js和WebAssembly

asm.js

asm.jsJavaScript語(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.jsJavaScript引擎中柏肪,運(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)代碼,比如使用EmscriptenC / 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)單

  1. 編寫一個(gè)C++程序hello.cc豪硅。
#include <iostream>

int main () {
  std::cout << "Hello World" << std::endl;
}
  1. 使用以下命令將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ǔ)法和 JavaJS 相似外厂,語(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.jsWebAssembly都是底層優(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 了解更多

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末惠昔,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子镇防,更是在濱河造成了極大的恐慌来氧,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,386評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件中狂,死亡現(xiàn)場(chǎng)離奇詭異胃榕,居然都是意外死亡瞄摊,警方通過查閱死者的電腦和手機(jī)泉褐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門膜赃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來跳座,“玉大人泣矛,你說我怎么就攤上這事】袼浚” “怎么了几颜?”我有些...
    開封第一講書人閱讀 164,704評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵蛋哭,是天一觀的道長(zhǎng)谆趾。 經(jīng)常有香客問我,道長(zhǎng)沪蓬,這世上最難降的妖魔是什么怜跑? 我笑而不...
    開封第一講書人閱讀 58,702評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮峡眶,結(jié)果婚禮上辫樱,老公的妹妹穿的比我還像新娘俊庇。我一直安慰自己,他們只是感情好辉饱,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,716評(píng)論 6 392
  • 文/花漫 我一把揭開白布缔逛。 她就那樣靜靜地躺著姓惑,像睡著了一般。 火紅的嫁衣襯著肌膚如雪敦冬。 梳的紋絲不亂的頭發(fā)上脖旱,一...
    開封第一講書人閱讀 51,573評(píng)論 1 305
  • 那天夯缺,我揣著相機(jī)與錄音踊兜,去河邊找鬼佳恬。 笑死于游,一個(gè)胖子當(dāng)著我的面吹牛贰剥,可吹牛的內(nèi)容都是我干的蚌成。 我是一名探鬼主播凛捏,決...
    沈念sama閱讀 40,314評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼瓶盛,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼惩猫!你這毒婦竟也來了蚜点?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,230評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎脯倒,沒想到半個(gè)月后藻丢,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體摄乒,經(jīng)...
    沈念sama閱讀 45,680評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,873評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了茵臭。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片旦委。...
    茶點(diǎn)故事閱讀 39,991評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡摩钙,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出胖笛,到底是詐尸還是另有隱情长踊,我是刑警寧澤谬返,帶...
    沈念sama閱讀 35,706評(píng)論 5 346
  • 正文 年R本政府宣布之斯,位于F島的核電站,受9級(jí)特大地震影響遣铝,放射性物質(zhì)發(fā)生泄漏佑刷。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,329評(píng)論 3 330
  • 文/蒙蒙 一酿炸、第九天 我趴在偏房一處隱蔽的房頂上張望瘫絮。 院中可真熱鬧,春花似錦填硕、人聲如沸麦萤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)壮莹。三九已至,卻和暖如春姻檀,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背胶台。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工铸磅, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留济竹,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,158評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子幅疼,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,941評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 參考Emscripten與拼音輸入法的相遇VS2010 c/c++ 本地化 emscripten 配置大家對(duì)Ems...
    合肥黑閱讀 1,890評(píng)論 0 2
  • WebAssembly 系列(一)生動(dòng)形象地介紹 WebAssemblyWebAssembly 系列(二)Java...
    合肥黑閱讀 7,523評(píng)論 0 9
  • 官方中文版原文鏈接 感謝社區(qū)中各位的大力支持髓涯,譯者再次奉上一點(diǎn)點(diǎn)福利:阿里云產(chǎn)品券瘤泪,享受所有官網(wǎng)優(yōu)惠髓棋,并抽取幸運(yùn)大...
    HetfieldJoe閱讀 2,342評(píng)論 1 21
  • Javascript ,也叫Ecma script, 是這家伙用了 10 天時(shí)間趕出來的须床。柒凉。 所以膝捞,各位程序猿們...
    自度君閱讀 2,172評(píng)論 1 7
  • (圖片來源:giphy.com) 編者按:本文由明非在眾成翻譯平臺(tái)上翻譯林艘。 最近卜朗,WebAssembly 在 Ja...
    a6217200e10d閱讀 268,432評(píng)論 44 303