Slide 1

Slide 1 text

I was understanding WASM all wrong! 🤯 やはり俺のWASMに対する理解は間違っている by @yujiosaka

Slide 2

Slide 2 text

npm build –name “Yuji Isobe” 自己紹介 –alias –company –title –features –publications @yujiosaka NearMe VPoE WebDev cargo 自己紹介 2

Slide 3

Slide 3 text

3 WASM Web + Assembly

Slide 4

Slide 4 text

What is WASM? WASMとは? ● A low-level bytecode format 低レベルのバイトコードフォーマット ● A compilation target for languages like C, C++ and Rust C、C++、Rust等の言語のコンパイル対象 ● Enabling high performance web applications 高性能なウェブアプリケーションを実現する ● Increasingly used on the server side サーバーサイドでの利用が増加している 4

Slide 5

Slide 5 text

Write once, run anywhere 一度書けばどこでも動く Java Bytecode Javaのバイトコード JVM Javaバーチャルマシン ARM X86 Intermediate Representation 中間表現(IR) 5

Slide 6

Slide 6 text

WASM Bytecode WASMのモジュール WASM Runtime WASMのランタイム ARM X86 Write once, run anywhere 一度書けばどこでも動く Intermediate Representation 中間表現(IR) 6

Slide 7

Slide 7 text

WASM Runtime WASMのランタイム WASM Bytecode WASMのモジュール ARM X86 Wasmtime Write once, run anywhere 一度書けばどこでも動く Intermediate Representation 中間表現(IR) 7

Slide 8

Slide 8 text

My WASM journey WASMとの付き合い Expectation 期待 Technology Trigger 技術トリガー Slope of Enlightenment 啓蒙の坂 Plateau of Productivity 生産性の高原 Peak of Inflated Expectations 期待のピーク Trough of Disillusionment 幻滅の谷 Time 時間 8

Slide 9

Slide 9 text

WASM’s features WASMの特徴 ● Performance –Executes in near native speed 性能 – ほぼネイティブの速度で実行 ● Polyglot – Supports various programming languages 多言語対応 – 様々なプログラミング言語をサポート ● Cross-platform – Compatible with browser and native runtimes クロスプラットフォーム – ブラウザやネイティブのランタイムに対応 ● Security – Executes in a sandbox environment セキュリティ – サンドボックス環境で実行 ● Cross-architecture – Runs on multiple CPU architectures (e.g., ARM, x86) クロスアーキテクチャ – 複数のCPUアーキテクチャで実行(例: ARM、x86) 9

Slide 10

Slide 10 text

10

Slide 11

Slide 11 text

Initial perception 最初の感想 Front end フロント Back end バック Edge エッジ 11

Slide 12

Slide 12 text

Initial misconception 最初の思い込み WASM is a FFI (Foreign Function Interface) that works on browsers? 🤔 WASMはブラウザでも動作する FFI ? ● FFI lets code in one language directly call code in another language FFIによって一つの言語で書かれたコードが別の言語のコードを直接呼び出すことができる ● FFI is useful when performance is crucial and the logic is implemented in low-level languages like C, C++ and Rust FFIはパフォーマンスが重要かつ低レベル言語( C、C++、Rust等)でロジックが実装されている場合に有用 12

Slide 13

Slide 13 text

FFI’s overheads FFIのオーバーヘッド Machine Code 機械語 Marshal Input 入力のマーシャリング Marshal Output 出力のマーシャリング Copy コピー Copy コピー Switch cost スイッチングコスト Switch cost スイッチングコスト Compile コンパイル 13

Slide 14

Slide 14 text

Languages used for Numpy 14

Slide 15

Slide 15 text

Peak of Inflated Expectations 期待のピーク Technology Trigger 技術トリガー Trough of Disillusionment 幻滅の谷 Time 時間 Plateau of Productivity 生産性の高原 Expectation 期待 My WASM journey WASMとの付き合い Slope of Enlightenment 啓蒙の坂 15

Slide 16

Slide 16 text

My first WASM and Rust Rustで初めて書いたWASM use wasm_bindgen::prelude::*; #[wasm_bindgen] pub fn add(a: u32, b: u32) -> u32 { a + b } 214 byte 😇 214バイト 16

Slide 17

Slide 17 text

WebAssembly Text Format (WAT) WASMのテキスト形式(WAT) (module (type (;0;) (func (param i32 i32) (result i32))) (func (;0;) (type 0) (param i32 i32) (result i32) local.get 0 local.get 1 i32.add) (memory (;0;) 17) (export "memory" (memory 0)) (export "add" (func 0))) 17

Slide 18

Slide 18 text

Adding complexity 少し変更を加えてみる use wasm_bindgen::prelude::*; #[wasm_bindgen] pub fn add(a: u32, b: u32) -> u32 { let a = a.to_string().parse::().unwrap(); let b = b.to_string().parse::().unwrap(); return a + b; } 15.9 KB 😱 15.9キロバイト 18

Slide 19

Slide 19 text

19

Slide 20

Slide 20 text

● Avoid panics – Abort instead of panicking パニックを避ける – パニックせずに中断 ● Link Time Optimization (LTO) – Configure opt-level リンクタイム最適化 (LTO) – opt-levelを設定 ● Switch to wee_alloc – Replace the default allocator wee_allocに切り替え – デフォルトのアロケータを置き換える ● Avoid string formatting – Use static strings 文字列変更を避ける – 静的な文字列を使用 ● Manual tree-shaking – Use wasm-snip 手動ツリーシェイキング – wasm-snipを使用 Optimizing WASM module size WASMモジュールサイズの最適化 14.6 KB 14.6キロバイト 13.1 KB 13.1キロバイト 7.2 KB 7.2キロバイト 20

Slide 21

Slide 21 text

JavaScript’s bundlers JavaScriptのバンドラー等 Tree-shaking toolchains are richer in frontend フロントエンドの方が ツリーシェイキングが進んでいる 21

Slide 22

Slide 22 text

WASM module size in other languages 他の言語におけるWASMモジュールサイズ ● Go’s minimum WASM module size is 2 MB according to golang-wiki golang-wikiによれば、Goの最小WASMモジュールサイズは 2MB ● Python’s WASM module size 25 MB for add(a, b) using py2wasm py2wasmを用いると、 PythonのWASMモジュールサイズは 25MB 22

Slide 23

Slide 23 text

Community challenges コミュニティの挑戦 ● Zaplib post-mortem – A startup abandoning WASM due to the lack of performance enhancements and development complexity Zaplibのポストモーテム – パフォーマンスの期待外れや開発の複雑さのため、 WASMを放棄したスタートアップ ● Tree-shaking, the horticulturally misguided algorithm – A highlight in the immaturity of tree-shaking in WASM toolchains ツリーシェイキング、園芸的に誤った使い方のアルゴリズム – WASMのツリーシェイキングの未熟さを強調している 23

Slide 24

Slide 24 text

Time 時間 Peak of Inflated Expectations 期待のピーク Technology Trigger 技術トリガー Trough of Disillusionment 幻滅の谷 Plateau of Productivity 生産性の高原 Expectation 期待 My WASM journey WASMとの付き合い Slope of Enlightenment 啓蒙の坂 24

Slide 25

Slide 25 text

h3 / h3-js h3とh3-js ● h3 – Geospatial indexing system using a hexagonal grid. Buildings for Java, JavaScript, Python and others are available h3 – 六角形グリッドを使用した地理空間インデックス。 Java、JavaScript、Pythonなどで利用可能 ● h3-js – JavaScript version of h3. The core library is compiled from C to WASM using emscripten h3-js – h3のJavaScriptバージョン。 emscriptenを使ってCからWASMでコンパイルされている 25

Slide 26

Slide 26 text

Performance comparison パフォーマンスの比較 Library ライブラリ Function 関数 Resolution 解像度 Opt/Sec OPS h3 latLngToCell 9 1,376,101 h3-js latLngToCell 9 1,464,987 Where did the overhead go? 🤔 オーバーヘッドはどこに行った? 26

Slide 27

Slide 27 text

Further exploration さらなる検証 27

Slide 28

Slide 28 text

Collatz conjecture コラッツの問題 3 Step ステップ数 n 5 8 4 2 1 10 16 1. Start with any positive integer n 任意の正の整数nで開始 2. If n is even, divide it by 2 nが偶数なら2で割る 3. If n is odd, multiply it by 3 and add 1 nが奇数なら3倍して1を足す 4. Repeat the process until n becomes 1 nが1になるまでこのプロセスを繰り返す 28

Slide 29

Slide 29 text

Vanilla JavaScript 生のJavaScript function collatzSteps(n) { let counter = 0; while (n !== 1) { if (n % 2 === 0) { n /= 2; } else { n = 3 * n + 1; } counter++; } return counter; } pub fn collatz_steps(mut n: u64) -> u64 { let mut counter = 0; while n != 1 { if n % 2 == 0 { n /= 2; } else { n = 3 * n + 1; } counter += 1; } return counter; } Rust (FFI / WASM) Rust(FFIとWASM) 29

Slide 30

Slide 30 text

Function 関数 Opt/Sec OPS JS 32,172 FFI 225,918 WASM 1,465,898 n = 670617279 (986 steps) n = 670617279 の場合(必要ステップ数: 986) 30

Slide 31

Slide 31 text

n = 7 (16 steps) n = 7 の場合(必要ステップ数: 16) Function 関数 Opt/Sec OPS JS 21,627,204 FFI 297,403 WASM 77,805,618 31

Slide 32

Slide 32 text

Deep dive into WASM WASMをさらに深く掘り下げてみる 32

Slide 33

Slide 33 text

Shared Linear Memory 共有線形メモリー use wasm_bindgen::prelude::*; #[wasm_bindgen] pub fn add(a: u32, b: u32) -> u32 { a + b } JavaScript WASM Shared Linear Memory write a aを書き込む write b bを書き込む Read result 結果を読み込む Read a aを読み込む Read b bを読み込む Write result 結果を書き込む 33

Slide 34

Slide 34 text

Import JavaScript functions JavaScriptの関数をインポートする use wasm_bindgen::prelude::*; #[wasm_bindgen] extern “C” { #[wasm_bindgen(js_namespace = console)] fn log(s: &str) } #[wasm_bindgen] pub fn hello_world(name: &str) { log(&format!(“Hello, {}!”, name)); } 34

Slide 35

Slide 35 text

Sandbox environment サンドボックスの実行環境 Shared Linear Memory hello_world(name: &str) log helloWorld(name: string) console.log Export エクスポート Import インポート Write 書き込む Read 読み込む Call 呼び出す WASM Module WASMモジュール https://www.youtube.com/live/VGLnqkegX-g?si=pPkYm1lNvz6tZLNA 35

Slide 36

Slide 36 text

● Performance –Executes in near native speed 性能 – ほぼネイティブの速度で実行 ● Polyglot – Supports various programming languages 多言語対応 – 様々なプログラミング言語をサポート ● Cross-platform – Compatible with browser and native runtimes クロスプラットフォーム – ブラウザやネイティブのランタイムに対応 ● Security – Executes in a sandbox environment セキュリティ – サンドボックス環境で実行 ● Cross-architecture – Runs on multiple CPU architectures (e.g., ARM, x86) クロスアーキテクチャ – 複数のCPUアーキテクチャで実行(例: ARM、x86) WASM’s features WASMの特徴 36

Slide 37

Slide 37 text

Execution process 実行のプロセス Marshal Input 入力の マーシャリング Marshal Output 出力のマーシャリング WASM module is an intermediate representation (IR) before compilation WASMモジュールは、コンパイル前の中間表現 (IR) AOT (ahead-of-time) compile 事前(AOT)コンパイル 37

Slide 38

Slide 38 text

● Performance –Executes in near native speed 性能 – ほぼネイティブの速度で実行 ● Polyglot – Supports various programming languages 多言語対応 – 様々なプログラミング言語をサポート ● Cross-platform – Compatible with browser and native runtimes クロスプラットフォーム – ブラウザやネイティブのランタイムに対応 ● Security – Executes in a sandbox environment セキュリティ – サンドボックス環境で実行 ● Cross-architecture – Runs on multiple CPU architectures (e.g., ARM, x86) クロスアーキテクチャ – 複数のCPUアーキテクチャで実行(例: ARM、x86) WASM’s features WASMの特徴 38

Slide 39

Slide 39 text

Imagine Numpy has only one distribution… 想像するのです…Numpyのディストリビューションが 1つにまとまった世界を … 39 https://pypi.org/project/numpy/#files

Slide 40

Slide 40 text

Current perception ver. 2024 2024年時点の感想 Front end フロント Back end バック Edge エッジ 40 Wasmtime

Slide 41

Slide 41 text

Current perception ver. 202X 202X年時点の感想 Edge エッジ 41 Back end バック Front end フロント Wasmtime

Slide 42

Slide 42 text

40x Performance Increase 40倍のパフォーマンスを実現する 42

Slide 43

Slide 43 text

ULID Universally Unique Lexicographically Sortable Identifier 01HY5BC0ECSP300WP0YEZQ6C5C Timestamp 48 bits 48ビットのタイムスタンプパート Randomness 80 bits 80ビットのランダムパート 43

Slide 44

Slide 44 text

How to make ULID x40 faster ULID生成のパフォーマンスを 40倍にする方法 1. “Borrow” unit tests from the original JavaScript implementation オリジナルの JavaScript実装から単体テストを借りてくる 2. Translate JavaScript to Rust JavaScriptをRustに翻訳する 3. Bridge JavaScript and Rust JavaScriptとRustのブリッジコードを書く 44

Slide 45

Slide 45 text

Benchmarking ベンチマーク Function 関数 ulid https://github.com/ulid/javascript wa-ulid https://github.com/yujiosaka/wa-ulid Performance Increase 倍率 encodeTime 3,612,913 ops/sec 1,466,147 ops/sec 0.41x decodeTime 2,022,776 ops/sec 4,885,942 ops/sec 2.42x ulid 47,312 ops/sec 454,704 ops/sec 9.61x 45

Slide 46

Slide 46 text

1. Use efficient data structure 効率的なデータ構造を使用する // Before String::new(); // After Vec::::with_capacity(len); 46

Slide 47

Slide 47 text

2. Avoid unnecessary conversions 不要な変換やメモリの割り当てを避ける const ENCODING: &str = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"; // Before let mut chars = Vec::with_capacity(len); for index in 0..len { chars.push(ENCODING.chars().nth(index).unwrap()); } // After let mut chars = Vec::with_capacity(len); let encoding_bytes = ENCODING.as_bytes(); for index in 0..len { chars.push(encoding_bytes[index] as char); } 47

Slide 48

Slide 48 text

3. Cache computations 計算結果をキャッシュしておく const ENCODING_LEN: usize = 32; const TIME_LEN: usize = 10; // Before for i in 0..TIME_LEN { time += i as f64 * (ENCODING_LEN as u64).pow(index as u32) as f64; } // After let powers = [1.0, 32.0, ..., 35184372088832.0] for i in 0..TIME_LEN { time += i as f64 * powers[index]; } 48

Slide 49

Slide 49 text

Benchmarking ベンチマーク Function 関数 ulid https://github.com/ulid/javascript wa-ulid https://github.com/yujiosaka/wa-ulid Performance Increase 倍率 encodeTime 3,863,741 ops/sec 4,904,495 ops/sec 1.27x decodeTime 1,951,730 ops/sec 5,336,862 ops/sec 2.73x ulid 44,758 ops/sec 1,933,859 ops/sec 43.20x 49

Slide 50

Slide 50 text

Nothing comes without a price… 得る物あれば失う物あり ulid.js wa-ulid.js 4.7kb 110kb 50

Slide 51

Slide 51 text

Conclusion 結論 ● Unique architecture – WASM running on V8 works differently than FFI, designed to run faster and more efficiently 独自のアーキテクチャ – WASMがV8上で動作する際、 FFIとは異なりより速く効率的に動作するように設計されている ● Backend efficiency – WASM is highly effective for backend today, where performance is critical バックエンドの効率 – WASMはバックエンドのパフォーマンス向上に効果的で、性能が重要な場面で有用 ● Frontend potential – Currently limited by large binary sizes, but toolchains will address this バックエンドの効率 – WASMはバックエンドで非常に効果的であり、性能が重要な場合に有用 51

Slide 52

Slide 52 text

Blog post ブログ 52

Slide 53

Slide 53 text

Appendix 付録 53

Slide 54

Slide 54 text

Async Rust in WASM WASMにおけるAsync Rust 54 [dependencies] wasm-bindgen = "0.2.92" wasm-bindgen-futures = "0.4.42" #[wasm_bindgen] pub async fn add(a: u32, b: u32) -> u32 { a + b } 16KB 16キロバイト

Slide 55

Slide 55 text

(module (type (;0;) (func (param i32) (result i32))) (func (;0;) (type 0) (param i32) (result i32) (local i32 i32 i32) local.get 0 i32.const 0 i32.le_s if ;; label = @1 i32.const 0 return end local.get 0 i32.const 1 i32.sub local.set 2 loop ;; label = @1 local.get 2 i32.eqz if ;; label = @2 local.get 1 i32.const 1 i32.add return end Fib in Java bytecode JavaバイトコードによるFibonacci Fib in WASM WASMによるFibonacci 55 local.get 2 i32.const 2 i32.sub local.set 2 local.get 0 i32.const 1 i32.sub call 0 local.get 1 i32.add local.set 1 local.get 0 i32.const 2 i32.gt_u local.get 0 i32.const 2 i32.sub local.set 0 br_if 0 (;@1;) end local.get 1) (func (;1;) (type 0) (param i32) (result i32) local.get 0 call 0) (memory (;0;) 17) (export "memory" (memory 0)) (export "fib" (func 1))) public static int fib(int); Code: 0: iload_0 1: ifgt 6 4: iconst_0 5: ireturn 6: iload_0 7: iconst_1 8: if_icmpne 13 11: iconst_1 12: ireturn 13: iload_0 14: iconst_1 15: isub 16: invokestatic #2 // Method fib:(I)I 19: iload_0 20: iconst_2 21: isub 22: invokestatic #2 // Method fib:(I)I 25: iadd 26: ireturn

Slide 56

Slide 56 text

WASI and component model WASIとコンポーネントモデル 56 OS / Platform WASI WASI WIT WIT