http://blog.csdn.net/zhangzq86/article/details/61195685
WebAssembly 的出現是不是意味着 Javascript 要完?
https://www.zhihu.com/question/34186498?sort=created
新時代
WebAssembly(簡稱Wasm)是一種新的適合於編譯到Web的,可移植的,大小和加載時間高效的格式。這是一個新的與平台無關的二進制代碼格式,目標是解決JavaScript性能問題。這個新的二進制格式遠小於JavaScript,可由瀏覽器的JavaScript引擎直接加載和執行,這樣可節省從JavaScript到字節碼,從字節碼到執行前的機器碼所花費的即時編譯JIT(Just-In-Time)時間。 作為一種低級語言,它定義了一個抽象語法樹(Abstract Syntax Tree,AST),開發人員可以以文本格式進行調試。
WebAssembly描述了一個內存安全的沙箱執行環境,可以在現有的JavaScript虛擬機中實現。 當嵌入到Web中時,WebAssembly將強制執行瀏覽器的同源和權限安全策略。因此,和經常出現安全漏洞的Flash插件相比,WebAssembly是一個更加安全的解決方案。
WebAssembly可由C/C++等語言編譯而來。此外,WebAssembly由Google、Mozilla、微軟以及蘋果公司牽頭的W3C社區組共同努力,基本覆蓋主流的瀏覽器廠商,因此其可移植性相較Silverlight等有極大提升,平台兼容問題將不復出現。
在Web平台的很多項目中,對於原生新功能的支持需要Web瀏覽器或Runtime提供復雜的標准化的API來實現,但是JavaScript API往往較慢。使用WebAssembly,這些標准API可以更簡單,並且操作在更低的水平。例如,對於一個面部識別的Web項目,對於訪問數據流我們可以由簡單的JavaScript API實現,而把面部識別原生SDK做的事情交由WebAssembly實現。
需要了解的是,WebAssembly不是將C/C++等其他語言編譯到JavaScript,更不是一種新的編程語言。
探究
asm.js
上文的c語言求和代碼經由編譯器生成asm.js后如代碼3所示。
上述代碼轉換為WebAssembly的文本格式稍顯復雜,為了理解方便,我們從精簡的asm.js開始(見代碼4)。
wast文本文件
將asm.js代碼轉換為WebAssembly的文本格式 add.wast(轉換工具見本文工具鏈章節,如代碼5所示)。
WebAssembly中代碼的可裝載和可執行單元被稱為一個模塊(module)。在運行時,一個模塊可以被一組import值實例化,多個模塊實例能夠訪問相同的共享狀態。目前文本格式中的module主要用S表達式來表示。雖然S表達格式不是正式的文本格式,但它易於表示AST。WebAssembly也被設計為與ES6的modules集成。
一個單一的邏輯函數定義包含兩個部分:功能部分聲明在模塊中每個內部函數定義的簽名,代碼段部分包含由功能部分聲明的每個函數的函數體。WebAssembly是帶有返回值的靜態類型,並且所有參數都含有類型。上面的add.wast可以解讀為:
- 聲明了一個名為$add的函數;
- 包含兩個參數a和b,兩者都是32位整型;
- 結果是一個32位整型;
- 函數體是一個32位的加法:
- 上面是局部變量$a得到的值;
- 下面是局部變量$b得到的值;
- 由於沒有明確的返回節點,因此return是該加法函數的最后加載指令。
二進制Wasm文件
如圖1所示,由C語言求和代碼經過編譯生成二進制文件,通讀文件可以找到相應的頭部、類型、導入、函數以及代碼段等。通過JavaScript API載入Wasm二進制文件后,最終轉換到機器碼執行。
工具鏈
開發人員現在可以使用相應的工具鏈從C / C ++源文件編譯WebAssembly模塊。WebAssembly由許多工具支持,以幫助開發人員構建和處理源文件和生成的二進制內容。
Emscripten
Emscripten是其中無法回避的工具之一,如圖2所示。在圖2中,Emscripten SDK管理器(emsdk)用於管理多個SDK和工具,並且指定當前正被使用到編譯代碼的特定SDK和工具集。
Emscripten的主要工具是Emscripten編譯器前端(emcc),它是例如GCC的標准編譯器的簡易替代實現。
Emcc使用Clang將C/C++文件轉換為LLVM(源自於底層虛擬機Low Level Virtual Machine)字節碼,使用Fastcomp(Emscripten的編譯器核心,一個LLVM后端)把字節碼編譯成JavaScript。輸出的JavaScript可以由Node.js執行,或者嵌入HTML在瀏覽器中運行。這帶來的直接結果就是,C和C++程序經過編譯后可在JavaScript上運行,無需任何插件。
WABT和Binaryen
除此之外,對於想要使用由其他工具(如Emscripten)生成的WebAssembly二進制文件感興趣的開發者,目前http://webassembly.org/官方額外提供了另外兩組不同的工具:
- WABT ——WebAssembly二進制工具包;
- Binaryen——編譯器和工具鏈。
WABT工具包支持將二進制WebAssembly格式轉換為可讀的文本格式。其中wasm2wast命令行工具可以將WebAssembly二進制文件轉換為可讀的S表達式文本文件。而wast2wasm命令行工具則執行完全相反的過程。
Binaryen則是一套更為全面的工具鏈,是用C++編寫成用於WebAssembly的編譯器和工具鏈基礎結構庫(如圖3所示)。WebAssembly是二進制格式(Binary Format)並且和Emscripten集成,因此該工具以Binary和Emscript-en的末尾合並命名為Binaryen。它旨在使編譯WebAssembly容易、快速、有效。它包含且不僅僅包含下面的幾個工具。
- wasm-as:將WebAssembly由文本格式(當前為S表達式格式)編譯成二進制格式;
- wasm-dis:將二進制格式的WebAssembly反編譯成文本格式;
- asm2wasm:將asm.js編譯到WebAssembly文本格式,使用Emscripten的asm優化器;
- s2wasm:在LLVM中開發,由新WebAssembly后端產生的.s格式的編譯器;
- wasm.js:包含編譯為JavaScript的Binaryen組件,包括解釋器、asm2wasm、S表達式解析器等。
Binaryen目前提供了兩個生成WebAssembly的流程,由於emscripten的asm.js生成已經非常穩定,並且asm2wasm是一個相當簡單的過程,所以這種將C/C ++編譯為WebAssembly的方法已經可用(如圖4所示)。
由此可見,Emscripten以及Binaryen提供了完整的C/C++到WebAssembly的解決方案。而Binaryen則幫助提升了WebAssembly的工具鏈生態。
提示
由於WebAssembly正處於活躍開發階段,各項編譯步驟和編譯工具會有大幅變更和改進,相信最終的編譯工具和步驟會趨於便捷,開發者需要留意官方網站的最新動態。
實戰
Linux和mac OS平台編譯原生代碼到WebAssembly可由如下步驟實現。
編譯環境准備
操作系統必須有可以工作的編譯器工具鏈,因此需要安裝GCC、cmake環境,此外Python、node.js及Java環境也是需要的(其中Java為可選,如圖5所示)。
如果是以其他方式安裝了Node.js,可能需要更新~/.emscripten文件的NODE_JS屬性。
安裝正確的emscripten分支
要編譯原生代碼到WebAssembly,我們需要emscripten的incoming分支。由於emscripten不僅僅是用於WebAssembly的編譯工具鏈,選擇正確的分支尤為重要(如圖6所示)。
其中URLTO具體的URL是https://s3.amazonaws.com/mozilla-games/emscripten/releases/emsdk-portable.tar.gz。
處理安裝異常
可運行emcc -v命令進行驗證安裝。如果遇到如圖7所示的錯誤,表明帶有JavaScript后端的LLVM編譯器並未被生成。
通過圖8步驟,可以解決該問題,並且在~/.emscripten 文件中修改如下配置:
開始編譯程序
現在一個完整的工具鏈已經具備,我們可以使用它來編譯簡單的程序到WebAssembly。但是,還有一些其他注意事項:
- 必須通過參數-s Wasm=1到emcc(否則默認emcc將編譯出asm.js);
- 除了Wasm二進制文件和JavaScript wrapper外,如果還希望emscripten生成一個可直接運行的程序的HTML頁面,則必須指定一個擴展名為.html的輸出文件。
在編譯之前,首先准備一個最基本的add.c程序,見代碼6。
按代碼7所示的命令編輯好add.c程序並編譯:
運行WebAssembly應用
以Chrome瀏覽器為例,如果直接在瀏覽器內本地打開HTML文件,會有圖9所示的錯誤:
由於XMLHttpRequest跨域請求不支持file://協議,必須經由HTTP實際輸出,可以由python的SimplHTTPServer改進,見代碼8:
在瀏覽器中輸入http://127.0.0.1:8080並打開add.html,就能直接看到轉換成WebAssembly的應用程序輸出結果。
創建獨立WebAssembly
默認情況下,emcc會創建JavaScript文件和WebAssembly的組合,其中JS加載包含編譯代碼的WebAssembly。對於C/C++開發人員,他們可能更傾向於創建獨立的WebAssembly,用於JavaScript開發人員調用,見代碼9。
上述命令運行后,我們可以得到獨立的Wasm文件。需要說明的是,該參數仍然在開發中,可能隨時發生規范和實現變更。
JavaScript API調用
從C/C++程序編譯獲得一個.wasm模塊之后,JavaScript開發人員可以通過如下方式進行載入.wasm文件並執行。WebAssembly社區組也有計划通過Streams使用streaming以及異步編譯,見代碼10。
最后一行調用導出的WebAssembly函數,它反過來調用我們導入的JS函數,最終執行add(201700, 2),並且在控制台獲得期望的結果輸出(如圖10所示)。
性能
那么,WebAssembly的真實性能如何呢?首先我們用一直被用來作為CPU基准測試的斐波那契 (Fibonacci)數列來進行對比,這里使用的是性能較差的遞歸算法,在Node.js v7.2.1環境下,能夠看到WebAssembly性能優勢越發明顯(如圖11所示)。
再看看最基本的1000毫秒時間內,求和計算的運算量統計,在同一台計算機的Firefox 50.1.0版本的運算結果如圖12所示。
盡管重復測試時結果不盡相同,重啟瀏覽器並多次測試取平均值后依然可以看到WebAssembly的運算量比JavaScript快了近一個量級。
Demo
圖13展示了Angry Bots Demo,它是由WebAssembly項目發布的一個Demo,由Unity游戲移植而來。
通過如下方式可以體驗WebAssembly在瀏覽器中的強大性能。即便Google Chrome較新的穩定版也已支持WebAssembly,還是推薦使用canary版及Firefox的nightly版進行測試。
- 下載瀏覽器:
1-1. Google Chrome;
1-2. Mozilla Firefox;
1-3. Opera;
1-4. Vivaldi。 - 打開 WebAssembly支持 :
2-1. Google Chrome:chrome://flags/#enable-webassembly;
2-2. Mozilla Firefox:about:config→接受→搜索javascript.options.wasm→設置為true;
2-3. Opera:opera://flags/#enable-webassembly;
2-4. Vivaldi:vivaldi://flags#enable-webassembly。
訪問:http://webassembly.org/demo/。
使用W、A、S、D等鍵實現移動操作,點擊鼠標進行射擊。該WebAssembly游戲在瀏覽器中運行相當流暢,媲美原生性能。
除了最新的瀏覽器開始對WebAssembly逐步支持外,Intel開源技術中心開發的Crosswalk項目(https://crosswalk-project.org/)早在2016年11月初的Crosswalk 22穩定版(Windows及Android 平台)即已加入對WebAssembly實驗性的支持,開發者可以使用該版本體驗Angry Bots Demo。
開發者
WebAssembly對於Web有顯著的性能提升,對於開發者尤其是前端或者JavaScript開發人員而言,並不意味着WebAssembly將會取代JavaScript(如圖14所示)。
WebAssembly被設計為對JavaScript的補充,而不是替代,是為了提供一種方法來獲得應用程序的關鍵部分接近原生性能。隨着時間的推移,雖然WebAssembly將允許多種語言(不僅僅是C/C++)被編譯到Web,但是JavaScript的發展勢頭不會因此被削弱,並且仍然將保持Web的單一動態語言。此外,由於WebAssembly構建在JavaScript引擎的基礎架構上,JavaScript和WebAssembly將在許多場景中配合使用。
那么WebAssembly是不是僅僅面向C/C++開發者呢?答案依舊是否定的。WebAssembly最初實現的重點是C/C++,由Mozilla主導開發的注重高效、安全和並行的Rust也能在2016年末被成功編譯到WebAssembly了,未來還會繼續增加其他語言的支持,見代碼11。
在未來,通過ES6模塊接口與JavaScript集成,Web開發人員並不需要編寫C++,而是可以直接利用其他人編寫的庫,重用模塊化C++庫可以像使用JavaScript中的modules一樣簡單。
進展
依據開發路線圖,2016年10月31日,WebAssembly到達瀏覽器預覽的里程碑。Google Chrome V8引擎及Mozilla Firefox SpiderMonkey引擎都已經在trunk上支持WebAssembly瀏覽器預覽。2016年12月下旬,Microsoft Edge瀏覽器使用的JavaScript引擎ChakraCore v1.4.0啟用了WebAssembly瀏覽器預覽支持。而Webkit JavaScriptCore引擎對於該支持也在積極進行中。
目前,WebAssembly社區組已經有初始(MVP)二進制格式發布候選和JavaScript API在多個瀏覽器中實現。作為瀏覽器預覽期間的一部分,WebAssembly社區組(WebAssembly Community Group)現在正在征求更廣泛的社區反饋。社區組的初步目標是瀏覽器預覽在2017年第一季度結束,但在瀏覽器預覽期間的重大發現可能會延長該周期。當瀏覽器預覽結束時,社區組將產生WebAssembly的草案規范,並且瀏覽器廠商可以開始默認提供符合規范的實現。預計在2017年上半年,四大主流瀏覽器對原生的WebAssembly支持將到達穩定版。
具體到Google V8引擎的最新進展,asm.js代碼將不再通過Turbofan JavaScript編譯器而是編譯到WebAssembly后,在WebAssembly的原生執行環境中執行最終的機器碼。這種改變帶來的好處有,為asm.js將預先編譯(AOT,Ahead Of Time Compilation)帶到了Chrome,且完全向后兼容。新的WebAssembly編譯渠道重用了一些Turbofan JavaScript編譯器后端部分,因此能夠在少了很多編譯和優化消耗的前提下,產生類似的代碼。在Google Chrome中,WebAssembly將很快在Canary版中默認啟用,開發團隊也期望能夠發布到2017年第一季度末的穩定版中。
社區
包含所有主要瀏覽器廠商代表的W3C Web——Assembly社區組於2015年4月底成立。該小組的任務是,在編譯到適用於Web的新的、便攜的、大小和加載時間高效的格式上,促進早期的跨瀏覽器協作。該社區組也正在將WebAssembly設計為W3C開放標准。目前,除了文中所述主流瀏覽器廠商Mozilla、Google、微軟、及蘋果公司之外,Opera CTO及Intel的8位該領域專家均參與了該社區組。當然,並不是只有社區組成員才能參與標准的制定,任何人都可以在https://github.com/WebAssembly做出貢獻。
展望
由於主要的瀏覽器廠商對WebAssembly支持表現積極,並且都在實現WebAssembly的各項功能,因此在Web中高性能需求的應用例如在線游戲、音樂、視頻流、AR/VR、平台模擬、虛擬機、遠程桌面、壓縮及加密等都能夠獲得接近於原生的性能。相信WebAssembly將會開創Web的新時代。