認識 WebAssembly

自從Brendan Eich用十天時間創造了JavaScript,人們對它的吐槽就從未間斷過。衆所周知JavaScript是一門動態語言。運行於JavaScript引擎中,咱們熟悉的有Mozilla的SpiderMonkey,Safari的JavaScriptCore,Edge的Chakra還有大名鼎鼎的V8。V8引擎將JavaScript的運行效率提高到一個新的level。因此後來的Nodejs也採用V8做爲引擎,實現了用js進行後端開發的願景。javascript

然而JavaScript發展到今天,其語言基因中存在的缺陷並不能獲得根本性的改變。好比常見的加法操做html

function add(a, b) {
    return a + b;
}

這段代碼在瀏覽器中的運行過程比你想象的複雜。
add在被調用前,js引擎並不能提早預判傳入參數的類型,須要在運行時對參數進行以下一連串的類型判斷和轉換操做。前端

pic

對js加法運算的詳細操做(keng)有興趣的能夠看這篇文章java

V8再快也難以逾越語言自己的瓶頸。這種問題是動態語言的弊端,對於此類問題,業界已經出現了很是多的解決方案。python

而本文要講的正是目前最爲前沿的一種 ------ WebAssemblygit

WebAssembly這個概念其實2015年就提出來了,而就在不久以前,四大瀏覽器廠商,Chrome, Firefox, Edge, Safari 在新版的瀏覽器中才所有默認支持Webassembly(Chrome, Firefox早於後二者),這種技術很快將在前端高性能開發領域中大放異彩。github

WebAssembly是什麼?

下面是來自官方的定義:web

WebAssembly or wasm is a new portable, size- and load-time-efficient format suitable for compilation to the web.typescript

關鍵詞:」format",WebAssembly 是一種編碼格式,適合編譯到web上運行。後端

事實上,WebAssembly能夠看作是對JavaScript的增強,彌補JavaScript在執行效率上的缺陷。

  • 它是一個新的語言,它定義了一種AST,並能夠用字節碼的格式表示。

  • 它是對瀏覽器的增強,瀏覽器可以直接理解WebAssembly並將其轉化爲機器碼。

  • 它是一種目標語言,任何其餘語言均可以編譯成WebAssembly在瀏覽器上運行。

想象一下,在計算機視覺,遊戲動畫,視頻編解碼,數據加密等須要須要高計算量的領域,若是想在瀏覽器上實現,並跨瀏覽器支持,惟一能作的就是用JavaScript來運行,這是一件吃力不討好的事情。而WebAssembly能夠將現有的用C,C++編寫的庫直接編譯成WebAssembly運行到瀏覽器上, 而且能夠做爲庫被JavaScript引用。那就意味着咱們能夠將不少後端的工做轉移到前端,減輕服務器的壓力。這是WebAssembly最爲吸引人的特性。而且WebAssembly是運行於沙箱中,保證了其安全性。

爲何要有WebAssembly?

若是隻是想讓C,C++,Java等原生語言編寫的模塊運行在瀏覽器上。咱們只須要一個轉換器,將源語言轉換爲目標語言JavaScript,而這種技術其實很早就有了。

例如將Java轉換成JavaScript的Google Web Toolkit (GWT)

將python轉換成JavaScript的pyjamas 等等。

可是這並無解決JavaScript執行慢的問題,這跟直接用JavaScript來重寫代碼庫是同樣的做用。這就是爲何Electron能直接運行Node.js但對比傳統桌面應用依然弱雞的緣由。

要理解JavaScript爲何運行慢,就要理解它在引擎中的處理過程。
傳統JavaScript在V8引擎中的編譯過程是這樣的:首先JavaScript會被編譯成AST,而後引擎再將AST, 轉化爲機器語言交給底層執行。

V8的pipeline結構會進一步先將AST轉化爲一種中間代碼,再對中間代碼再次生成優化後的機器碼,從而實現更快的執行速度。

pic

對於WebAssembly來講,前面的parser, optimize 所有省了,直接編譯到機器碼。

pic

瀏覽器經過增長一種語言格式的編譯支持,來實現執行效率的突破。

WebAssembly除了運行快以外,其特殊的二進制表示法也大大減少了代碼包的大小。同時提高了瀏覽器的加載速度。

如何使用WebAssembly?

如今你已經能在這些瀏覽器中使用WebAssembly了。

WebAssembly這麼快,但並不意味着JavaScript這門語言要今後絕跡了。

如前面所說,WebAssembly和JavaScript之間是能夠相互調用的。

假設咱們用C寫了這段代碼

include <math.h>
int add(int a, int b) {
    return a + b;
}

首先將其轉化爲wasm文件, 這裏運用一個線上的工具 WasmFiddle

將轉化的add.wasm下載下來。

因爲目前還沒支持 <script src=「abc.wasm" type="module" />的引入方式。因此不能直接在html引入,咱們能夠經過JS fetch來請求文件。

先封裝一個fetch方法:

function fetchAndInstantiateWasm (url, imports) {
    return fetch(url)
    .then(res => {
        if (res.ok)
            return res.arrayBuffer();
        throw new Error(`Unable to fetch Web Assembly file ${url}.`);
    })
    .then(bytes => WebAssembly.compile(bytes))
    .then(module => WebAssembly.instantiate(module, imports || {}))
    .then(instance => instance.exports);
}

用定義好的fetchAndInstantiateWasm方法請求add.wasm文件,並在回調中調用C中定義的add方法,成功輸出結果15。

fetchAndInstantiateWasm('add.wasm', {})
.then(m => {
    console.log(m.add(5, 10)); // 15
});

一樣經過js import,也可以在C中調用js的方法。

fetchAndInstantiateWasm('program.wasm', {
    env: {
      consoleLog: num => console.log(num)
    }
})
.then(m => {
    console.log(m.add(5, 10)); // 15
});

上面在js代碼中定義了consoleLog, 並傳入了wasm文件,在C中就能夠調用consoleLog方法往控制檯輸出信息,你也能夠執行一些你想要的其餘操做。

#include<stdio.h>
void consoleLog(int num);
int add(int num1, int num2) {
    int result = num1 + num2;
    consoleLog(result);
    return result;
}

可直接下載demo代碼執行查看效果 demo

運行python -m SimpleHTTPServer後訪問localhost:8000, 查看log中輸出信息。

前面說WebAssembly是一門新的語言,但上面引入的wasm只是一種字節碼,是做爲其餘語言編譯的目標語言,徹底沒有可讀性。其實WebAssembly是有本身的語法的,文件格式爲wast。下面是add方法編譯成的WebAssembly版本。

(module
  (type $FUNCSIG$vi (func (param i32)))
  (import "env" "consoleLog" (func $consoleLog (param i32)))
  (table 0 anyfunc)
  (memory $0 1)
  (export "memory" (memory $0))
  (export "add" (func $add))
  (func $add (param $0 i32) (param $1 i32) (result i32)
    (call $consoleLog
      (tee_local $1
        (i32.add
          (get_local $1)
          (get_local $0)
        )
      )
    )
    (get_local $1)
  )
)

wast是可編輯的,它一樣能夠直接轉化爲wasm, 用於瀏覽器引入。

上面只是一個最簡單的例子,實際上利用WebAssembly實現的應用已經能夠至關酷炫。

官方展現的demo遊戲

還有一個運用webassembly實現的瀏覽器視頻編輯器

和其餘相似技術的區別?

asm.js

可能對前端比較關注的同窗有據說過asm.js。它是Mozilla開發的一個JavaScript的子集。就是在JavaScript的基礎上,加入了靜態類型的支持。
asm.js是Mozilla開發的,因此只支持自家瀏覽器Firefox。固然代碼也能夠兼容運行於其餘瀏覽器,可是就沒有了優化效果。

asm.js 提供一種語法來表示變量類型

var first = 5;
var second = first;

對於上面這段JavaScript代碼,在asm.js裏是這樣寫的

var first = 5;
var second = first | 0;

在first後面加上|0,咱們就將first標記爲32位整數,而被賦值的second也爲被定義爲32位整數。
在Mozilla引擎編譯代碼的時候,遇到這些標誌就會提早知道變量的類型,提早優化代碼。而這些標記也不影響其餘引擎的運算結果。

然而說到底它仍是JavaScript,只不過咱們提早爲優化作了準備。代碼仍是要通過JavaScript Code ->AST->Optimize的過程。
另外asm.js也是支持將C,C++轉化爲asm.js的,有興趣的能夠參考這裏

TypeScript

你們應該也知道微軟的TypeScript,TypeScript作的工做其實跟asm.js有點相似,只不過TypeScript是更加High-Level的。他是JavaScript的一個超集,就是在JavaScript的基礎上支持了類型和類等語法。而且能直接編譯爲JavaScript。TypeScript在於能在開發階段就進行類型檢查,保證代碼開發效率和安全性。可是從瀏覽器運行效率上來看並無優化效果,由於瀏覽器並不原生支持。

相同功能的還有facabook的Flow,也是在開發階段加入類型的支持。

結語

目前WebAssembly由W3C WebAssembly Community Group負責開發與標準定製,而該組織的成員正是來自Google, Microsoft, Mozilla等瀏覽器開發人員。幾個大廠同時投入到WebAssembly的開發中,相信不久WebAssembly就會成爲一種瀏覽器網站&應用的通用優化技術。

參考資料

  1. https://medium.com/javascript...

  2. https://medium.com/javascript...

  3. https://www.youtube.com/watch...

  4. http://blog.techbridge.cc/201...

  5. https://github.com/WebAssembl...

相關文章
相關標籤/搜索