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
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
uhyo
August 24, 2024
Technology
4
5.1k
非同期処理を活用しながら Rust製wasmとJSを連携する方法(wasm-bindgenを使いたくない人向け)
2024-08-24 フロントエンドカンファレンス北海道
uhyo
August 24, 2024
Tweet
Share
More Decks by uhyo
See All by uhyo
タグ付きユニオン型を便利に使うテクニックとその注意点
uhyo
2
910
ECMAScript仕様の最新動向: プロセスの変化と仕様のトレンド
uhyo
3
750
TypeScript 6.0で非推奨化されるオプションたち
uhyo
17
6.8k
Claude Code 10連ガチャ
uhyo
5
990
AI時代、“平均値”ではいられない
uhyo
8
3.7k
意外と難しいGraphQLのスカラー型
uhyo
5
1k
RSCの時代にReactとフレームワークの境界を探る
uhyo
13
4.8k
知られざるprops命名の慣習 アクション編
uhyo
12
3.4k
libsyncrpcってなに?
uhyo
0
780
Other Decks in Technology
See All in Technology
GSIが複数キー対応したことで、俺達はいったい何が嬉しいのか?
smt7174
3
120
今日から始めるAmazon Bedrock AgentCore
har1101
4
320
KubeCon + CloudNativeCon NA ‘25 Recap, Extensibility: Gateway API / NRI
ladicle
0
170
Databricks Free Edition講座 データサイエンス編
taka_aki
0
280
Vitest Highlights in Angular
rainerhahnekamp
0
130
(金融庁共催)第4回金融データ活用チャレンジ勉強会資料
takumimukaiyama
0
100
クレジットカード決済基盤を支えるSRE - 厳格な監査とSRE運用の両立 (SRE Kaigi 2026)
capytan
6
1.8k
サイボウズ 開発本部採用ピッチ / Cybozu Engineer Recruit
cybozuinsideout
PRO
10
73k
Embedded SREの終わりを設計する 「なんとなく」から計画的な自立支援へ
sansantech
PRO
2
1.6k
【5分でわかる】セーフィー エンジニア向け会社紹介
safie_recruit
0
41k
変化するコーディングエージェントとの現実的な付き合い方 〜Cursor安定択説と、ツールに依存しない「資産」〜
empitsu
4
1.1k
We Built for Predictability; The Workloads Didn’t Care
stahnma
0
120
Featured
See All Featured
Rails Girls Zürich Keynote
gr2m
96
14k
DevOps and Value Stream Thinking: Enabling flow, efficiency and business value
helenjbeal
1
89
The B2B funnel & how to create a winning content strategy
katarinadahlin
PRO
0
270
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
16
1.8k
A brief & incomplete history of UX Design for the World Wide Web: 1989–2019
jct
1
290
HDC tutorial
michielstock
1
340
ReactJS: Keep Simple. Everything can be a component!
pedronauck
666
130k
実際に使うSQLの書き方 徹底解説 / pgcon21j-tutorial
soudai
PRO
196
71k
Understanding Cognitive Biases in Performance Measurement
bluesmoon
32
2.8k
世界の人気アプリ100個を分析して見えたペイウォール設計の心得
akihiro_kokubo
PRO
66
36k
Introduction to Domain-Driven Design and Collaborative software design
baasie
1
580
How to build an LLM SEO readiness audit: a practical framework
nmsamuel
1
630
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