WASM和機(jī)器學(xué)習(xí)

webassembly

什么是WebAssembly?

WebAssembly 是一種可以在現(xiàn)代Web瀏覽器中運(yùn)行的低級的類匯編語言,具有緊湊的二進(jìn)制格式,接近本機(jī)的性能運(yùn)行的。為了實(shí)現(xiàn)代碼緊湊WebAssembly 被設(shè)計成了不容易手寫,但是支持C、C++、C#、Golang、Rust 等源語言編寫代碼,使用相應(yīng)工具鏈翻譯源語言代碼。

webassembly

WebAssembly旨在補(bǔ)充并與JavaScript一起運(yùn)行,使用 WebAssemblyJavaScript API,你可以將WebAssembly模塊加載到 JavaScript 應(yīng)用程序中并在兩者之間共享功能。這使您可以在相同的應(yīng)用程序中利用WebAssembly的性能和功能以及 JavaScript 的表現(xiàn)力和靈活性。WebAssembly 模塊甚至可以導(dǎo)入Node.js應(yīng)用程序中來提供高性能的服務(wù)。

為什么需要 WebAssembly?

從歷史上看,Web瀏覽器的VM 只能加載 JavaScript。這對我們來說效果很好,因?yàn)?JavaScript 足夠強(qiáng)大,可以解決當(dāng)今人們在 Web 上遇到的大多數(shù)問題。然而,當(dāng)我們嘗試將 JavaScript 用于更密集的用例時,例如 3D 游戲、虛擬和增強(qiáng)現(xiàn)實(shí)、計算機(jī)視覺、圖像/視頻編輯以及許多其他需要本機(jī)性能的領(lǐng)域時,我們遇到了性能問題。

W3C WebAssembly 標(biāo)準(zhǔn)

WebAssembly Core Specification定義了一個低級虛擬機(jī),模擬運(yùn)行該虛擬機(jī)的許多微處理器的功能。 通過即時編譯或解析,WebAssembly 引擎使編寫的代碼可以以接近本地平臺的速度運(yùn)行。.wasm 資源類似于 Java .class 文件,它包含靜態(tài)數(shù)據(jù)和對該靜態(tài)數(shù)據(jù)進(jìn)行操作的代碼段。

WebAssembly Web API定義了一個基于 Promise 的接口,用于請求和執(zhí)行 .wasm 資源。 .wasm 資源的結(jié)構(gòu)經(jīng)過優(yōu)化,允許在檢索整個資源之前開始執(zhí)行。

WebAssembly JavaScript Interface提供了一個 JavaScript 接口,用于調(diào)用并將參數(shù)傳遞給 WebAssembly 函數(shù)。 在 Web 瀏覽器中,WebAssembly 與主機(jī)環(huán)境的交互都通過 JavaScript 進(jìn)行管理。

WebAssembly 關(guān)鍵概念

了解 WebAssembly 如何在瀏覽器中運(yùn)行需要幾個關(guān)鍵概念。所有這些概念都在WebAssembly JavaScript API中 1:1 反映。

Module : 表示已被瀏覽器編譯成可執(zhí)行機(jī)器代碼的 WebAssembly 二進(jìn)制文件。Module 是無狀態(tài)的,因此,像
Blob一樣,可以在 windows 和 worker 之間顯式共享(通過postMessage的方式)。Module 聲明導(dǎo)入和導(dǎo)出就像 ES 模塊一樣。

Memory : 一個可調(diào)整大小的ArrayBuffer,其中包含由WebAssembly的低級內(nèi)存訪問指令讀取和寫入的線性字節(jié)數(shù)組。

Table : 一個可調(diào)整大小的類型化引用數(shù)組(例如,函數(shù)),否則無法將其作為原始字節(jié)存儲在內(nèi)存中(出于安全和可移植性原因)。

Instance :一個模塊與它在運(yùn)行時使用的所有狀態(tài)配對,包括內(nèi)存、表和一組導(dǎo)入的值。Instance 就像一個 ES 模塊,它已通過一組特定的導(dǎo)入加載到特定的全局中。

JavaScript API 為開發(fā)人員提供了創(chuàng)建模塊(Module)、內(nèi)存(Memory)、表(Table)和實(shí)例(Instance)的能力。給定一個WebAssembly實(shí)例,JavaScript代碼可以同步調(diào)用它的導(dǎo)出,這些導(dǎo)出作為普通的JavaScript函數(shù)公開。通過將這些JavaScript函數(shù)作為導(dǎo)入傳遞給WebAssembly實(shí)例,WebAssembly代碼也可以同步調(diào)用任意JavaScript函數(shù)。

WebAssembly 工作流程

如果您使用C/C++,您可能使用過gcc或類似的編譯器。為了獲得Webassembly二進(jìn)制文件,我們需要一些其他特殊的編譯器??捎玫牟恢挂环N,但目前最好的一種是Emscripten它是開源的。
與"普通"匯編語言不同,Webassembly 不是特定于 CPU 的,因此可以在多個平臺上運(yùn)行,從手機(jī)等嵌入式系統(tǒng)到計算機(jī)的 CPU。
一旦我們用 Emscripten 編譯了我們的 C/C++ 代碼,我們就獲得了一個可以在瀏覽器上運(yùn)行的合適的 WASM 文件,很簡單吧?
實(shí)際上,還有更多細(xì)節(jié)需要考慮,但我們將逐步介紹它們。
WASM WebApp 工作的步驟是:

  1. 使用Emscripten編譯C/C++代碼,以獲得WASM二進(jìn)制文件。

  2. 使用JavaScript"膠水代碼"將WASM二進(jìn)制文件綁定到頁面。

  3. 運(yùn)行您的應(yīng)用程序并讓瀏覽器實(shí)例化您的WASM模塊、內(nèi)存和引用表。一旦完成,您的WebApp就可以完全運(yùn)行了。

webassembly

從C/C++代碼生成

c/c++

Emscripten 首先將 C/C++ 輸入到 clang+LLVM( C/C++ 編譯器工具鏈),將C/C++代碼編譯成.wasm 二進(jìn)制文件。

WebAssembly 本身目前無法直接訪問 DOM;它只能調(diào)用 JavaScript,傳入整數(shù)和浮點(diǎn)原始數(shù)據(jù)類型。因此,要訪問任何 Web API,WebAssembly 需要調(diào)用 JavaScript,然后 JavaScript 會調(diào)用 Web API。因此,Emscripten 創(chuàng)建了實(shí)現(xiàn)此目的所需的 HTML 和 JavaScript 粘合代碼。

要使 WebAssembly 可用,我們需要兩個主要組件:將代碼編譯成 WebAssembly 的工具鏈,以及可以執(zhí)行該輸出的瀏覽器。這兩個組件都依賴于完成WebAssembly 規(guī)范的進(jìn)展,但除此之外,很大程度上是獨(dú)立的工程工作。這種分離是一件好事,因?yàn)樗鼘⑹咕幾g器能夠發(fā)出在任何瀏覽器中運(yùn)行的 WebAssembly,并且無論是哪個編譯器生成它,瀏覽器都可以運(yùn)行 WebAssembly;換句話說,它允許多個工具鏈和多個瀏覽器協(xié)同工作,改善用戶選擇。分離還允許兩個組件的工作立即并行進(jìn)行。

git clone https://github.com/emscripten-core/emsdk.git

cd emsdk

./emsdk install latest

./emsdk activate latest

source ./emsdk_env.sh

在你的IDE中復(fù)制如下代碼:

// hello.c
#include <stdio.h>
int main() {
    printf("hello, world!\n");
    return 0;
}

然后編譯這個文件

emcc hello.c -o hello.js

您可以使用node.js運(yùn)行它們:

node hello.js

Emscripten 還可以生成用于測試嵌入式 JavaScript 的 HTML。要生成 HTML,請使用-o( output ) 命令并指定一個 html 文件作為目標(biāo)文件:

emcc hello.c -O3 -o hello.html

請注意,除了發(fā)出WebAssembly之外,我們在此模式下發(fā)出的構(gòu)建通常使用Emscripten工具鏈中的所有其他內(nèi)容:Emscripten的musl libc端口和訪問它的系統(tǒng)調(diào)用、OpenGL/WebGL 代碼、瀏覽器集成代碼、node.js 集成代碼,等等。因此,它支持Emscripten已經(jīng)做的所有事情,并且使用Emscripten的現(xiàn)有項(xiàng)目只需輕按一下開關(guān)即可切換到發(fā)出WebAssembly。這是讓現(xiàn)有的 C++ 項(xiàng)目在WebAssembly啟動時從WebAssembly中受益的關(guān)鍵部分,而他們幾乎不需要付出任何努力。

WebAssembly 在機(jī)器學(xué)習(xí)中的應(yīng)用

僅僅使用 WebAssembly 協(xié)議很難滿足機(jī)器學(xué)習(xí)所需的各種矩陣運(yùn)算所需的計算指令,因此有了很多不同補(bǔ)充協(xié)議實(shí)現(xiàn)協(xié)機(jī)器學(xué)習(xí)的功能,下面介紹三種比較主流的實(shí)現(xiàn)方式WebAssembly SIMD, WASI-NN, Apache TVM。

WebAssembly SIMD

SIMD代表單指令多數(shù)據(jù)。SIMD 指令是一類特殊的指令,它通過同時對多個數(shù)據(jù)元素執(zhí)行相同的操作來利用應(yīng)用程序中的數(shù)據(jù)并行性。音頻/視頻編解碼器、圖像處理器等計算密集型應(yīng)用程序都是利用 SIMD 指令來加速性能的應(yīng)用程序示例。大多數(shù)現(xiàn)代架構(gòu)支持 SIMD 指令的一些變體。

WebAssembly SIMD 提案中包含的一組操作由在各種平臺上得到很好支持的操作組成,并且被證明是高性能的。為此,目前的提議僅限于標(biāo)準(zhǔn)化固定寬度 128 位 SIMD 操作。

當(dāng)前的提議引入了一種新的v128值類型,以及對這種類型進(jìn)行操作的許多新操作。用于確定這些操作的標(biāo)準(zhǔn)是:

這些操作應(yīng)該在多個現(xiàn)代架構(gòu)中得到很好的支持。

在一個指令組內(nèi)的多個相關(guān)架構(gòu)中,性能優(yōu)勢應(yīng)該是積極的。

所選的一組操作應(yīng)盡量減少性能懸崖(如果有)。

在Chrome 中的 SIMD 支持

默認(rèn)情況下,Chrome 91 提供 WebAssembly SIMD 支持。確保使用最新版本的工具鏈,如下所述,以及最新的 wasm-feature-detect 來檢測支持最終版本規(guī)范的引擎。

在 Firefox 中啟用實(shí)驗(yàn)性 SIMD 支持

WebAssembly SIMD 在 Firefox 的標(biāo)志后面可用。目前它僅在 x86 和 x86-64 架構(gòu)上受支持。要在 Firefox 中試用 SIMD 支持,請轉(zhuǎn)到about:config并啟用javascript.options.wasm_simd. 請注意,此功能仍處于試驗(yàn)階段,正在開發(fā)中。

WASI-NN

如同WASM SIMD一樣,wasi-nn也允許WebAssembly 程序訪問主機(jī)提供的機(jī)器學(xué)習(xí)(ML)功能。為保持足夠的兼容性WASM SIMD只能使用CPU作為計算但愿,但是許多機(jī)器學(xué)習(xí)模型利用了一些其他輔助處理單元(例如GPU、TPU)。目前很難找到一種合適的方法使用WASM編譯到這樣的設(shè)備上的,因此在WASM基礎(chǔ)上提供一種使用這些設(shè)備的方法,wasi-nn就是為了實(shí)現(xiàn)這一目的而被設(shè)計出來的更高級別的 API 。

最后,將 ML 推理部署到 Wasm 運(yùn)行時已經(jīng)足夠困難了,而無需將翻譯的復(fù)雜性添加到較低級別的抽象中。因此,wasi-nn允許程序員直接部署模型,將針對適當(dāng)設(shè)備編譯模型的工作轉(zhuǎn)移到其他工具(例如OpenVINO、TF)。如果在某個時候有一個WASM提案可以使用機(jī)器的完整 ML 性能(例如靈活向量、GPU),那么可以想象,wasi-nn可以僅使用WASM原語"在后臺"實(shí)現(xiàn)——直到到那時,ML程序員仍然可以使用此處描述的方法執(zhí)行推理,并且如果發(fā)生這種切換,應(yīng)該會看到最小的變化。

經(jīng)過訓(xùn)練的機(jī)器學(xué)習(xí)模型都會被部署在具有不同體系結(jié)構(gòu)和操作系統(tǒng)類型的各類不同設(shè)備上。而借助于 wasi-nn,.wasm 文件便能夠以一種可移植的方式去執(zhí)行諸如"描述張量"及"執(zhí)行推理請求"等操作,而無視底層具體的指令集架構(gòu)(ISA)及操作系統(tǒng)(OS)差異。

tvm

Apache TVM

在Apache TVM深度學(xué)習(xí)編譯器中引入了WASM和WebGPU的支持。實(shí)驗(yàn)表明,在將模型部署到Web時,TVM的WebGPU后端可以接近本機(jī) GPU的性能。

tvm

計算是現(xiàn)代機(jī)器學(xué)習(xí)應(yīng)用程序的支柱之一。GPU的引入加快了深度學(xué)習(xí)的工作量,極大地提高了運(yùn)行速度。部署機(jī)器學(xué)習(xí)的需求不斷增長,瀏覽器已成為部署智能應(yīng)用程序的自然之所。

TensorFlow.js和ONNX.js將機(jī)器學(xué)習(xí)引入瀏覽器,但是由于缺乏對Web上GPU的標(biāo)準(zhǔn)訪問和高性能訪問的方式,他們使用了WASM SIMD優(yōu)化CPU計算,通過過WebGL提供GPU計算部分。但是WebGL缺少高性能著色學(xué)習(xí)所需的重要功能,例如計算著色器和通用存儲緩沖區(qū)。

WebGPU是下一代Web圖形標(biāo)準(zhǔn)。與最新一代的圖形API(例如Vulkan和Metal)一樣,WebGPU提供了一流的計算著色器支持。

為了探索在瀏覽器中使用WebGPU進(jìn)行機(jī)器學(xué)習(xí)部署的潛力,增強(qiáng)了深度學(xué)習(xí)編譯器Apache(incubating)TVM,以WASM(用于計算啟動參數(shù)并調(diào)用設(shè)備啟動的主機(jī)代碼)和WebGPU(用于設(shè)備)為目標(biāo)。使用TVM在Web上部署機(jī)器學(xué)習(xí)應(yīng)用程序時,仍能接近GPU的本機(jī)性能。

tvm

WebGPU的傳統(tǒng)工作流程是為深度神經(jīng)網(wǎng)絡(luò)(矩陣乘法和卷積)中的原始算子編寫著色器,然后直接優(yōu)化性能。這是現(xiàn)有框架(TensorFlow.js)最新版本中使用了這種工作模式。

TVM則與之相反,采用了基于編譯的方法。TVM自動從TensorFlow,Keras,PyTorch,MXNet和ONNX等高級框架中提取模型,使用機(jī)器學(xué)習(xí)驅(qū)動的方法自動生成低級代碼,在這種情況下,將以SPIR-V格式計算著色器。然后可以為可部署模塊生成的代碼打包。

編譯的方法的一個重要優(yōu)點(diǎn)是基礎(chǔ)架構(gòu)的重用。通過重用基礎(chǔ)結(jié)構(gòu)來優(yōu)化CUDA,Metal和OpenCL等本機(jī)平臺的GPU內(nèi)核,能夠輕松地以Web為目標(biāo)。如果WebGPU API到本機(jī)API的映射有效,可以通過很少的工作獲得類似的性能。更重要的是,AutoTVM基礎(chǔ)架構(gòu),能夠針對特定模型專門化計算著色器,從而能夠?yàn)楦信d趣的特定模型生成最佳的計算著色器。

TVM已經(jīng)有Vulkan的SPIR-V目標(biāo),使用LLVM生成主機(jī)代碼。可以僅將二者的用途重新生成設(shè)備和主機(jī)程序。

主要挑戰(zhàn)是runtime。需要一個runtime來加載著色器代碼,并使主機(jī)代碼對話能夠正確地與著色器通信。TVM具有最低的基于C ++的runtime。構(gòu)建了一個最小的Web runtime庫,生成的著色器和主機(jī)驅(qū)動代碼鏈接,生成一個WASM文件。但是,此WASM模塊仍然包含兩個未知的依賴項(xiàng):

runtime需要調(diào)用系統(tǒng)庫調(diào)用(malloc,stderr)。

wasmruntime需要與WebGPU驅(qū)動程序進(jìn)行交互(在Javascript中,WebGPU API是the first-class citizen)。

WASI是解決第一個問題的標(biāo)準(zhǔn)解決方案。盡管網(wǎng)絡(luò)上還沒有成熟的WASI,使用Emscripten生成類似WASI的庫,提供這些系統(tǒng)庫。

通過在TVM的JS runtime內(nèi)部構(gòu)建WebGPU runtime來解決第二個問題,在調(diào)用GPU代碼時,從WASM模塊中回調(diào)這些功能。使用TVM runtime系統(tǒng)中的PackedFunc機(jī)制,可以通過將JavaScript閉包傳遞到WASM接口,直接公開高級runtime原語。這種方法將大多數(shù)runtime代碼保留在JavaScript中,隨著WASI和WASM支持的成熟,可以將更多JS代碼引入WASM runtime。

未來的某個時候,當(dāng)WebGPU成熟,通過WASI標(biāo)準(zhǔn)化時,可以將其定位為WebGPU的本機(jī)API,使用WebGPU的獨(dú)立WASM應(yīng)用程序。

Reference

WebAssembly-Wiki

WebAssembly

WebAssembly – Where is it going?

WebAssembly SIMD

WasmEdge

WebGPU-wiki

Apache TVM

更多技術(shù)分享瀏覽我的博客:

https://thierryzhou.github.io

本文由mdnice多平臺發(fā)布

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容