天天看點

WebAssembly 學習筆記(一)WebAssembly 學習筆記(一)

WebAssembly 學習筆記(一)

以下内容都是我在學習WebAssembly的過程中根據官方的開發手冊的一些筆記。

這部分的内容是介紹WebAssembly的相關概念、該工具的目的、該工具想要解決的問題以及它如何可以在浏覽器中的渲染引擎中使用。

原文連結在這裡。

WebAssembly 概念

WebAssembly是一種新的代碼類型,可以在現代的web浏覽器中運作并提供新特性以及性能增益。它本身并不是設計為一種手寫代碼,而是作為其他源代碼如C,C++,Rust的一種高效的編譯對象(compilation target)。

這個工具對于web平台具有巨大的影響力,它它提供了一種方式讓各種源代碼可以在web上以接近原生速度(near-native speed),這種運作在web端的程式以前達不到這個效果。

此外,你甚至不需要知道如何去建立WebAssembly去使用它。WebAssembly子產品(WebAssembly modules)可以被導入到web (或者Node.js)App中,通過JavaScript暴露WebAssembly的功能來使用。

JavaScript 架構可以使用WebAssembly去獲得巨大的性能優點以及新的特性,同時仍然可以保證web 開發者可以輕松使用功能。

WebAssembly 目的

WebAssembly正在作為一個公開的标準,主要在W3C WebAssembly社群獲得廣泛認可,主要的目的如下:

  • 快速、高效、便捷。WebAssembly 代碼可以通過利用普遍的硬體性能以近似原生的速度跨平台運作。
  • 可讀性和可調試性–WebAssembly是一種低級别的彙編語言,但它确實有一種人類可讀的文本格式(其規範仍在定稿中),允許用手編寫、檢視和調試代碼。
  • 保持安全 - WebAssembly被指定為在安全的沙盒執行環境中運作。像其他網絡代碼一樣,它将執行浏覽器的同源和權限政策。
  • 不打斷web–WebAssembly的設計使它能與其他網絡技術很好地配合,并保持向後(backwards)相容。

WebAssembly如何适應web平台?

web可以被認為是有兩個部分。

  • 一個運作網絡應用程式代碼的虛拟機(VM),例如為你的應用程式提供動力的JavaScript代碼。
  • 一組web API,網絡應用可以調用它來控制網絡浏覽器/裝置的功能,并使事情發生(DOM、CSSOM、WebGL、IndexedDB、網絡音頻API等)。

ps:

DOM:

文檔對象模型 (DOM) 将 web 頁面與到腳本或程式設計語言連接配接起來。通常是指 JavaScript,但将 HTML、SVG 或 XML 文檔模組化為對象并不是 JavaScript 語言的一部分。

CSSOM:CSS 對象模型 (CSSOM) 是樹形形式的所有 CSS 選擇器和每個選擇器的相關屬性的映射,具有樹的根節點,同級,後代,子級和其他關系。

WebGL:WebGL(Web 圖形庫)是一個 JavaScript API,可在任何相容的 Web 浏覽器中渲染高性能的互動式 3D 和 2D 圖形,而無需使用插件。

IndexedDB:IndexedDB 是一種底層 API,用于在用戶端存儲大量的結構化資料(也包括檔案/二進制大型對象(blobs))。該 API 使用索引實作對資料的高性能搜尋。

曆史上,虛拟機一直隻能加載JavaScript。這對我們來說效果很好,因為JavaScript足夠強大,可以解決今天人們在web上遇到的大多數問題。然而,當我們試圖将JavaScript用于更密集的用例時,如3D遊戲、虛拟和增強現實(Virtual and Augmented Reality)、計算機視覺、圖像/視訊編輯,以及其他一些需要原生性能的領域時,我們遇到了性能問題(更多想法見WebAssembly用例)。

此外,下載下傳、解析和編譯非常大的JavaScript應用程式的成本可能是令人望而卻步的。移動和其他資源受限的平台會進一步放大這些性能瓶頸。

WebAssembly是一種不同于JavaScript的語言,但它并不打算作為一種替代。相反,它被設計為補充并與JavaScript一起工作,使Web開發者能夠利用這兩種語言的優點:

  • JavaScript是一種進階語言,具有足夠的靈活性和表現力來編寫網絡應用程式。它有很多優點–它是動态類型的,不需要編譯步驟,并且有一個巨大的生态系統,提供強大的架構、庫和其他工具。
  • WebAssembly是一種類似于彙編的低級語言,具有緊湊的二進制格式,運作時具有接近原生的性能,為C++和Rust等具有底層記憶體模型的語言提供了一個編譯目标,以便它們能夠在網絡上運作。(請注意,WebAssembly的高層次目标是在未來支援具有垃圾收集記憶體模型的語言)。

随着WebAssembly在浏覽器中的出現,我們前面談到的虛拟機現在将加載和運作兩種類型的代碼–JavaScript和WebAssembly。

不同的代碼類型可以根據需要(as required)互相調用–WebAssembly的JavaScript API用可以正常調用的JavaScript函數來包裝導出的WebAssembly代碼,而WebAssembly代碼可以導入并同步調用正常的JavaScript函數。事實上,WebAssembly代碼的基本機關被稱為子產品,WebAssembly子產品在許多方面與ES子產品對稱。

WebAssembly關鍵概念

要了解WebAssembly如何在浏覽器中運作,需要幾個關鍵概念。所有這些概念都在WebAssembly的JavaScript API中得到了1:1的反映。

  • 子產品(Module)。代表一個WebAssembly二進制檔案,已經被浏覽器編譯成可執行的機器代碼。子產品是無狀态的(stateless),是以,像Blob一樣,可以明确地在視窗和工作者之間共享(通過postMessage())。一個子產品就像ES子產品一樣聲明導入和導出。
  • 記憶體(Memory)。一個可調整大小的ArrayBuffer,包含由WebAssembly的底層(low-level)記憶體通路指令(access instructions)讀取和寫入的線性陣列。
  • 表(Table)。一個可調整大小的類型化的(typed)引用數組(例如函數),否則不能作為原始位元組存儲在Memory中(出于安全和可移植性的原因)。
  • 執行個體(Instance)。一個子產品與它在運作時使用的所有狀态配對,包括記憶體、表和一組導入值。一個執行個體就像一個ES子產品,它已經被加載到一個特定的全局中,并帶有一組特定的導入(imports)。

    PS:

    Blob: Blob(Binary Large Object)表示二進制類型的大對象。在資料庫管理系統中,将二進制資料存儲為一個單一個體的集合。Blob 通常是影像、聲音或多媒體檔案。在 JavaScript 中 Blob 類型的對象表示不可變的類似檔案對象的原始資料。

    postMessage:window.postMessage() 方法可以安全地實作跨源通信。通常,對于兩個不同頁面的腳本,隻有當執行它們的頁面位于具有相同的協定(通常為 https),端口号(443 為 https 的預設值),以及主機 (兩個頁面的模數 Document.domain設定為相同的值) 時,這兩個腳本才能互相通信。

JavaScript API為開發者提供了建立子產品、記憶體、表格和執行個體的能力。給定一個WebAssembly執行個體,JavaScript代碼可以同步調用它的出口(exports),這些出口被暴露為正常的JavaScript函數。任意的JavaScript函數也可以被WebAssembly代碼同步調用,通過傳遞這些JavaScript函數作為WebAssembly執行個體的導入(imports)。

由于JavaScript可以完全控制WebAssembly代碼的下載下傳、編譯和運作,JavaScript開發者甚至可以把WebAssembly看作隻是一個有效生成高性能函數的JavaScript功能。

在未來,WebAssembly子產品将像ES子產品一樣可以加載(使用

如何在自己的程式中使用WebAssembly

上面我們談到了WebAssembly添加到Web平台的原始基元(raw primitives):代碼的二進制格式和用于加載和運作這種二進制代碼的API。現在我們來談談如何在實踐中使用這些基元。

WebAssembly生态系統正處于萌芽階段;今後無疑會有更多的工具出現。現在,有四個主要的切入點:

  • 用Emscripten移植C/C++應用程式。
  • 直接在彙編層面(assembly level)上編寫或生成WebAssembly。
  • 編寫Rust應用程式并将WebAssembly作為其輸出目标。
  • 使用AssemblyScript,它看起來類似于TypeScript,并編譯成WebAssembly二進制。

對于每個切入點概述:

移植C/C++程式

建立WASM代碼的衆多選擇中,其中有兩種常見的方式,一是線上WASM彙編器,二是Emscripten。有許多線上WASM彙編器的選擇,如:

  • WasmFiddle
  • WasmFiddle++
  • WasmExplorer

    對于那些想知道從哪裡開始的人來說,這些都是很好的資源,但它們缺乏Emscripten的一些工具(tooling)和優化(optimizations)。

Emscripten工具能夠接受幾乎所有(just about any)的C/C++源代碼,并将其編譯成一個.wasm子產品,加上必要的JavaScript "膠水 "代碼(“glue” code)來加載和運作該子產品,以及一個HTML文檔來顯示代碼的結果。

WebAssembly 學習筆記(一)WebAssembly 學習筆記(一)

簡而言之(In a nutshell),這個過程是這樣的:

  1. Emscripten首先将C/C++輸入clang+LLVM, clang+LLVM是一個成熟的開源C/C++編譯器工具鍊,例如作為OSX上XCode的一部分。
  2. Emscripten将clang+LLVM的編譯結果轉化為一個.wasm二進制檔案。
  3. 目前,WebAssembly本身不能直接通路DOM(文檔對象模型,前面有解釋);它隻能調用JavaScript,傳遞整數和浮點原始資料類型。是以,要通路任何Web API,WebAssembly需要調用JavaScript,然後由JavaScript來調用Web API。是以,Emscripten建立了實作這一目标所需的HTML和JavaScript膠合代碼。
Note: There are future plans to allow WebAssembly to call Web APIs directly.(未來的發展)

JavaScript的膠合代碼并不像你想象的那樣簡單。首先,Emscripten實作了流行的C/C++庫,如SDL、OpenGL、OpenAL和POSIX的一部分。這些庫是以Web API的形式實作的(These libraries are implemented in terms of Web APIs),是以每個庫都需要一些JavaScript膠水代碼來連接配接WebAssembly和底層(underlying)Web API。

是以膠水代碼的一部分是實作C/C++代碼所使用的各個庫的功能。膠合代碼還包含調用上述WebAssembly JavaScript APIs來擷取、加載和運作.wasm檔案的邏輯。

生成的HTML文檔會加載JavaScript膠水檔案,并将stdout寫到

<textarea>

中。如果應用程式使用OpenGL,HTML還包含一個

<canvas>

元素,作為渲染目标。修改Emscripten的輸出非常容易,可以把它變成你需要的任何web app。

你可以在emscripten.org上找到關于Emscripten的完整文檔,還可以在Compiling from C/C++ to WebAssembly上找到實作工具鍊和将自己的C/C++應用編譯到wasm的指南。

直接寫WebAssembly 彙編指令

你想建立自己的編譯器,或者自己的工具,或者做一個在運作時生成WebAssembly的JavaScript庫?

與實體彙編語言一樣,WebAssembly二進制格式也有文本表示–兩者有1:1的對應關系。你可以用手寫或生成這種格式,然後用幾個WebAssembly文本到二進制的工具中的任何一個将其轉換為二進制格式。

關于如何做到這一點的簡單指南,請參閱我們的轉換WebAssembly文本格式為wasm的文章。

寫Rust 目标的WebAssembly

由于Rust WebAssembly工作組的不懈努力,編寫Rust代碼并編譯到WebAssembly上也是可能的。你可以從安裝必要的工具鍊開始,将一個Rust程式樣本編譯成WebAssembly的npm包,并在我們的Rust到WebAssembly的編譯文章中使用這個樣本。

使用AssemblyScript

對于那些想嘗試WebAssembly而不需要學習C或Rust的細節,停留在TypeScript這樣熟悉的語言中的網絡開發者來說,AssemblyScript将是最好的選擇。AssemblyScript将TypeScript的嚴格變體編譯成WebAssembly,允許Web開發者繼續使用他們熟悉的TypeScript相容工具–如Prettier、ESLint、VS Code intellisense等。你可以在https://www.assemblyscript.org/ 上檢視它的文檔。

總結

學習一的内容提供了WebAssembly的概述,解釋了為什麼WebAssembly這麼有用,它如何适應web以及你怎麼使用它,下一步開始着重學習利用Emscripten工具去編譯C/C++代碼。

繼續閱讀