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

20210825_ossx

chikoski
September 16, 2021

 20210825_ossx

chikoski

September 16, 2021
Tweet

More Decks by chikoski

Other Decks in Technology

Transcript

  1. プラグインとしての WebAssembly
    [email protected]

    View Slide

  2. WebAssembly (Wasm)の概観
    プラグインシステムの基礎技術としての Wasm
    Outline

    View Slide

  3. カテゴリー 利用例 説明
    Web アプリ Acrobat PDF 編集ツール
    Autocad CAD
    Lightroom 画像編集
    Squoosh 画像圧縮、変換ツール
    Zoom ビデオミーティング
    サーバーレス環境 Cloudflare Workers
    [email protected]
    Envoy Service Proxy
    Istio Service mesh。Envoy を利用
    Krustlet Kubernetes 上で動く Wasm 実行環境

    View Slide

  4. エコシステム パフォーマンス

    View Slide

  5. ● Shopify App
    ● Microsoft Flight Simulator
    ● Envoy / Proxy-Wasm
    ● Vector by Datadog
    プラグイン としての Wasm

    View Slide

  6. Shopify
    “Shopify is a subscription-based
    software that allows anyone to set
    up an online store and sell their
    products”, What is Shopify

    View Slide

  7. View Slide

  8. https://shopify.dev/apps/getting-started

    View Slide

  9. https://shopify.engineering/shopify-webassembly

    View Slide

  10. Performance × Flexibility × Security
    ・サーバー内での実行
    ・ネイティブコードの事前生成
    ・生成したコードのキャッシュ
    ・プログラムなのでユースケースを広くカバー
    ・いろんな言語で実装可能
    ・メモリ保護
    ・安全な実行コードの生成
    ・アイソレーション

    View Slide

  11. WebAssembly 101

    View Slide

  12. コンパイラターゲット

    View Slide

  13. 0061736D01000000010A026000006002
    7F7F017F030302000104050170010101
    05030100020615037F01418088040B7F
    00418088040B7F004180080B072B0406
    6D656D6F727902000B5F5F686561705F
    6261736503010A5F5F646174615F656E
    6403020361646400010A0C0202000B07
    00200120006A0B0020046E616D650119
    0200115F5F7761736D5F63616C6C5F63
    746F72730103616464
    #define WASM_EXPORT
    __attribute__((visibility("default")))
    WASM_EXPORT
    int add(int a, int b) { return a + b; }
    #[no_mangle]
    pub extern
    fn add(x: i32, y: i32) -> i32 { x + y }
    export function add(x: i32, y: i32): i32 {
    return x + y;
    }

    View Slide

  14. 0061736D01000000010A026000006002
    7F7F017F030302000104050170010101
    05030100020615037F01418088040B7F
    00418088040B7F004180080B072B0406
    6D656D6F727902000B5F5F686561705F
    6261736503010A5F5F646174615F656E
    6403020361646400010A0C0202000B07
    00200120006A0B0020046E616D650119
    0200115F5F7761736D5F63616C6C5F63
    746F72730103616464
    (module
    (type $t0 (func))
    (type $t1 (func (param i32 i32) (result i32)))
    (func $__wasm_call_ctors (type $t0))
    (func $add (export "add") (type $t1)
    (param $p0 i32) (param $p1 i32) (result i32)
    get_local $p1
    get_local $p0
    i32.add)
    (table $T0 1 1 anyfunc)
    (memory $memory (export "memory") 2)
    (global $g0 (mut i32) (i32.const 66560))
    (global $__heap_base (export "__heap_base") i32 (i32.const
    66560))
    (global $__data_end (export "__data_end") i32 (i32.const 1024)))

    View Slide

  15. スタックベースの仮想マシン

    View Slide

  16. 1 + 2 + 3 = ? i32.const 1
    i32.const 2
    i32.const 3
    i32.add
    i32.add

    View Slide

  17. 1 + 2 + 3 = ? i32.const 1
    i32.const 2
    i32.const 3
    i32.add
    i32.add
    1

    View Slide

  18. 1 + 2 + 3 = ? i32.const 1
    i32.const 2
    i32.const 3
    i32.add
    i32.add
    1
    2

    View Slide

  19. 1 + 2 + 3 = ? i32.const 1
    i32.const 2
    i32.const 3
    i32.add
    i32.add
    1
    2
    3

    View Slide

  20. 1 + 2 + 3 = ? i32.const 1
    i32.const 2
    i32.const 3
    i32.add
    i32.add
    1
    5

    View Slide

  21. 1 + 2 + 3 = ? i32.const 1
    i32.const 2
    i32.const 3
    i32.add
    i32.add
    6

    View Slide

  22. 関数定義と関数呼び出し (i32.const 2)
    (i32.const 1)
    (call $add)
    (func $add
    (param $p0 i32)
    (param $p1 i32)
    (result i32)
    local.get $p0
    local.get $p1
    i32.add)

    View Slide

  23. 関数定義と関数呼び出し (i32.const 2)
    (i32.const 1)
    (call $add)
    (func $add
    (param $p0 i32)
    (param $p1 i32)
    (result i32)
    local.get $p0
    local.get $p1
    i32.add)
    2

    View Slide

  24. 関数定義と関数呼び出し (i32.const 2)
    (i32.const 1)
    (call $add)
    (func $add
    (param $p0 i32)
    (param $p1 i32)
    (result i32)
    local.get $p0
    local.get $p1
    i32.add)
    2
    1

    View Slide

  25. 関数定義と関数呼び出し (i32.const 2)
    (i32.const 1)
    (call $add)
    (func $add
    (param $p0 i32)
    (param $p1 i32)
    (result i32)
    local.get $p0
    local.get $p1
    i32.add)
    2
    1

    View Slide

  26. 関数定義と関数呼び出し (i32.const 2)
    (i32.const 1)
    (call $add)
    (func $add
    (param $p0 i32)
    (param $p1 i32)
    (result i32)
    local.get $p0
    local.get $p1
    i32.add)
    3

    View Slide

  27. 関数定義と関数呼び出し (i32.const 2)
    (i32.const 1)
    (call $add)
    (func $add
    (param $p0 i32)
    (param $p1 i32)
    (result i32)
    local.get $p0
    local.get $p1
    i32.add)
    3

    View Slide

  28. 線形メモリー

    View Slide

  29. 線形メモリー
    ● バイト単位でのアクセス
    ● ページ単位でメモリを増やせる
    ● 1ページ = 64KiB 単位
    ● data section で初期状態を記述
    64KiB
    最大サイズは
    実行時に指定可

    View Slide

  30. pub struct Item{
    pub id: u32,
    pub price: i32,
    }
    #[no_mangle]
    pub extern
    fn add(item_a: &Item,
    item_b: &Item) -> i32 {
    item_a.price + item_b.price
    }
    (func $add
    ($param $p0 i32)
    (param $p1 i32)
    (result i32)
    local.get $p1
    i32.load offset=4
    local.get $p0
    i32.load offset=4
    i32.add)

    View Slide

  31. (func $add
    ($param $p0 i32)
    (param $p1 i32)
    (result i32)
    local.get $p1
    i32.load offset=4
    local.get $p0
    i32.load offset=4
    i32.add)
    1 0 0 0 10 0 0 0
    2 0 0 0 2 3 0 0
    0
    8
    メモリー
    スタック

    View Slide

  32. (func $add
    ($param $p0 i32)
    (param $p1 i32)
    (result i32)
    local.get $p1
    i32.load offset=4
    local.get $p0
    i32.load offset=4
    i32.add)
    1 0 0 0 10 0 0 0
    2 0 0 0 2 3 0 0
    0
    8
    メモリー
    スタック
    0

    View Slide

  33. (func $add
    ($param $p0 i32)
    (param $p1 i32)
    (result i32)
    local.get $p1
    i32.load offset=4
    local.get $p0
    i32.load offset=4
    i32.add)
    1 0 0 0 10 0 0 0
    2 0 0 0 2 3 0 0
    0
    8
    メモリー
    スタック
    10

    View Slide

  34. (func $add
    ($param $p0 i32)
    (param $p1 i32)
    (result i32)
    local.get $p1
    i32.load offset=4
    local.get $p0
    i32.load offset=4
    i32.add)
    1 0 0 0 10 0 0 0
    2 0 0 0 2 3 0 0
    0
    8
    メモリー
    スタック
    10
    8

    View Slide

  35. (func $add
    ($param $p0 i32)
    (param $p1 i32)
    (result i32)
    local.get $p1
    i32.load offset=4
    local.get $p0
    i32.load offset=4
    i32.add)
    1 0 0 0 10 0 0 0
    2 0 0 0 2 3 0 0
    0
    8
    メモリー
    スタック
    10
    770

    View Slide

  36. ● コンパイルによって作られる
    関数定義のまとまり
    ● 仮想マシン向けの命令セット
    ● 線形メモリー
    Wasm ファイル
    https://rsms.me/wasm-intro

    View Slide

  37. Wasm ファイルを実行するには

    View Slide

  38. const stream = await fetch('some.wasm');
    const wasmFile = await stream.arrayBuffer();
    const instance = WebAssembly.instantiate(wasmFile);
    const value = instance.exports.add(1, 2);

    View Slide

  39. Ahead of time コンパイル
    x86
    ARM
    インタープリター
    Just in time コンパイル
    x86
    ARM

    View Slide

  40. const wasm = WebAssembly.instantiate(wasmFile);
    const memory =
    new Uint32Array(wasm.exports.memory.buffer);
    memory[0] = 0; // id for the first item
    memory[1] = 10; // price of the first item
    memory[8] = 2; // id for the first item
    memory[9] = 770; // the second item’s price
    wasm.exports.add(0, 8);

    View Slide

  41. #[wasm_import_module = "mod"]
    extern{
    fn discount() -> u32;
    }
    #[no_mangle]
    pub extern
    fn price(item: &Item) -> u32{
    unsafe{
    item.price - discount()
    }
    }
    (import "mod" "discount"
    (func $discount (type $t2)))
    (func $price (export "price")
    (type $t0)
    (param $p0 i32)
    (result i32)
    local.get $p0
    i32.load offset=4
    call $discount
    i32.sub)

    View Slide

  42. const imports = {
    mod: {discount: () => 50}
    };
    const wasm = WebAssembly.instantiate(wasmFile, imports);
    const memory =
    new Uint32Array(wasm.exports.memory.buffer);
    memory[8] = 2; // id for the first item
    memory[9] = 770; // the second item’s price
    wasm.exports.price(8); // returns 720

    View Slide

  43. ● インスタンス化が必要
    ● export
    ○ ホスト側に参照できる値
    ○ メモリーも export できる
    ● import
    ○ ホスト側から与えられる値
    ○ メモリーも与えられる
    Wasm の実行
    https://rsms.me/wasm-intro

    View Slide

  44. プラグインシステムの基盤としての Wasm

    View Slide

  45. ● プラグインとは第三者のコード
    ○ Security vs flexibility
    ○ 信頼の実現は難しい
    ● 安全にコードを実行するには
    ○ サンドボックス内での実行
    ○ 安全なコードの生成
    ● 信頼できないコードの実行を
    考慮して設計されている
    1 0 0 0 10 0 0 0
    2 0 0 0 2 3 0 0
    0
    8
    第三者のコードの実行
    x86
    ARM

    View Slide

  46. https://rsms.me/wasm-intro
    API

    View Slide

  47. https://rsms.me/wasm-intro
    Hook / callback

    View Slide

  48. API とコールバックの意味を定めることで、
    拡張機能を Wasm で実装できるようになる

    View Slide

  49. Microsoft Flight Simulator SDK Documentation

    View Slide

  50. WebAssembly in Envoy Proxy-Wasm vNEXT ABI specification

    View Slide

  51. API の設計以外に気をつけることは?

    View Slide

  52. 1. バージョン
    2. データ表現

    View Slide

  53. Wasm には追加の仕様があります
    https://webassembly.org/roadmap/ https://github.com/WebAssembly/proposals

    View Slide

  54. https://rsms.me/wasm-intro
    1で固定

    View Slide

  55. 対応する仕様の明示
    ● 策定中の仕様が多く存在
    ● Wasm にはバージョンがない
    ● 対応する仕様を明示することで、動
    作確認の手間を減らせる
    https://webassembly.org/roadmap/

    View Slide

  56. ● i32, i64, f32, f64 については規定がある
    ● それ以外は規定がない
    ○ 配列、リスト
    ○ 文字、文字列
    ○ レコード(構造体、共用体)
    ● 線形メモリ上に、なんとかして置いている
    → 置き方は処理系に任せられている。
    データ表現に関する規定があまりない

    View Slide

  57. バージョン名を返すAPIを例に考えると
    extern char* get_version_code();
    void do_something(){
    char *code = get_version_code();
    if (strncmp("pre", code, 3)){
    work_with_prelease();
    }else{
    work();
    }
    }
    (type $t0 (func (result i32)))
    (import "env" "get_version_code"
    (func $get_version_code (type $t0)))

    View Slide

  58. extern {
    fn get_version_code() -> String;
    }
    fn do_something(){
    unsafe{
    if get_version_code() == "pre"{
    work_with_prerelease();
    }else{ work(); }
    }
    }
    バージョン名を返すAPIを例に考えると
    (type $t3 (func (param i32)))
    (import "env" "get_version_code"
    (func $get_version_code (type $t3)))

    View Slide

  59. 文字列の表現が異なる
    H e l l o , w o r l d ! \0
    H e l l o , w o r l d !
    0
    0
    表現A:(0)
    表現B:(0, 12)
    0 12

    View Slide

  60. グルーコードが必要
    H e l l o \0
    8 5
    H e l l o

    View Slide

  61. char* id(char* value) {
    return value;
    }
    (type $t1
    (func (param i32) (result i32)))
    (func $id (export "id")
    (type $t)
    (param $p0 i32) (result i32)
    local.get $p0)

    View Slide

  62. #[no_mangle]
    pub extern
    fn id(value: &str) -> &str{
    value
    }
    (type $t2
    (func (param i32 i32 i32)))
    (func $id (export "id") (type $t2)
    (param $p0 i32) (param $p1 i32)
    (param $p2 i32)
    local.get $p0
    local.get $p2
    i32.store offset=4
    local.get $p0
    local.get $p1
    i32.store)

    View Slide

  63. データ表現の数だけ
    API が必要
    ● サポートする言語が
    (type $t1
    (func (param i32) (result i32)))
    (type $t2
    (func (param i32 i32 i32)))

    View Slide

  64. ● 策定中の仕様で、次の 3 つが目的
    ○ Language neutral なインターフェイスの定義を可能にする
    ○ Web API 呼び出しの最適化
    ○ Wasm モジュールの再利用性の向上
    ● 内容
    ○ Interface type の定義
    ○ Adapter
    ○ Lifting and Lowering Instructions
    Interface Types

    View Slide

  65. 言語ごとにライブラリを提供することも必要
    Microsoft Flight Simulator: Platform toolset WebAssembly for Proxies (ABI specification)

    View Slide

  66. まとめ

    View Slide

  67. ● さまざまなユースケースに対応できる
    ● サンドボックス化による安全性の確保できる
    ● 手に馴染んだ言語が使え、開発者体験の向上が期待できる
    ○ 勉強のコストを減らす可能性が高い
    ○ 既存資産を、少ない手間で利用できる
    → エコシステムの面で有利になりうる
    プラグインシステムに Wasm を採用すると

    View Slide