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