(轉)WASM(WebAssember)快速了解第四篇——創建和使用WebAssembly模塊


這是有關WebAssembly的系列文章的第四部分,如果您還沒有閱讀其他文章,我們建議從頭開始

WebAssembly是一種在網頁上運行JavaScript以外的編程語言的方法。過去,當您想在瀏覽器中運行代碼以與網頁的不同部分進行交互時,唯一的選擇就是JavaScript。

因此,當人們談論WebAssembly的速度很快時,Apple與Apple的比較就是JavaScript。但這並不意味着這是兩種情況,要么您正在使用WebAssembly,要么您正在使用JavaScript。

實際上,我們期望開發人員將在同一應用程序中同時使用WebAssembly和JavaScript。即使您自己不編寫WebAssembly,也可以利用它。

WebAssembly模塊定義可從JavaScript使用的功能。因此,就像您今天從npm下載lodash之類的模塊並調用其API中的函數一樣,將來您將能夠下載WebAssembly模塊。

因此,讓我們看看如何創建WebAssembly模塊,然后如何從JavaScript中使用它們。

WebAssembly適合哪里?

在有關匯編的文章中,我談到了編譯器如何采用高級編程語言並將其轉換為機器代碼。

WebAssembly在哪里適合這張圖片?

您可能會認為這只是目標匯編語言中的另一種。這是真的,只是每種語言(x86,ARM)對應於特定的計算機體系結構。

當您通過網絡交付要在用戶計算機上執行的代碼時,您不知道代碼將在您的目標體系結構上運行。

因此,WebAssembly與其他類型的程序集有所不同。它是概念性機器的機器語言,而不是實際的物理機器。

因此,WebAssembly指令有時稱為虛擬指令。與JavaScript源代碼相比,它們對機器代碼的映射要直接得多。它們代表了可以在常見的流行硬件上有效完成的工作的一種交集。但是它們並不是直接映射到一種特定硬件的特定機器代碼。

瀏覽器下載WebAssembly。然后,它可以使從WebAssembly到目標計算機的匯編代碼的距離很短。

編譯為.wasm

當前對WebAssembly的支持最多的編譯器工具鏈稱為LLVM。LLVM可以插入許多不同的前端和后端。

注意:大多數WebAssembly模塊開發人員將使用C和Rust等語言進行編碼,然后編譯為WebAssembly,但是還有其他方法可以創建WebAssembly模塊。例如,有一個實驗性工具可以幫助您使用TypeScript構建WebAssembly模塊,或者您可以直接在WebAssembly的文本表示形式中進行編碼

假設我們想從C到WebAssembly。我們可以使用clang前端從C到LLVM中間表示。一旦進入LLVM的IR,LLVM就會理解它,因此LLVM可以執行一些優化。

為了從LLVM的IR(中間表示)過渡到WebAssembly,我們需要一個后端。LLVM項目中正在進行中的一項。后端是其中的大部分方式,應盡快完成。但是,要使其今天開始工作可能會很棘手。

還有另一個名為Emscripten的工具,此工具現在更易於使用。它有自己的后端,可以通過編譯到另一個目標(稱為asm.js)並將其轉換為WebAssembly來生成WebAssembly。但是,它在后台使用LLVM,因此您可以在Emscripten的兩個后端之間切換。

Emscripten包括許多其他工具和庫,可用於移植整個C / C ++代碼庫,因此,它比編譯器更像是軟件開發人員工具包(SDK)。例如,系統開發人員習慣於擁有可以讀取和寫入的文件系統,因此Emscripten可以使用IndexedDB模擬文件系統。

無論使用哪種工具鏈,最終結果都是以.wasm結尾的文件。我將在下面解釋有關.wasm文件結構的更多信息。首先,讓我們看看如何在JS中使用它。

在JavaScript中加載.wasm模塊

.wasm文件是WebAssembly模塊,可以將其加載到JavaScript中。到目前為止,加載過程有點復雜。

function fetchAndInstantiate(url, importObject) {
  return fetch(url).then(response =>
    response.arrayBuffer()
  ).then(bytes =>
    WebAssembly.instantiate(bytes, importObject)
  ).then(results =>
    results.instance
  );
}

您可以在我們的文檔中更深入地了解這一點

我們正在努力簡化此過程。我們希望對工具鏈進行改進,並與現有的模塊捆綁器(如webpack)或加載器(如SystemJS)集成。我們相信,加載WebAssembly模塊就像加載JavaScript模塊一樣容易。

但是,WebAssembly模塊和JS模塊之間存在主要區別。當前,WebAssembly中的函數只能將數字(整數或浮點數)用作參數或返回值。

對於任何更復雜的數據類型(例如字符串),您必須使用WebAssembly模塊的內存。

如果您主要使用JavaScript,那么直接訪問內存就不那么熟悉了。C,C ++和Rust等性能更高的語言往往具有手動內存管理功能。WebAssembly模塊的內存模擬了您會在這些語言中找到的堆。

為此,它使用JavaScript中的ArrayBuffer。數組緩沖區是字節數組。因此,數組的索引用作內存地址。

如果要在JavaScript和WebAssembly之間傳遞字符串,請將字符轉換為等效的字符代碼。然后將其寫入內存陣列。由於索引是整數,因此可以將索引傳遞給WebAssembly函數。因此,字符串的第一個字符的索引可以用作指針。

可能正在開發要由Web開發人員使用的WebAssembly模塊的任何人都將圍繞該模塊創建包裝器。這樣,您作為模塊的使用者就無需了解內存管理。

如果您想了解更多信息,請查閱有關使用WebAssembly的內存的文檔

.wasm文件的結構

如果您要使用高級語言編寫代碼,然后將其編譯為WebAssembly,則無需了解WebAssembly模塊的結構。但這有助於了解基本知識。

如果您還沒有的話,我們建議您閱讀有關匯編文章(該系列的第3部分)。

這是一個C函數,我們將其轉換為WebAssembly:

int add42(int num) {
  return num + 42;
}

您可以嘗試使用WASM Explorer來編譯此功能。

如果打開.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樣子。

代碼的工作方式:堆棧機

如果您想知道,以下是這些說明的操作。

您可能已經注意到,該add操作沒有說明其值應從何而來。這是因為WebAssembly是稱為堆棧機的示例。這意味着在執行操作之前,操作需要的所有值都在堆棧中排隊。

像這樣的操作項add知道他們需要多少個值。由於add需要兩個值,因此它將從堆棧頂部獲取兩個值。這意味着該add指令可以很短(一個字節),因為該指令不需要指定源或目標寄存器。這樣可以減小.wasm文件的大小,這意味着下載時間更少。

即使WebAssembly是根據堆棧計算機指定的,但這也不是它在物理計算機上的工作方式。當瀏覽器將WebAssembly轉換為運行瀏覽器的機器的機器代碼時,它將使用寄存器。由於WebAssembly代碼未指定寄存器,因此它為瀏覽器提供了更大的靈活性,可以為該計算機使用最佳的寄存器分配。

模塊各節

除了add42函數本身,.wasm文件中還有其他部分。這些稱為段。對於任何模塊,某些段都是必需的,而某些則是可選的。

需要:

  1. Input,包含此模塊中定義的功能和所有導入功能的功能簽名。
  2. Function,為該模塊中定義的每個函數提供索引。
  3. Code,該模塊中每個功能的實際功能主體。

可選的:

  1. Export,使功能,內存,表和全局變量可用於其他WebAssembly模塊和JavaScript。這允許將單獨編譯的模塊動態鏈接在一起。這是WebAssembly的.dll版本。
  2. Import,指定要從其他WebAssembly模塊或JavaScript導入的函數,內存,表和全局變量。
  3. Start,加載WebAssembly模塊時將自動運行的功能(基本上像主要功能一樣)。
  4. Global,聲明模塊的全局變量。
  5. Memory。定義此模塊將使用的內存。
  6. Table,可以映射到WebAssembly模塊外部的值,例如JavaScript對象。這對於允許間接函數調用特別有用。
  7. Data,初始化導入的或本地的內存。
  8. Element,初始化導入的表或本地表。

有關各節的更多信息,這是這些節的工作原理的深入解釋

接下來的是

既然您知道如何使用WebAssembly模塊,那么讓我們看看為什么WebAssembly很快

轉自:https://hacks.mozilla.org/2017/02/creating-and-working-with-webassembly-modules/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM