以前的幾篇文章分別介紹了WebWorkers、Typed Array和ParallelArray,最後,咱們再來介紹一下與性能相關的標準:asm.js。html
asm.js
asm.js是由Mozilla提出的一個基於JS的語法標準,主要是爲了解決JS引擎的執行效率問題,尤爲是使用Emscripten從C/C++語言編譯成JS的程序的效率,目前只有Mozilla的Firefox Nightly中支持。html5
Emscripten
Emscripten是Mozilla的一個實驗性項目,目的是把C/C++開發的應用編譯成JS或HTML5的應用,編譯過程當中須要首先把C/C++程序編譯成LLVM的中間代碼,而後再轉換成JS代碼,這樣作的主要緣由是能夠很好地複用現有的針對LLVM的優化。java
C/C++是一種強類型的語言,這很好地對應了不一樣字長的CPU指令,每一種數據類型都有固定的上下限,一旦計算超出這個上下限就會產生溢出,好比char
類型的取值範圍是[-128,127],而(char)(127 + 1)
這個計算就會溢出,獲得(char)-128
。git
JS語言不只是弱類型的,並且數值類型只有一種-Number
,Number
類型的數據採用雙精度64位格式的IEEE 754值表示。若是用JS模擬C/C++類型的數值計算,就要模擬各類類型數據計算時的溢出效果。咱們經過下面這個簡單的C程序來看看如何用JS來模擬這樣的計算。github
2 |
char yInt8 = xInt8 + 1; |
3 |
cahr zInt8 = xInt8 / 2; |
上面這段代碼經過JS模擬後的代碼以下:web
2 |
var $add = (xInt8 + 1) | 0; |
3 |
var yInt8 = ($add << 24) >> 24; |
4 |
var $div = ((yInt8 | 0) / 2) & -1; |
5 |
var zInt8 = ($div << 24) >> 24; |
- 8位整型變量127,至關於
char xInt8 = 127;
X|0
讓計算結果成爲32位整數,此時$add == 128
- 先左移24位再右移24位,讓第8位成爲32位整數的符號位,來模擬8位整數計算,此時
yInt8 == -128
- JS中
127/2
結果是浮點數63.5,利用X&-1
將結果轉化成32位整數63(-1的補碼錶示就是0xFFFFFFFF)
- 用上面(3)相同的方法,把結果轉換成8位整數
你們看到,利用一些位移和邏輯運算能夠模擬C/C++語言中的數據計算,Emscripten就利用這個方法將C代碼轉換成JS代碼。你們可能還記得前篇文章介紹過的Typed Array,對Typed Array元素賦值則會自動進行相應的溢出和舍入處理,所以,利用Typed Array還能夠改寫成如下的代碼:數組
1 |
var HEAP8 = new Int8Array(STACK_SIZE); |
3 |
HEAP8[1] = HEAP[0] + 1; |
4 |
HEAP8[2] = HEAP[1] / 2; |
在EmScripten中,Typed Array用來模擬C/C++中的堆棧以及指針的訪問。瀏覽器
語法
asm.js不是一種新的語言,而是JS語法的一個子集,也就是說全部用asm.js寫的程序都是合法的JS程序,asm.js與JS語言的關係有點相似C與C++的關係。所以,不支持asm.js的瀏覽器或JS引擎也能夠無誤地執行asm.js的代碼。app
asm.js顧名思義是做爲JS的彙編語言來設計的,它的語法手寫起來很是困難,且難以閱讀。首先,asm.js的語法利用了一些標註讓JS的變量成爲強類型的,這些標註與Emscripten生成的代碼一模一樣,實際上asm.js的產生就是爲了提升Emscripten轉換後的代碼執行效率的。咱們來看一個例子:
同時,asm.js還規定了一個特殊的語法格式,下面這段代碼是一個最簡單的asm.js示例(代碼來自:http://asmjs.org/spec/latest/):
1 |
function MyAsmModule(stdlib, foreign, heap) { |
9 |
var result = MyAsmModule({}, {}, null ).export1(); |
在這個例子中,參數stdlib、foreign、heap由外部傳入,表示: 1. stdlib:有限的標準庫函數,主要是一些數學函數,對應Math對象上的方法 2. foreign:foreign function interface (FFI)外部JS函數訪問接口 3. heap:傳入一個ArrayBuffer對象,做爲asm.js的堆
編譯和運行
因爲asm.js至關於支持了強類型,所以能夠直接對應編譯成機器指令執行。asm.js的代碼採用另一套AOT(Ahead Of Time)編譯器,將asm.js代碼預先編譯成機器指令,在編譯過程或運行過程當中,一旦發現語法錯誤或違反類型標記的狀況出現,便從新將代碼交予JS引 擎解析執行(見下圖)。
圖片來自:http://asmjs.org/spec/latest/
性能
JS一直以來被人詬病的一個方面就是它的性能,得益於這些年來瀏覽器之間的競爭,讓JS的性能大大提高,Google的V八、Mozilla的 SpiderMonkey以及微軟的Chakra在性能方面都已經至關不錯,而asm.js進一步提高到相對本地代碼2倍慢的性能(以下圖)。這些測試用 例使用Emscripten轉換而來,Emscripten已經能夠直接生成asm.js代碼。 數據來自:http://ejohn.org/blog/asmjs-javascript-compile-target/
Demo
參考
- asm.js specification: http://asmjs.org/spec/latest/
- IEEE 754: https://en.wikipedia.org/wiki/IEEE_floating_point