Open8

WebAssembly入門者向け情報をまとめてみる(基礎編)

tetter / Tetta Maedatetter / Tetta Maeda

WebAssembly (Wasm) とは

W3Cが2019年に標準化した、Webブラウザ上で動作する第4の言語。

Following HTML, CSS and JavaScript, WebAssembly becomes the fourth language for the Web which allows code to run in the browser

https://www.w3.org/press-releases/2019/wasm/

C/C++やRustを含むメジャーなプログラミング言語のコードを変換することで生成でき、そのコードの通りにブラウザ上で動作させることができる。

対応している任意のプログラミング言語を中間言語へと変換し、Wasmファイル(.wasm)として保存する。実行時にブラウザによってWebアプリケーションの一部としてダウンロードされ、ブラウザによって検証された後にプロセッサに応じた機械語へと変換されて動作する。

JavaScript同様VM上で動作し、ArrayBufferをメモリとして扱うためPCのリソースに直接アクセスすることはなくセキュアである。

また、WasmにはJavaScriptより高速に動作するという特徴があるため、パフォーマンスがネックになりやすい画像処理やAI処理をブラウザ上で動作させる目的で活用されることが多い。

同じ処理を実行する場合、処理時間は概ね以下のような関係になる。
Native < ブラウザ (Wasm) <<< ブラウザ (JavaScript)

tetter / Tetta Maedatetter / Tetta Maeda

Wasmの得意・不得意

大きく分けるとWasmには以下2つのメリットがある。

  1. 実行速度がJavaScriptより速い
  2. JavaScript以外の言語をブラウザへ持ち込める

すなわち、以下のようなことが得意である。

  • 画像処理や機械学習などの実行に時間がかかる処理をリアルタイム実行すること
  • これまで別言語で書いていたコードをJavaScriptへ書き換えることなく再利用する

一方、以下のようなことはWasmには不可能である。

  • DOMの直接的な操作
  • ブラウザAPIの実行

そのため、WasmでJavaScriptを完全に置き換えることはできず、2つを組み合わせて使用し、JavaScriptが苦手なところを補うような立ち位置になると考えられる。

tetter / Tetta Maedatetter / Tetta Maeda

対応言語

対応言語はすでに50種類以上あるようだが、その大半はまだ安定していないらしい。
https://github.com/appcypher/awesome-wasm-langs?tab=readme-ov-file

良く商用利用されている言語

  • C
  • C++
  • Rust
  • Go

生成方法の違い

Wasmの生成方法は言語によって異なり、主に以下の3種類:

  • Emscriptenを使用する
    • C, C++, etc.
  • 内部のコンパイラで生成する
    • Rust, Go, Kotlin/Native, etc.
  • 外部のツールを使用する
    • Python, C#, TypeScript, etc

https://scrapbox.io/mrsekut-p/WebAssemblyをサポートする言語

実行方法の違い

Wasmの実行は言語によって2種類ある。

  • Wasmをターゲットとしてコード自体をコンパイルする
    • C/C++, Rust, Go, etc.
  • Wasmとして出力されたインタープリタを動作させて言語を動作させる
    • Python, Perl, LUA, etc.

Wasmの<高速>というメリットを存分に活かせるのは前者である。
後者について、例えばPythonであればPythonのランタイム (Pyodide) をWasm化してブラウザ上で動かし、その上でPythonを動作させる形式になる。
https://www.creationline.com/tech-blog/58019

Pyodide公式はNative Pythonと比べて3〜5倍遅く、Wasm化されたCのコードよりも2〜2.5倍遅いとアナウンスしており、動作速度は出ない。

Across benchmarks Pyodide is currently around 3x to 5x slower than native Python.
At the same time, C code compiled to WebAssembly typically runs between near native speed and 2x to 2.5x times slower (Jangda et al. 2019 PDF).

https://pyodide.org/en/stable/project/roadmap.html#improve-performance-of-python-code-in-pyodide

tetter / Tetta Maedatetter / Tetta Maeda

Wasmのコンポーネント

ファイル構成

これまでのWebアプリケーションのファイル構成を上だとしたら、Wasmを使用したWebアプリケーションは下のイメージ。

Wasmを使用しないWebアプリケーション
.
├── index.html
├── style.css
└── index.js
Wasmを使用するWebアプリケーション
.
├── index.html
├── style.css
├── index.js
├── process.js    // 新たに必要
└── process.wasm  // 新たに必要

グルーコード (process.js) はWebAssembly APIに従ってWasmモジュールの関数実行を代行してくれる役割を果たす。Emscriptenなどのコンパイルツールを使えば自動で生成できる。

なお、process.jsとindex.jsが別ファイルに分かれている必要はない。

ブラウザ上での動作

下図のJavaScript Codeは前述のprocess.jsとindex.jsが結合されたもの。
JavaScript CodeでWebAssembly Moduleをインスタンス化し、Wasmが使用するLinear Memoryを割り当てる。

引用: https://youtu.be/RcHER-3gFXI?si=uf1RkACjoqGdpDfu

tetter / Tetta Maedatetter / Tetta Maeda

登場の背景

2008年

  • JITコンパイラが誕生し、3つのブラウザエンジンChrome-V8, Firefox-TraceMonkey, Safari-SquirrelFish Extremeがそれぞれ高速化
    • ブラウザ高速化時代の幕開け

2010年

NativeClient (NaCl) プロジェクト登場
  • Chromeが立ち上げた高速化と安全性を両立するためのプロジェクト
  • 独自のプロセスサンドボックスの上でNativeコードを実行する
  • ゲーム産業などで注目を集めた
    • Unity, MonoがNaClに移植
    • Bastionを含む多数のゲームがNaClに移植
  • NaClはブラウザ拡張をするためのNPAPI (Netscape Plugin API) に依存していた
    • 例えば、動画や音声のサポートでAdobe Flash Playerを導入したりしている
  • ただ、3つの理由からNaClは天下を取れなかった
    • プラグインAPIがすでに衰退期に入っていた (2014年に廃止された)
    • サンドボックスからWebAPIを呼び出せず、新規のWebAPIと重複したプラグインAPIを用意する必要があった
    • プロセッサ (x86, ARM, …) 毎にバイナリを用意する必要があり、移植性に問題があった
      • これに関してはPortable Native Client (PNaCl) が発表されて改善される

2012年

Emscripten登場
  • Mozilla FirefoxのAlon Zakaiが開発
  • C++のコードを (LLVM IRを介して) JavaScriptへ変換するためのコンパイラ
  • かなり注目されたが、この時点では高速化は不十分だった
    • JITコンパイラの最適化は予測できないので安定しない

2013年

asm.js登場
  • Mozilla FirefoxのAlonとLuke Wagner、Davide Hermanが開発

  • JavaScriptの型は予測できないので事前に型ヒントを付けるというコンセプト

    function AsmModule() {
      "use asm";               // エンジンにasm.jsの使用を知らせるフラグ
      return {
          add: function(a, b) {
            a = a | 0;           // 変数が32ビット整数であることを示すタイプヒント
            b = b | 0;
            return (a + b) | 0;  // 戻り値が32ビット整数であることを示すタイプヒント
          }
      }
    }
    
    • clangでビルドしたNativeコードには及ばないが、かなりの高速化を実現した
    • Unity (WebGL) もこれに移植
    • サイズが大きくてパースに時間がかかるという課題があった
  • GoogleとMozillaがWebを高速化するために協力し、月イチでMeetupを実施

2014年

  • FirefoxのLuke WagnerやChromeのBen TitzerがWebにとって最適なソリューションはBytecodeを活用した設計であるという共通認識を得る

2015年

2017年

  • WebAssemblyがFirefox, Chrome, Safari, Edgeへ搭載される

2018年

  • UnityがWebGLの出力形式をWasmへ切り替えた

2019年

  • WebAssembly 1.0 (MVP) が標準化される
  • WASIの仕様の策定が開始される

https://zenn.dev/hodagi/articles/4925afbeb3c4dc

tetter / Tetta Maedatetter / Tetta Maeda

WASIについて

ランタイムを用意することでWasmをブラウザ以外で動作させることもできる。

Bytecode Allianceがブラウザ外でWasmを動作させるための標準仕様WASI (WebAssembly System Interface) を標準化中。Bytecode AllianceにはMicrosoft, Google, Amazon, Arm, Intel, Ciscoなどの名だたる企業が参加しており注目度が高いことがわかる。

現状リリースされているWASIランタイムにはさまざまなものがあるが、有名どころはWasmtime, Wasmer, WasmEdgeあたり。

Docker FounderのSolomon Hykesが「2008年にWASIがあればDockerはいらなかった」と言及しており、Docker DesktopでのWasm対応を進めている。

Wasmは今後サーバ上で利用するコンテナの代替としても利用されていく可能性が高い。

引用: https://www.docker.com/blog/docker-wasm-technical-preview/