Upgrade to Pro — share decks privately, control downloads, hide ads and more …

I was understanding WASM all wrong! 🤯

I was understanding WASM all wrong! 🤯

yujiosaka

July 01, 2024
Tweet

More Decks by yujiosaka

Other Decks in Technology

Transcript

  1. npm build –name “Yuji Isobe” 自己紹介 –alias –company –title –features

    –publications @yujiosaka NearMe VPoE WebDev cargo 自己紹介 2
  2. 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
  3. WASM Bytecode WASMのモジュール WASM Runtime WASMのランタイム ARM X86 Write once,

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

    once, run anywhere 一度書けばどこでも動く Intermediate Representation 中間表現(IR) 7
  5. My WASM journey WASMとの付き合い Expectation 期待 Technology Trigger 技術トリガー Slope

    of Enlightenment 啓蒙の坂 Plateau of Productivity 生産性の高原 Peak of Inflated Expectations 期待のピーク Trough of Disillusionment 幻滅の谷 Time 時間 8
  6. 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
  7. 10

  8. 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
  9. FFI’s overheads FFIのオーバーヘッド Machine Code 機械語 Marshal Input 入力のマーシャリング Marshal

    Output 出力のマーシャリング Copy コピー Copy コピー Switch cost スイッチングコスト Switch cost スイッチングコスト Compile コンパイル 13
  10. Peak of Inflated Expectations 期待のピーク Technology Trigger 技術トリガー Trough of

    Disillusionment 幻滅の谷 Time 時間 Plateau of Productivity 生産性の高原 Expectation 期待 My WASM journey WASMとの付き合い Slope of Enlightenment 啓蒙の坂 15
  11. 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
  12. 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
  13. Adding complexity 少し変更を加えてみる use wasm_bindgen::prelude::*; #[wasm_bindgen] pub fn add(a: u32,

    b: u32) -> u32 { let a = a.to_string().parse::<u32>().unwrap(); let b = b.to_string().parse::<u32>().unwrap(); return a + b; } 15.9 KB 😱 15.9キロバイト 18
  14. 19

  15. • 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
  16. 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
  17. 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
  18. Time 時間 Peak of Inflated Expectations 期待のピーク Technology Trigger 技術トリガー

    Trough of Disillusionment 幻滅の谷 Plateau of Productivity 生産性の高原 Expectation 期待 My WASM journey WASMとの付き合い Slope of Enlightenment 啓蒙の坂 24
  19. 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
  20. 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
  21. 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
  22. 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
  23. Function 関数 Opt/Sec OPS JS 32,172 FFI 225,918 WASM 1,465,898

    n = 670617279 (986 steps) n = 670617279 の場合(必要ステップ数: 986) 30
  24. n = 7 (16 steps) n = 7 の場合(必要ステップ数: 16)

    Function 関数 Opt/Sec OPS JS 21,627,204 FFI 297,403 WASM 77,805,618 31
  25. 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
  26. 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
  27. 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
  28. • 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
  29. Execution process 実行のプロセス Marshal Input 入力の マーシャリング Marshal Output 出力のマーシャリング

    WASM module is an intermediate representation (IR) before compilation WASMモジュールは、コンパイル前の中間表現 (IR) AOT (ahead-of-time) compile 事前(AOT)コンパイル 37
  30. • 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
  31. ULID Universally Unique Lexicographically Sortable Identifier 01HY5BC0ECSP300WP0YEZQ6C5C Timestamp 48 bits

    48ビットのタイムスタンプパート Randomness 80 bits 80ビットのランダムパート 43
  32. 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
  33. 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
  34. 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
  35. 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
  36. 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
  37. 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
  38. 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キロバイト
  39. (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