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.5k
非同期処理を活用しながら Rust製wasmとJSを連携する方法(wasm-bindgenを使いたくない人向け)
2024-08-24 フロントエンドカンファレンス北海道
uhyo
August 24, 2024
Tweet
Share
More Decks by uhyo
See All by uhyo
tsconfig.jsonの最近の新機能 ファイルパス編
uhyo
7
2k
tsconfig.jsonの設定を見直そう!フロントエンド向け 2024夏
uhyo
25
8.5k
React 19を概念から理解する
uhyo
21
9.5k
require(ESM)とECMAScript仕様
uhyo
6
1.8k
TypeScript Quiz (Encraft #12 Frontend Quiz Night)
uhyo
8
1.7k
Shadow DOMとCSSの現状
uhyo
11
7.2k
TypeScriptってどんな言語? 言語そのものを知る面白さ
uhyo
16
8.9k
App Router時代のデータ取得アーキテクチャ
uhyo
49
16k
ステート管理を超えるRecoil運用の考え方
uhyo
15
12k
Other Decks in Technology
See All in Technology
2024-10-30-reInventStandby_StudyGroup_Intro
shinichirokawano
1
610
AWS CDKでデータリストアの運用、どのように設計する?~Aurora・EFSの実践事例を紹介~/aws-cdk-data-restore-aurora-efs
mhrtech
4
630
サイロ化した金融システムを、packwerk を利用して無事故でリファクタリングした話
coincheck_recruit
3
3.7k
30万人が利用するチャットをFirebase Realtime DatabaseからActionCableへ移行する方法
ryosk7
5
330
マネジメント視点でのre:Invent参加 ~もしCEOがre:Inventに行ったら~
kojiasai
0
440
オーティファイ会社紹介資料 / Autify Company Deck
autifyhq
9
120k
物価高なラスベガスでの過ごし方
zakky
0
350
プロダクト成長に対応するプラットフォーム戦略:Authleteによる共通認証基盤の移行事例 / Building an authentication platform using Authlete and AWS
kakehashi
1
150
AIを駆使したゲーム開発戦略: 新設AI組織の取り組み / sge-ai-strategy
cyberagentdevelopers
PRO
1
130
visionOSでの空間表現実装とImmersive Video表示について / ai-immersive-visionos
cyberagentdevelopers
PRO
1
100
わたしとトラックポイント / TrackPoint tips
masahirokawahara
1
240
オニオンアーキテクチャで実現した 本質課題を解決する インフラ移行の実例
hryushm
14
3k
Featured
See All Featured
It's Worth the Effort
3n
183
27k
Building Applications with DynamoDB
mza
90
6.1k
Building Flexible Design Systems
yeseniaperezcruz
327
38k
Music & Morning Musume
bryan
46
6.1k
Principles of Awesome APIs and How to Build Them.
keavy
126
17k
Being A Developer After 40
akosma
86
590k
Thoughts on Productivity
jonyablonski
67
4.3k
What's in a price? How to price your products and services
michaelherold
243
12k
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
27
790
[RailsConf 2023] Rails as a piece of cake
palkan
51
4.9k
The World Runs on Bad Software
bkeepers
PRO
65
11k
Agile that works and the tools we love
rasmusluckow
327
21k
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