摘要:但是它們其實并不是二選一的關(guān)系并不是只能用或者。正因為如此,指令有時也被稱為虛擬指令。這是因為是采用基于棧的虛擬機的機制。聲明模塊的全局變量。。下文預告現(xiàn)在你已經(jīng)了解了模塊的工作原理,下面將會介紹為什么運行的更快。
作者:Lin Clark
編譯:胡子大哈
翻譯原文:http://huziketang.com/blog/posts/detail?postId=58c77641a6d8a07e449fdd24
英文原文:Creating and working with WebAssembly modules
轉(zhuǎn)載請注明出處,保留原文鏈接以及作者信息
本文是關(guān)于 WebAssembly 系列的第四篇文章(本系列共六篇文章)。如果你沒有讀先前文章的話,建議先讀這里。如果對 WebAssembly 沒概念,建議先讀這里(中文文章)。
WebAssembly 是除了 JavaScript 以外,另一種可以在網(wǎng)頁中運行的編程語言。過去如果你想在瀏覽器中運行代碼來對網(wǎng)頁中各種元素進行控制,只有 JavaScript 這一種選擇。
所以當人們談論 WebAssembly 的時候,往往會拿 JavaScript 來進行比較。但是它們其實并不是“二選一”的關(guān)系——并不是只能用 WebAssembly 或者 JavaScript。
實際上,我們鼓勵開發(fā)者將這兩種語言一起使用,即使你不親自實現(xiàn) WebAssembly 模塊,你也可以學習它現(xiàn)有的模塊,并它的優(yōu)勢來實現(xiàn)你的功能。
WebAssembly 模塊定義的一些功能可以通過 JavaScript 來調(diào)用。所以就像你通過 npm 下載 lodash 模塊并通過 API 使用它一樣,未來你也可以下載 WebAssembly 模塊并且使用其提供的功能。
那么就讓我們來看一下如何開發(fā) WebAssembly 模塊,以及如何通過 JavaScript 使用他們。
WebAssembly 處于哪個環(huán)節(jié)?在上一篇關(guān)于匯編的文章中,我介紹了編譯器是如何從高級語言翻譯到機器碼的。
那么在上圖中,WebAssembly 在什么位置呢?實際上,你可以把它看成另一種“目標匯編語言”。
每一種目標匯編語言(x86、ARM)都依賴于特定的機器結(jié)構(gòu)。當你想要把你的代碼放到用戶的機器上執(zhí)行的時候,你并不知道目標機器結(jié)構(gòu)是什么樣的。
而 WebAssembly 與其他的匯編語言不一樣,它不依賴于具體的物理機器??梢猿橄蟮乩斫獬伤?strong>概念機器的機器語言,而不是實際的物理機器的機器語言。
正因為如此,WebAssembly 指令有時也被稱為虛擬指令。它比 JavaScript 代碼更直接地映射到機器碼,它也代表了“如何能在通用的硬件上更有效地執(zhí)行代碼”的一種理念。所以它并不直接映射成特定硬件的機器碼。
瀏覽器把 WebAssembly 下載下來,然后先經(jīng)過 WebAssembly 模塊,再到目標機器的匯編代碼。
編譯到 .wasm 文件目前對于 WebAssembly 支持情況最好的編譯器工具鏈是 LLVM。有很多不同的前端和后端插件可以用在 LLVM 上。
提示:很多 WebAssembly 開發(fā)者用 C 語言或者 Rust 開發(fā),再編譯成 WebAssembly。其實還有其他的方式來開發(fā) WebAssembly 模塊。例如利用 TypeScript 開發(fā) WebAssembly 模塊,或者直接用 WebAssembly 文本也可以。
假設想從 C 語言到 WebAssembly,我們就需要 clang 前端來把 C 代碼變成 LLVM 中間代碼。當變換成了 LLVM IR 時,說明 LLVM 已經(jīng)理解了代碼,它會對代碼自動地做一些優(yōu)化。
為了從 LLVM IR 生成 WebAssembly,還需要后端編譯器。在 LLVM 的工程中有正在開發(fā)中的后端,而且應該很快就開發(fā)完成了,現(xiàn)在這個時間節(jié)點,暫時還看不到它是如何起作用的。
還有一個易用的工具,叫做 Emscripten。它通過自己的后端先把代碼轉(zhuǎn)換成自己的中間代碼(叫做 asm.js),然后再轉(zhuǎn)化成 WebAssembly。實際上它背后也是使用的 LLVM。
Emscripten 還包含了許多額外的工具和庫來包容整個 C/C++ 代碼庫,所以它更像是一個軟件開發(fā)者工具包(SDK)而不是編譯器。例如系統(tǒng)開發(fā)者需要文件系統(tǒng)以對文件進行讀寫,Emscripten 就有一個 IndexedDB 來模擬文件系統(tǒng)。
不考慮太多的這些工具鏈,只要知道最終生成了 .wasm 文件就可以了。后面我會介紹 .wasm 文件的結(jié)構(gòu),在這之前先一起了解一下在 JS 中如何使用它。
加載一個 .wasm 模塊到 JavaScript.wasm 文件是 WebAssembly 模塊,它可以加載到 JavaScript 中使用,現(xiàn)階段加載的過程稍微有點復雜。
function fetchAndInstantiate(url, importObject) { return fetch(url).then(response => response.arrayBuffer() ).then(bytes => WebAssembly.instantiate(bytes, importObject) ).then(results => results.instance ); }
如果想深入了解,可以在 MDN 文檔中了解更多。
我們一直在致力于把這一過程變得簡單,對工具鏈進行優(yōu)化。希望能夠把它整合到現(xiàn)有的模塊打包工具中,比如 webpack 中,或者整合到加載器中,比如 SystemJS 中。我們相信加載 WebAssembly 模塊也可以像加載 JavaScript 一樣簡單。
這里介紹 WebAssembly 模塊和 JavaScript 模塊的主要區(qū)別。當前的 WebAssembly 只能使用數(shù)字(整型或者浮點型)作為參數(shù)或者返回值。
對于任何其他的復雜類型,比如 string,就必須得用 WebAssembly 模塊的內(nèi)存操作了。如果是經(jīng)常使用 JavaScript,對直接操作內(nèi)存不是很熟悉的話,可以回想一下 C、C++ 和 Rust 這些語言,它們都是手動操作內(nèi)存。WebAssembly 的內(nèi)存操作和這些語言的內(nèi)存操作很像。
為了實現(xiàn)這個功能,它使用了 JavaScript 中稱為 ArrayBuffer 的數(shù)據(jù)結(jié)構(gòu)。ArrayBuffer 是一個字節(jié)數(shù)組,所以它的索引(index)就相當于內(nèi)存地址了。
如果你想在 JavaScript 和 WebAssembly 之間傳遞字符串,可以利用 ArrayBuffer 將其寫入內(nèi)存中,這時候 ArrayBuffer 的索引就是整型了,可以把它傳遞給 WebAssembly 函數(shù)。此時,第一個字符的索引就可以當做指針來使用。
這就好像一個 web 開發(fā)者在開發(fā) WebAssembly 模塊時,把這個模塊包裝了一層外衣。這樣其他使用者在使用這個模塊的時候,就不用關(guān)心內(nèi)存管理的細節(jié)。
如果你想了解更多的內(nèi)存管理,看一下我們寫的 WebAssembly 的內(nèi)存操作。
.wasm 文件結(jié)構(gòu)如果你是寫高級語言的開發(fā)者,并且通過編譯器編譯成 WebAssembly,那你不用關(guān)心 WebAssembly 模塊的結(jié)構(gòu)。但是了解它的結(jié)構(gòu)有助于你理解一些基本問題。
如果你對編譯器還不了解,建議先讀一下“WebAssembly 系列(三)編譯器如何生成匯編”這篇文章。
這段代碼是即將生成 WebAssembly 的 C 代碼:
int add42(int num) { return num + 42; }
你可以使用 WASM Explorer 來編譯這個函數(shù)。
打開 .wasm 文件(假設你的編輯器支持的話),可以看到下面代碼:
00 61 73 6D 0D 00 00 00 01 86 80 80 80 00 01 60 01 7F 01 7F 03 82 80 80 80 00 01 00 04 84 80 80 80 00 01 70 00 00 05 83 80 80 80 00 01 00 01 06 81 80 80 80 00 00 07 96 80 80 80 00 02 06 6D 65 6D 6F 72 79 02 00 09 5F 5A 35 61 64 64 34 32 69 00 00 0A 8D 80 80 80 00 01 87 80 80 80 00 00 20 00 41 2A 6A 0B
這是模塊的“二進制”表示。之所以用引號把“二進制”引起來,是因為上面其實是用十六進制表示的,不過把它變成二進制或者人們能看懂的十進制表示也很容易。
例如,下面是 num + 42 的各種表示方法。
代碼是如何工作的:基于棧的虛擬機如果你對具體的操作過程很好奇,那么這幅圖可以告訴你指令都做了什么。
從圖中我們可以注意到 加 操作并沒有指定哪兩個數(shù)字進行加。這是因為 WebAssembly 是采用“基于棧的虛擬機”的機制。即一個操作符所需要的所有值,在操作進行之前都已經(jīng)存放在堆棧中。
所有的操作符,比如加法,都知道自己需要多少個值。加 需要兩個值,所以它從堆棧頂部取兩個值就可以了。那么加指令就可以變的更短(單字節(jié)),因為指令不需要指定源寄存器和目的寄存器。這也使得 .wasm 文件變得更小,進而使得加載 .wasm 文件更快。
盡管 WebAssembly 使用基于棧的虛擬機,但是并不是說在實際的物理機器上它就是這么生效的。當瀏覽器翻譯 WebAssembly 到機器碼時,瀏覽器會使用寄存器,而 WebAssembly 代碼并不指定用哪些寄存器,這樣做的好處是給瀏覽器最大的自由度,讓其自己來進行寄存器的最佳分配。
WebAssembly 模塊的組成部分除了上面介紹的,.wasm 文件還有其他部分,通常把它們叫做部件。一些部件對于模塊來講是必須的,一些是可選的。
必須部分:
Type。在模塊中定義的函數(shù)的函數(shù)聲明和所有引入函數(shù)的函數(shù)聲明。
Function。給出模塊中每個函數(shù)一個索引。
Code。模塊中每個函數(shù)的實際函數(shù)體。
可選部分:
Export。使函數(shù)、內(nèi)存、表單(table)、全局變量等對其他 WebAssembly 或 JavaScript 可見,允許動態(tài)鏈接一些分開編譯的組件,即 .dll 的WebAssembly 版本。
Import。允許從其他 WebAssembly 或者 JavaScript 中引入指定的函數(shù)、內(nèi)存、表單或者全局變量。
Start。當 WebAssembly 模塊加載進來的時候,可以自動運行的函數(shù)(類似于 main 函數(shù))。
Global。聲明模塊的全局變量。
Memory。定義模塊用到的內(nèi)存。
Table。使得可以映射到 WebAssembly 模塊以外的值,如映射到 JavaScript 對象中。這在間接函數(shù)調(diào)用時很有用。
Data。初始化內(nèi)存。
Element。初始化表單(table)。
如果想要了解更多的部件,可以在“如何使用部件”中深入了解。
下文預告現(xiàn)在你已經(jīng)了解了 WebAssembly 模塊的工作原理,下面將會介紹為什么 WebAssembly 運行的更快。
我最近正在寫一本《React.js 小書》,對 React.js 感興趣的童鞋,歡迎指點。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/81987.html
摘要:但是為什么執(zhí)行的更快呢在這個系列文章中,我會為你解釋這一點。所以當人們說更快的時候,一般來講是與相比而言的。被人們廣為傳播的性能大戰(zhàn)在年打響。性能的提升使得的應用范圍得到很大的擴展?,F(xiàn)在通過,我們很有可能正處于第二個拐點。 作者:Lin Clark 編譯:胡子大哈 翻譯原文:http://huziketang.com/blog/posts/detail?postId=58ce8036...
摘要:并且于年月日,四個主要的瀏覽器一致同意宣布的版本已經(jīng)完成,即將推出一個瀏覽器可以搭載的穩(wěn)定版本。因此本文著重介紹為什么比更快。本文主要表達的是為什么應該是更快的。則不同,它是由幾大主要的瀏覽器廠商共同設計的。 作者:Alon Zakai 編譯:胡子大哈 翻譯原文:http://huziketang.com/blog/posts/detail?postId=58ce80d2a6d8a0...
摘要:現(xiàn)在,我們將會剖析的工作原理,而最重要的是它和在性能方面的比對加載時間,執(zhí)行速度,垃圾回收,內(nèi)存使用,平臺訪問,調(diào)試,多線程以及可移植性。目前,是專門圍繞和的使用場景設計的。目前不支持多線程。 原文請查閱這里,略有改動,本文采用知識共享署名 4.0 國際許可協(xié)議共享,BY Troland。 本系列持續(xù)更新中,Github 地址請查閱這里。 這是 JavaScript 工作原理的第六章...
摘要:前端每周清單專注前端領(lǐng)域內(nèi)容,以對外文資料的搜集為主,幫助開發(fā)者了解一周前端熱點分為新聞熱點開發(fā)教程工程實踐深度閱讀開源項目巔峰人生等欄目。利用降低三倍加載速度自推出之后,很多開發(fā)者都開始嘗試在小型項目中實踐,不過尚缺大型真實案例比較。 前端每周清單專注前端領(lǐng)域內(nèi)容,以對外文資料的搜集為主,幫助開發(fā)者了解一周前端熱點;分為新聞熱點、開發(fā)教程、工程實踐、深度閱讀、開源項目、巔峰人生等欄目...
閱讀 3396·2023-04-25 17:19
閱讀 700·2021-11-23 09:51
閱讀 1407·2021-11-08 13:19
閱讀 854·2021-09-29 09:34
閱讀 1754·2021-09-28 09:36
閱讀 1547·2021-09-22 14:59
閱讀 2775·2019-08-29 16:38
閱讀 2107·2019-08-26 13:40