【譯】WebAssembly 初嘗

本文轉載自:衆成翻譯
譯者:文藺
連接:http://www.zcfy.cc/article/1031
原文:http://cultureofdevelopment.com/blog/build-your-first-thing-with-web-assemblyjavascript

拖拖拉拉很久,終於把我的博客整出來了。鳴謝 @pinggod。厚着臉安利一下,地址是 http://www.wemlion.com/。歡迎訪問,歡迎收藏。html

頭一次據說 WebAssembly 的時候就以爲很酷,而後就超興奮地開始嘗試。但從一開始嘗試的過程就不順利,愈來愈讓人灰心。本文的目的就是解決問題,讓你免受困擾。java

beware of cliff

讀者須知

本文寫做於 2016 年 6 月 24 日。WebAssembly 是一項很新的、不穩定的技術;隨着其標準化過程發展,本文中的任何內容均可能是錯誤的。git

不過先無論了....github

WebAssembly 是什麼

好吧,官網是這麼描述的:web

WebAssembly,或者稱做 wasm,是一項適用於 Web 編譯的可移植的、體積與加載高效的格式。(WebAssembly or wasm is a new portable, size- and load-time-efficient format suitable for compilation to the web.)chrome

嗯...什麼鬼?什麼格式?文本(Text)?二進制(Binary)?老實說,這個描述真糟糕。因此無論它,收起那些 binggo 遊戲卡(buzzword bingo cards,一種填詞遊戲,這些詞一般都是流行語,閱讀https://en.wikipedia.org/wiki/Buzzword_bingo瞭解更多 —— 譯者注),根據我全部的經驗來描述吧:npm

WebAssembly/wasm 是用來編寫高性能的、瀏覽器無關的 Web 組件的一種字節碼規範。(WebAssembly or wasm is a bytecode specification for writing performant, browser agnostic web components.)數組

有此妙語,聽起來超棒,但仍然沒 get 到點,接下來重點來了。WebAssembly 經過靜態類型變量實現性能提高,運行時靜態類型變量引用比動態類型變量更有效率。WebAssembly 由 W3C Community Group 制定,最終將被全部規範兼容的瀏覽器支持。還有殺手鐗,最終咱們可使用任何語言編寫這些 Web 組件(web components)。瀏覽器

聽起來酷了不少,不是麼?

一塊兒開始吧

學習新東西的時候,我一般會找儘量最簡單的例子來看它是如何工做的。不幸的是,對 WebAssembly 來講,這不太現實。在當前階段,wasm 僅僅只是字節碼規範。想象回到 1996 年,假如太陽公司(Sun Microsystems)的一些工程師們帶來了 JVM,但卻...沒有 Java....若果然如此,我想當時的對話多是這樣的:

—— 「夥計們,快來看看咱們作的這個執行字節碼的虛擬機!」
—— 「真棒!但咱們給它怎麼寫代碼?」

HelloWorld.class

圖:字節碼形式的 HelloWorld

—— 「嗯..這問題提得好。等會兒我查查看。」
—— 「真棒,若是遇到了任何問題,告訴咱們你的想法,在咱們的 github page 上貼出來。」
—— 「你說對啦。咱們如今先去看看其餘項目。」

這個例子有些糟糕,由於 JVM 是基於 Java 語言的;儘管如此,但願你仍是 get 到點了。若是都沒有將代碼編譯爲字節碼的工具,要起步就很困難了。那咱們要怎麼開始?

WebAssembly 以前有什麼

多數技術都是創新的結果,特別是當合理的嘗試成爲正式規範時。wasm 也不例外,它其實是 asm.js 的工做的延續, asm.js 是一個編寫 javascript 組件的的規範,可編譯爲靜態類型。wasm 的規範拓展了這些創意,它接受任何語言編譯而成的字節碼,這些字節碼做爲二進制文件而非文本文件經過網絡傳輸;規範由不少來自主流瀏覽器廠商的表明們一塊兒制定,而非僅僅是 Mozilla。

asm.js 僅僅是一個使用 javascript 語言特徵的最小子集編寫 javascript 的規範。你能夠手寫一些簡單的 asm.js 代碼,若是你想弄髒你的手,這正是極好的方式。(等會兒最好將這放在單獨的文件中,一般約定文件名格式爲 your-module-name.asm.js。))

function MyMathModule(global) {
    "use asm";
    var exp = global.Math.exp;
    function doubleExp(value) {
        value = +value;
        return +(+exp(+value) * 2.0);
    }
    return { doubleExp: doubleExp };
}

這還不是一個特別有用的函數,但符合規範。若是你以爲這很二,別人也是這麼以爲的,不過基本上每個字符都是必須的。在這當中,一元運算符 + 的做用是類型註解,這樣編譯器會知道那些變量是 double 類型的,運行時就沒必要再次分辨它們是什麼。它至關挑剔,若是你把什麼地方弄得一團糟,火狐控制檯會給你一些合理的錯誤信息。

若是你想在瀏覽器中使用,像下面這樣:

var myMath = new MyMathModule(window);
for(var i = 0; i < 5; i++) {
    console.log(myMath.doubleExp(i));
}

一切正常的話,結果大概像下圖這樣:

asm.js success

開始嘗試 WebAssembly

如今咱們已經有了一個能夠工做的 asm.js 代碼片斷,可使用 WebAssembly github page 提供的工具將其編譯爲 wasm。本身克隆代碼倉庫構建工具吧。這最麻煩了。這些工具一直在不斷髮展,代碼會時不時掛掉,特別是在 Windows 環境下。

無論你是用 Windows 仍是 Mac,電腦上必需要安裝 make 和 cmake 命令行工具。若是你在使用 Windows,你還須要安裝 Visual Studio 2015。Mac 用戶按照這裏的說明 操做;Windows 用戶按照這個說明操做。

building binaryen

圖: Windows 下的工具構建

對 WebAssembly 團隊來講,發佈能夠工做的二進制文件意味着朝着正確的方向前進了一大步。

構建成功以後,binaryen 目錄下會有一個 bin 文件夾,其中有一些用來將 asm.js 轉換爲 wasm 的工具。第一個工具是 asm2wasm.exe。它將 asm.js 代碼轉換爲 .s 格式的代碼,這些代碼是生成 wasm 所需的抽象語法樹(AST)的文本表現形式。運行工具,最終會獲得相似下面的東西:

(module
  (memory 256 256)
  (export "memory" memory)
  (type $FUNCSIG$dd (func (param f64) (result f64)))
  (import $exp "global.Math" "exp" (param f64) (result f64))
  (export "doubleExp" $doubleExp)
  (func $doubleExp (param $0 f64) (result f64)
    (f64.mul
      (call_import $exp
        (get_local $0)
      )
      (f64.const 2)
    )
  )
)

之後咱們能夠逐行分析上面的代碼,但如今我只想讓你看下它的樣子。我還得提醒你一點,由於 wasm 是二進制格式的,因此像你今天對 javascript 所作的那樣右擊、查看源碼是行不通的。從頭至尾都是二進制碼。目前的計劃是查看 wasm 模塊源碼時對二進制格式進行反彙編,讓人能讀懂。

接下來要作的是使用 wasm-as.exe.s 格式的代碼轉換爲 wasm 二進制碼。運行文件,最後就能獲得瀏覽器須要的真正的 wasm 二進制碼。

building wasm from asm.js

圖:將 asm.js 轉換爲 wasm 二進制碼

wasm bytecode

圖:wasm 二進制碼

緊接着,安裝最新版的 FirefoxChrome Canary,並啓用 WebAssembly。

若是你使用的是 Firefox,在地址欄中輸入 about:config,點擊「確認我會保證當心」。而後在搜索框中輸入 wasm,雙擊 javascript.options.wasm 將值設置爲 true,而後重啓瀏覽器。

若是你使用的是 Chrome Canary,打開 chrome://flags,往下翻,找到 Experimental WebAssembly,點擊「啓用」連接,再重啓瀏覽器。

最後一步就是讓模塊在瀏覽器中跑起來。初次嘗試時,這又是一大痛點,徹底不知道怎麼作。在規範中使用 wasm 模塊的 API 一點都沒找到。最後我在 Canary 的控制檯上輸入 WebAsse,並無任何提示。接着輸入 Was 的時候,提示出來了!控制檯上打印出的對象大概最簡陋的文檔了,不過這時候我忽然想到,能夠用一些其餘工具 (emscripten) 將代碼編譯爲 wasm。不過這是另一篇博客的話題了。

一段時間以後,鼠標落在了 WebAssembly 的設計文檔倉庫上。我看到了一個名爲 JS.md 的文件,點擊以後,有一個 API 的說明。仔細看頂部斜體的文字。但最精彩的部分仍是最底部的代碼片斷,演示瞭如何最低限度地加載模塊。我所須要作的就是拆出相關部分進行嘗試。

fetch("my-math-module.wasm")
    .then(function(response) {
        return response.arrayBuffer();
    })
    .then(function(buffer) {
        var dependencies = {
            "global": {},
            "env": {}
        };
        dependencies["global.Math"] = window.Math;
        var moduleBufferView = new Uint8Array(buffer);
        var myMathModule = Wasm.instantiateModule(moduleBufferView, dependencies);
        console.log(myMathModule.exports.doubleExp);
        for(var i = 0; i < 5; i++) {
            console.log(myMathModule.exports.doubleExp(i));
        }
    });

把代碼放到 html 文件中,啓動本地文件服務器,在瀏覽器中加載頁面。下面是在瀏覽器中的結果:

wasm in a browser

瀏覽器中運行的 wasm (至少嘗試運行了)

我估計須要去提交一個 bug 報告了。記着,一切都是實驗性的、不穩定的,因此當此類事情發生的時候,別灰心喪氣。

keep calm and file bug reports

恭喜你

你已經完成了第一個 WebAssembly 組件。接下來作些什麼?目前咱們碰到的還只是皮毛而已。在本例中手寫 asm.js 很重要,但須要時間和耐心。使用 emscripten 將應用轉換爲 asm.js 要簡單多了。關於這一點,我強烈建議你閱讀 asm.js 規範,特別是內存模型的部分,由於其中的許多概念都被遷移到 WebAssembly 上了。另一個怪異的事情是,目前還不能直接將數組做爲函數參數。人們已經達成共識,這須要改變,但規範中還沒有有關於這一點的。去看看指針邏輯吧。

還有一點,在 wasm 中作一些工做的時候,你可能發展實際上 WebAssembly 還沒普通的 javascript 運行得快。記住,現代的 javascript 引擎已是高度優化的,wasm 要遇上這速度還須要時間。WebAssembly 還還沒有進入準備生產的階段。

若是你有任何關於 wasm 或者本文中提到的工具的問題,在 Stack Overflow 中提出來,記得標上恰當的 tag。

相關文章
相關標籤/搜索