Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
非同期処理を活用しながらRust製wasmとJSを連携する方法(wasm-bindgenを使...
Search
uhyo
August 24, 2024
Technology
4
3.1k
非同期処理を活用しながら Rust製wasmとJSを連携する方法(wasm-bindgenを使いたくない人向け)
2024-08-24 フロントエンドカンファレンス北海道
uhyo
August 24, 2024
Tweet
Share
More Decks by uhyo
See All by uhyo
tsconfig.jsonの設定を見直そう!フロントエンド向け 2024夏
uhyo
22
7.4k
React 19を概念から理解する
uhyo
21
9k
require(ESM)とECMAScript仕様
uhyo
6
1.7k
TypeScript Quiz (Encraft #12 Frontend Quiz Night)
uhyo
8
1.6k
Shadow DOMとCSSの現状
uhyo
11
6.9k
TypeScriptってどんな言語? 言語そのものを知る面白さ
uhyo
16
8.7k
App Router時代のデータ取得アーキテクチャ
uhyo
48
15k
ステート管理を超えるRecoil運用の考え方
uhyo
15
12k
ついに来る!TypeScript5.0の新機能
uhyo
21
16k
Other Decks in Technology
See All in Technology
Monitor GraalVM Native Apps with OpenTelemetry
logico_jp
0
120
waitany と waitall を作った話
mrkn
0
120
エンジニア視点で見る、 組織で運用されるデザインシステムにするには
shunya078
1
280
MySQLユーザ会なにやってるの?とおさそいと / たいへんなのw
sakaik
1
130
ロボットアームを遠隔制御の話 & LLMをつかったIoTの話もしたい
soracom
PRO
1
250
難しいから面白い!医薬品×在庫管理ドメインの複雑性と向き合い、プロダクトの成長を支えるための取り組み / Initiatives to Support Product Growth
kakehashi
2
170
Mocking in Rust Applications
taiki45
1
330
サーバレスでモバイルアプリ開発! NTTコム「ビジネスdアプリ」のアーキテクチャ / The architecture of business d app
nttcom
11
190
App Router を実プロダクトで採用して見えてきた勘所をちょっとだけ紹介
marokanatani
0
720
標準ライブラリの奥深アップデートを掘り下げよう!
logica0419
2
440
FastConnect の冗長性
ocise
0
7.2k
四国クラウドお遍路 2024 in 高知 オープニング
yukataoka
0
180
Featured
See All Featured
The Power of CSS Pseudo Elements
geoffreycrofte
71
5.2k
What’s in a name? Adding method to the madness
productmarketing
PRO
21
3k
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
28
1.6k
Fireside Chat
paigeccino
31
2.9k
The Invisible Side of Design
smashingmag
295
50k
Design by the Numbers
sachag
277
19k
Large-scale JavaScript Application Architecture
addyosmani
508
110k
I Don’t Have Time: Getting Over the Fear to Launch Your Podcast
jcasabona
26
1.9k
Making the Leap to Tech Lead
cromwellryan
128
8.8k
Principles of Awesome APIs and How to Build Them.
keavy
125
16k
Automating Front-end Workflow
addyosmani
1365
200k
Why Our Code Smells
bkeepers
PRO
334
56k
Transcript
非同期処理を活用しながら Rust製wasmとJSを連携する方法 (wasm-bindgenを使いたくない人向け) 2024-08-24 フロントエンドカンファレンス北海道
発表者紹介 uhyo 株式会社カオナビ フロントエンドエキスパート 好きなプログラミング言語は TypeScriptとRust。
宣伝 株式会社カオナビはフロントエンドカンファ レンス北海道のゴールドスポンサーです 午前中にスポンサーLTもありました
このトークのテーマ: WASM JavaScriptエンジンに処理系が組み込まれており、 フロントエンドからも利用可能な実行可能形式。 多くの言語からコンパイルターゲットとして 利用可能であり、JS以外で書いたコードを フロントエンドで動かす有力な手段である。
WASMの進化 WASMは基本機能がすでに標準化されて各処理系 に組み込まれているが、 さらなる進化が提案・検討されている。 WebAssembly proposals https://github.com/WebAssembly/proposals
WASMと非同期処理 非同期処理に関する機能はまだ完成していない。 関連するプロポーザル: • Threads • Stack Switching • JS
Promise Integration (JSPI)
現状のWASMは同期処理である 現状のWASMの実行モデルは単純であり、 呼び出し側(JS側)から見たら同期関数に見える。 つまり、JSとWASMでコールスタックを共有する。
コールスタック共有の例 WASM側のコードはRustで示します 呼び出し
コールスタック共有の例 JS→WASM→JSのように関数呼び出しをした場合、 このように同じコールスタックに混ざる。 JSの関数もWASMの関数も 同期関数として振る舞っている。 JS JS WASM
現状のWASMと非同期処理の問題 JS側で非同期処理が発生した場合、 WASM側が「待つ」方法が現状存在しない。 WASM JS これやって (関数呼び出し) ちょっと待ってね (Promise返し) え?
何これ
このトークの内容 JS側で非同期処理を使いつつ、 WASMと連携する方法を2つ解説。 ① JSPIを使う方法 用意されたAPIを使えば簡単にできる ② 自前のイベントループを用意する方法 WASM側でRustのasync/awaitを使える! 「JS側の非同期処理を.awaitする」とかできる
補足: wasm-bindgen このトークではwasm-bindgenは扱いません。 Rustに組み込みのWASM対応のみ使用。 wasm-bindgen不使用 wasm-bindgen使用 WASM Rust コンパイル WASM
Rust コンパイル JS Promise対応とかもあるらしい
JSPI (JS Promise Integration)
JSPI JS側の非同期処理が完了するまで、WASM側を 止めておくことができる仕様。 実験的機能としてChromeに実装されている。
JSPIの例 WASM側は、JS側から getA, getB, getCを インポートする。 runが実行されると、 3つの関数を実行して 結果を足した値を 返す。
JSPIの例 getAとかは実はJS側では非同期関数になっている。 WebAssembly.SuspendingでラップしてWASM側に渡す ことで、WASM側から同期関数に見える。
JSPIの例 WASMのエントリーポイントとなる関数を、 WebAssembly.promisingでラップする必要がある。 ラップしたら返り値がPromiseになる。
JS JSPI図解 WASM サスペンド可能な世界 JS promisingでラップ した関数を呼出 Suspendingでラップした関数が Promiseを返した場合、 解決するまでWASMの実行が止まる
(非同期処理がWASM側からは同期に見える) Promiseが返る (処理が全部 終わったら解決)
JSPI図解 promisingを介してWASMを実行することで、 WASMがasync関数みたいになる。 JS側が返したPromiseは自動的にawaitする。 JS WASM JS async await await
await
WASMの並行実行 promisingを介して実行した場合、 WASMを複数並行して実行することができる。 (JSのasync関数を複数同時に実行するのと同じ) 複数同時に並列実行され ることはない。 JSと同じ実行モデル。 JS WASM JS
JSPIの利点・欠点 利点 WASM側に非同期処理の知識は一切不要。 WASM側では従来通りの同期的な処理を書きつつ、 JavaScript側では非同期処理を利用できる。 欠点 WASM側から複数の非同期処理を並行的に開始す ることはできない。(複雑なアプリの実装に不向き)
自前のイベントループを 用意する
背景 Rust製のCLIツールをWASMにコンパイルして Node.js経由で実行する形でnpmで配布している。 一部の処理をNode.js側に移譲しており、Node.js 側の処理を待つ間もWASM側を実行し続けたい。 (JSPIでは難しいユースケース)
WASM側中心のアーキテクチャ WASMがメインのアーキテクチャ(WASIも活用) なので、非同期処理もWASM側で取り扱いたい。 具体的には、Rustのasync/.awaitを使いたい。 JS側の非同期処理をRustのFutureとして扱いたい。 (FutureはJSのPromiseみたいなやつです)
Rustの非同期処理の例 JSの実行をNode.jsに任せているところ 非同期処理
自前のイベントループ コード実行 非同期処理発生 Node.jsに処理を発注 結果受け取り処理 タスクを 登録 Node.js側で処理完了 JS WASM
コード実行 自前のイベントループ(別視点) コード実行 非同期処理 発生 Node.jsに処理を発注 結果受け取り 処理 Node.js側で処理完了 起動
非同期処理 発生 Node.jsに処理を発注 同期実行 同期実行 WASMの同期実行は、 できるタスクが無くなったら returnしてJSに制御を戻す。 非同期処理が完了したら、 JS側からWASMを呼び出して できるようになった タスクを走らせる。 JSのイベントループ (マイクロタスク)と 考え方は同じ。 関数呼び出し return 呼出 return
Rustの非同期ランタイム Rustでのasync/.awaitの実務は 非同期ランタイムが担当する。 Tokioなどが知られており、 WASM対応もされている。 https://tokio.rs/
Rustの非同期ランタイム WASM含むシングルスレッドの環境では、 非同期ランタイムの実態はイベントループになる。 しかし、今回の要件にはTokioは採用できなかった。
Tokioを採用できない理由 Tokio(のようなハイレベルなランタイム)では、 イベントループを回すイベントが 決め打ちされている。 (ネットワークIO、ファイルIO、タイマー) 「Node.js側で非同期処理が終わったとき」 のようなイベントは作れない。
ローレベルな非同期ランタイムを使う 今回はasync-executorを採用。 「タスクを積む」「イベントループを1週回す」 というローレベルなAPIを提供しており、 Node.js側で非同期処理が完了したらイベント ループが再開する挙動を実装できる。 https://github.com/smol-rs/async-executor
JS側とWASM側の通信 ① WASMからJSにコード実行を依頼 WASM JS execute_node コード文字列とハンドル(i32)を JS側に渡す JS側で非同期的 にコードを実行
JS側とWASM側の通信 ② JSからWASMに実行結果を渡す WASM JS execute_node_ret ハンドルと実行結果を渡す そのままWASM側の イベントループ再開 タスクが無くなったら
制御が戻る
JS側とWASM側の通信 コード実行 非同期処理発生 Node.jsに処理を発注 結果受け取り処理 タスクを 登録 Node.js側で処理完了 execute_node execute_node_ret
JS側のイベントループとの統合 この構成は、JSのイベントループの一部として WASMのイベントループを動かしているという見 方もできる。 コード実行 非同期処理発生 結果受け取り 処理 JSイベント ループ
非同期処理
実際のコード (JS側) やっていることは、 渡された文字列を読む →非同期的に 実行する (w.run) →結果が出たら WASM側に送る (execute_node_ret)
単純!
WASM側のコード(Rust)もチラ見せ 非同期処理を表す構造体に Futureを自前実装
WASM側のコード(Rust)もチラ見せ JS側から非同期処理の 結果を受け取る 結果をメモリに記録 イベントループを回す この処理を待っている Futureを起こす
今後の展望 現在、Node.js側に非同期処理として任せている のはJSの実行のみ。 ファイル読みこみなどはWASIで行っている。 (=非同期にはなっていない) ここも非同期化できたら面白そう。 しかし独自実装が増えるので大変。
まとめ Rust製のWASMでJS側の非同期処理を待つ、 しかもそれをRustのasync/.awaitで表現する ためには、 ローレベルな非同期ランタイムを用いて自前の イベントループを実装し、 WASMのイベントループをJSのイベントループの 一部として扱えばいい。 ※2024年8月現在の情報です。
宣伝 今回お話ししたアイデアが実際に実装されている ソースコードはこちらで見ることができます。 https://github.com/uhyo/nitrogql