2024年6月20日に『深掘りRubyKaigi 2024 with kateinoigakukun & ledsun & remore』を開催しました。イベントの内容をほぼ全文文字起こし形式でお届けします。この記事は第1部です。
イベントのアーカイブはYouTubeでも公開しています。
登場人物
ゲスト
- kateinoigakukun/齋藤さん
- ledsun/中島さん
- remore/澤田さん
STORES
- fujimura/藤村 大介
- mame/遠藤 侑介
自己紹介
fujimura:藤村です。STORES でCTOをやっています。
mame:遠藤です。STORES でフルタイムRubyコミッターをしています。ruby.wasmはkateiさんが開発を始めた初期から話を聞いたりサポートさせてもらったりしてます。ruby.wasmはまだ仕事で使った人はほとんどいないと思いますが、ぼくはRubyKaigiでruby.wasmを使ってRubyクイズを作ったので、プロダクション利用経験ありです。よろしくお願いします。
kateinoigakukun:齋藤です。僕はruby.wasmを作ったんですけど、まだプロダクションで使ったことはないです。なのでmameさんより経験は浅いんですけど、 今日はよろしくお願いします。
ledsun:ledsunというハンドルネームでやっています、中島といいます。ruby.wasmを使ってブラウザでRubyを動かすのを楽しんでいます。やっぱりプロダクションではまだやったことがないので、やはりmameさんにはちょっと劣っていると思っています。よろしくお願いします。
remore:remoreというハンドルでやってます、澤田といいます。ruby.wasm歴は多分1年ぐらい、WebAssembly歴も大体1年ぐらいです。プロダクションでいうと、自分のサイトを実験的に公開はしてるので、半分ぐらいプロダクション歴ありますけど、がっつりワークロードをやってるわけじゃないんで、mameさんには負けるかなと思います。よろしくお願いします。
fujimura:ありがとうございます。真の職業ruby.wasmエンジニアがここに一人じゃないわけですね。今日は『深堀りRubyKaigi 2024』をお届けします。
ブラウザで動くMastodonを作るまでの道のり
fujimura:早速なんですけどパート1はkateiさんの発表について伺っていきたいと思います。発表を見ている方も多いと思うんですが、簡単に発表についてダイジェストを教えていただけますか?
kateinoigakukun:ruby.wasmはこれまでRuby自体では動いていたんですけど、gemの利用っていうところが基本的な機能として欠けていたので、そこのピースを埋めましたという発表でした。Asyncifyみたいな技術的なブレークスルーがあったわけじゃないんですが、やるべきことを着々とやって、最新のコンポーネントモデルのような、ちょっと今どきのも取り入れつつ、いろいろと実装を進めていった結果、なんとMastodonが動くようになりましたという発表でした。
fujimura:ありがとうございます。遠藤さんからMastodonが動いたって話を事前に聞いてて腰を抜かしたんですけど、kateiさんが世界で初めてブラウザでMastodonを動かしたわけじゃないですか。最初に動いた時の感触、感動があったらぜひ聞きたいです。
kateinoigakukun:一番最初に動いたページが本当にMastodonのタイムラインが出るページなんですけど、なるほど動くんだと思いました。それまでもRails自体が動いたという報告は受けていて、理論上動かない理由はないので、できるだろうなと思っていたけど、本当にできるんだなと思いましたね。SEGVしなくてよかった。
fujimura:ruby.wasm以外にも手を入れたものがたくさんあったと思うんですけど、どんなものに手を入れましたか?
kateinoigakukun:だいぶいろんなところに出張しました。RubyGemsリポジトリの方のRubyGemsの実装、JSONの高速処理ライブラリのOjやXMLパーサーのNokogiriも直したり。あとだいぶ出張したのは、ポスト上のURLを認識して、テキスト処理をするtwitter-text gemをMastodonは使っていて、それが依存しているGNUが提供しているURLの国際標準ライブラリ(libidn)があって、それが依存しているGNU Libcっていう共通ユーティリティライブラリをWasm対応するためにメーリングリストまで出張したり。だいぶいろんなところまで直しました。
fujimura:世にあるライブラリの、だいたいここのポイントでWasm対応ができてないなっていうのがあるんですか?
kateinoigakukun:Wasm対応はRubyレベルではほとんど必要なくて、拡張ライブラリでC拡張を書いているところでWASIのシステムコールにない機能を使っている部分が直さないといけないところです。足りないのがスレッドと細々したユーティリティ、例えばシステムスタックがいじれないのでスタックサイズの変更だったり。あとはWASI-LibcっていうWasmで使っているLibc実装がmusl-basedなので、その辺の実装の違いによる調整が必要っていうのがメインですね。
fujimura:ライブラリをやっている人たちはWasm対応で必要ですっていうと素直に応じてくれるもんなんですか?それともWasmは別に対応しなくてもいいかなみたいな気持ちになっている人もいる?
kateinoigakukun:GNUの方はちょっと感触が厳しかったんですけど、gem自体のメンテナーの人たちは心よくパッチを受け入れてくれたので、だいぶありがたかったですね。
fujimura:なんか難しいやつありました?
kateinoigakukun:そんなに難しくないんですよね。
fujimura:本当かな?
kateinoigakukun:いやいや、本当に。ビルドしてコンパイルできないので直す。そしたらテストが大体通るっていうくらいのクオリティになりました。Wasm自体のツールチェーンのクオリティもだいぶ上がってるので、そんなに難しくないですね。あと現状だとCIでチェックできないのが若干ネックなんですけど、そこら辺はruby.wasm本体のツール整備をしていくことで、もうちょっと頑張るとできるようになりそうなので、メンテナーさんに負担のないサポートができるといいなと思っています。
fujimura:簡単にCIでruby.wasmのテストが流せるようになるとハッピーですよね。
kateinoigakukun:ブラウザ上でIRBが開いて、そこで実際にビルドしたgemがその場で試せる世界観になったら面白いなとか考えたりしています。
fujimura:Wasmだからこそできることでもあるから、楽しそうですよね。
kateinoigakukun:これができたら面白いと思う。Gemをメンテナンスしてないのでわからないんですけど、実際に嬉しいんですかね。
fujimura:そうじゃないですかね。0からruby.wasmでビルドする環境を作って、ちょっと試せる環境も手作りで作るって慣れてる人はやれるけど大変だし、みんながそれをやる必要はないと思うので、あるといいですよね。
コンポーネントモデル
fujimura:最近はruby.wasmやRuby周辺だと、どんなことをされているんですか?
kateinoigakukun:ruby.wasmで今メインでやってるのはダイナミックリンクへの対応です。 それをやるにはコンポーネントモデルに依存しているので、コンポーネントモデルのツール群の実装を直す活動を日々しています。
fujimura:コンポーネントモデルで何ができるのか、コンポーネントモデルが入ることでWasmの世界でできるようになること、kateiさんからの見え方も含めて聞いてみたいです。
kateinoigakukun:コンポーネントモデルはだいぶいろんな人の夢が詰まった仕様になっています。コンポーネントモデルってプログラムとプログラムをどうリンクするかを記述する仕様で、ダイナミックリンクするようなプログラム、例えばRubyみたいにその本体のプログラムがあって、そこに対して拡張ライブラリのようなプラグインを読み込ませるには、そこのインターフェースを決めないといけないじゃないですか。CRubyの拡張ライブラリの仕組みだと基本的にCのABIを規定して、それでお互い通信すると思うんですけど、これってお互いにCだから成り立つ話ですよね。コンポーネントモデルはそこを抽象化して、言語に依存しないインターフェースを作ります。
インターフェースで区切ったコンポーネントごとをどうやって組み上げるかを記述する。高レベルなインターフェースのABIとそのインターフェースへ区切られたコンポーネントをどう組み上げるかっていうのを記述する仕様になっています。
これができることで僕が嬉しいのは、ruby.wasmを作るときにWASI-vfsを作ったんです。WASIっていうインターフェースを区切って、その先にバーチャルファイルシステムを作って、その2つをくっつけるっていうコンセプトなんですけど、それがコンポーネントモデル上できれいに表現できるんですね。
WASIっていうインターフェースがコンポーネントモデル上で表現されて、その先は高レベルなインターフェースをVFSが実装する、最終的にそれを組み上げるところもコンポーネントモデルの仕様の中で記述できるとRuby的には嬉しいところですね。
fujimura:いろんな言語で作られたものがコンポーネントモデルっていうインターフェースを使って一つのものになれる未来っていうのが美しい世界なんですかね。
kateinoigakukun:未来を見ている人は全然違う言語間でマイクロサービスを作って、その通信をコンポーネントモデルのインターフェースでやる。そうするとネットワークトラフィックはなくて、インターフェース間の通信は最適化可能な形になっているので、それなりに早くポータブルなプログラムが小さい単位で組み合わせられるっていう世界観を見てる人はいますね。
fujimura:kateiさん的にはその未来は順風満帆に見えているのか、今見えてる課題もあるのか。
kateinoigakukun:なかなか難しいですね。その未来が来たらいいなとは思っていて、いろんな実装のクオリティやいろんなものを考慮するとまだまだ道のりは長いけど、きっと今ここで苦しみを耐えて突き進んでいくことで、明るい未来が見えていくんじゃないかなとそこは信じるしかないですね。
言語とかツールが対応していかないとユースケースも生まれないし、ユースケースとツール言語の関係もそうですし、そのツールと言語の関係もそう。コンポーネントモデルをうまく動かすためには、エコシステムとしてユーザーとツールと言語のフィードバックサイクルが必要なんですけど、そこのブートスラップを頑張っている最中っていう感じです。
fujimura:一回まわり始めれば改善点が見えて、みんなが巻き込まれていってってなると面白いですけどね。
気になった発表は『Running Optcarrot (faster) on my own Ruby.』
fujimura:RubyKaigiの他の方の発表に対して、感想はありますか?
kateinoigakukun:僕が個人的に興味があるのがJITとかコンパイラ系の領域で、それで言うとMonochromeさんのMonoRubyのJITの話は好きでした。めちゃくちゃアセンブリーアセンブリーしていて。現状のWasmだとJITっていうのはなかなか難しい。何が難しいかというとデータ空間にコードを置くことができないので、ハーバードアーキテクチャゆえのセキュリティを前提とした制約があるんですけど、ないものねだりというか、WasmでJITできたらこういうアーキテクチャにするのが面白いかなとか考えながら聞いていました。
fujimura:そこはWasmの受け入れてるデザインとしての制約で、超えられることはない壁って感じなんですかね?
kateinoigakukun:自由にデータ領域にコードを置くことはできないかもしれないですけど、JITっていうユースケースを満たすための口はあり得るかなと思っていて。なぜかというと、一番避けないといけないのは検証されていないコードを実行することなんですね。Wasmって実行する前に色んな検証をするんですよ。スタックマシンがたくさんポップしすぎないかとか、そういう検証をするんですけど、検証をしないまま実行するのが一番まずいので、実行時に組み上げたコードのバイトシーケンスをホスト側に渡して、検証してもらって、それを実行可能な形にする。そういうJITのユースケースを満たすためだけなら色々とやりようはあるかなと思っています。
remore:JITをやりたくなったらWasmのランタイム側を見たいという欲望は生まれないんですか?モジュール側じゃなくてランタイム側だったらJITできるじゃないですか。
kateinoigakukun:そうですね。それはWasmモジュール自体をJITする話か、Wasmプログラムの中でプログラムを生成してJITする話なのか。
remore:ruby.wasm のWasmモジュールの中の制約の中で考えてるのか、ゆくゆくはWasmのランタイムの方もちょっと手入れてみたいと思っているのか、どんなスコープで考えてますか?
kateinoigakukun:Wasm対応はそんなにいっぱいマンパワーがあるわけじゃないので、できるだけ標準に乗っていきたいなっていうのがruby.wasmの方針としてあります。Ruby自体で独自のことをして標準から外れてまでJITをやるっていう気持ちはあんまりなくて、やるとしたらランタイムの方に標準の口をつけてもらうっていうのが筋かなと思う。
remore:わかりました、ありがとうございます。
kateinoigakukun:マンパワーがあるところは実際にこれをやっていて、.NETのBlazorはWasmに対応しているんですけど、実行時にWasmも作って、ブラウザっていうターゲットを前提にしているので、ランタイム側、JavaScript側とのコンベンションは自由にできるので、その上でJITっぽいことをしている。やろうと思えばできるんだけど、標準を外れてまでやる元気はまだないって感じですね。
mame:実行時にWasmを生成するってことなんですかね?実行してる何かの単位を小さいWasmにJITでコンパイルする感じになるんですか?
kateinoigakukun:そうですね。モジュールのバイトシーケンスを組み立てる。例えばブラウザだったら、そのバイトシーケンスをインポートされた関数に渡して、それをブラウザ側でモジュールにコンパイルして、エクスポートされた関数のポインタをどこかに保存して、それを呼び出す。
mame:ブラウザだったら理論上できるというのはわかるんですけども、そういう計画があるんですか?まだkateiさんのアイデアの段階?
kateinoigakukun:まだ僕の頭の中で進んでいるだけですね。
mame:面白いですね。それはどこのレベルの仕様になるんですかね?WASI?
kateinoigakukun:コアスペックに入れる話ではない気がしています。コアスペックの上で表現可能な仕様に落とせるような気がしています。
mame:なるほど。
kateinoigakukun:それがWASIの上で標準化するのかわからないですけど、でも同じモチベーションの人がいると思うんですよね。
mame:それはWasmでインタプリタを作っている人ですかね。
kateinoigakukun:そうですね。
mame:YJITの第三のアウトプットとしてWasmというのがあり得るんですかね。
kateinoigakukun:あり得るんじゃないですか。実行時にコンパイルするっていう方向性だけじゃなくて、例えばWasmだとプレイニシャライザーっていうのがだいぶ熱くて、途中までプログラムを実行して、そこでプロセスのスナップショットをとって、それがWasmプログラムとして表現可能なんですね。それを別のWasmランタイムで特別なセットアップなしにそのまま動かせる。そういうのを考えると、途中まで実行するところでコンパイルしてしまえば、ランタイムとの通信の口がなくても、途中で止めているところで新しくモジュールを実際にユーザーコードから生成されたWasmをリンクしておくとランタイムとのJITの口がなくてもスタンダードに乗った状態でコンパイルされたユーザーコードからWasmにコンパイルされた状態で実行できるような形になり得るんですよ。
mame:なるほど。コメントも来てますね。
CRIUみたい
kateinoigakukun:そうそう。
これからの開発方針
mame:ruby.wasmでMastodonを動かすという大きなマイルストーン、目標を達成したと思うんですけど、この先どういう方向で開発を進めていく予定ですか?
kateinoigakukun:一番最初のRubyKaigiでIRBを動かして、今回はMastodonを動かして、びっくりドッキリネタは尽きちゃったので、あとは堅実にクオリティを上げていく感じですね。
mame:ブラウザの上で動くっていうのがインパクトがでかいんですよね。ブラウザの上で何かが動くというのはそれだけで人を魅了する何かがあるっていう感じなんですけど。kateiさんの興味として、ブラウザの上で作り込んでいくということにめちゃめちゃ興味があるわけではないのかなと思っていて、普通のプロセスとして動かしていく方向で発展させていきたいのか、それともブラウザの上でもっと便利にしていくのかだと?
kateinoigakukun:どちらかというとブラウザ外の方が興味はあるんですけど、なかなかユースケースが見えてこないとどっちの方向に進めていいのかわからないなって感じですね。
mame:今日は幸いFastlyのユースケースが聞けるかもしれないですけど。Mastodonがブラウザの上で動くっていうのができたんですけども、RailsアプリケーションをWasmにコンパイルして、DockerをWasmが置き換えるという構想の話は聞くんですけども、その段階にどのくらい近づいてます?サーバープロセス的に動かせるレベルまで来てますか?
kateinoigakukun:テストは毎回ブラウザを立ち上げると大変なので、テストはWasmtimeでやっています。
mame:そうなんだ。
kateinoigakukun:Wasmtime上である程度動くことは確認していて、Mastodonが動くなら、大抵のRailsアプリは動くんじゃないかなと思ってるけど、それを実際に動かして嬉しいんですかね?
mame:それは難しいですね。DockerコンテナよりWasmにした方がいいことがあるかという。一番わかりやすいのはセキュリティ面ですかね。アタックサーフェスが少ない。Wasmに変換して動かせば、シェルが取られることはないと思うんですよ。Wasmtimeにバグがなければの話ではあるんですけど。そういうメリットがあるかなと思うのと、あとはどうでしょうね?起動速度とかスループットとかはやってみないと分かんないなと思ってるんですが、その辺はぼちぼちって感じですかね?
kateinoigakukun:起動速度はさっきのプレイニシャライザーとかの話を含めると、だいぶネイティブより速くなる可能性はあって。
mame:おー!
kateinoigakukun:ただ実際のリクエストを受けたりするワークロードでは、2倍くらい遅いくらいで手を打ってくれないかなって。
mame:どの辺にオーバーヘッドがあるんですかね?Asyncify?
kateinoigakukun:Asyncifyで2、3倍遅くなっていて、Wasmにするだけで大体2倍。なので、2倍で手を打ってもらうためには、Asyncifyをどうにかしないといけない。
mame:Asyncifyをやめた上で2倍くらい、なるほど。
fujimura:時間の都合で最後の質問にしようと思うんですけど、脱Asyncifyは大変そうなんですけど、こうするとできるみたいなのがあるんでしょうか?
kateinoigakukun:明確なロードマップはまだないんですけど、いくつかWasm自体に提案されている機能があって、その一つにStack Switchingっていうプロポーザルがあるんですけど、これがいい感じに発展していくといいのかなって。今Asyncifyをどこで使っているかというと、Fiber、Conservative GCとsetjmp、longjmpの3つなんですねsetjmp、longjmpは基本的に下から上への一端方向のJumpに限れば、Exception Handlingっていう機能でどうにかまかなえそうで、そっちはだいぶ実装が進んでいます。
Fiberの方が超大変で、Fiberは下から上じゃなくて、上から全然別のStackから別のStackに移動しないといけないんですよね。これが現状だとネイティブなWasmだと表現できないんで、これがStack Switchingっていうプロポーザルでカバーされる。これをいい感じにはまるとあとは倒すべきはConservative GCだけになります。Conservative GCはまだわからないです。
mame:「Conservative GCだけ」といっても、ぜんぜん簡単な話じゃないですよね。多分Conservative GCが一番重たいんじゃないかなと僕は思っております。
kateinoigakukun:重いかなぁ。
mame:CRubyの結構な量の書き換えが必要なのかなっていうところが大変そう。これからに期待。
fujimura:みなさんもご質問があればぜひ。
ledsun:発表の中でサイズを小さくするっていうロードマップがあって、それが僕の中ではおおって盛り上がったんですね。なので、期待してますっていうだけなんですけど。
fujimura:ブラウザに使うってなるとマジでそれ重要ですよね。
kateinoigakukun:サイズは本当に難しくて、必要ないものを削るっていうのはもちろんなんですけど。今ruby.wasmのバイナリで多く占めている部分で、本当は絶対に使ってないんだけど、バイナリに含まれている部分っていうのが多い。それを消したとしても、サイズを考える上で2パターンあります。
小さいプログラムを小さく作れるっていうのと、そこそこ大きいプログラムをそれなりの中ぐらいのサイズで作れる。例えばRustだと小さいプログラムはすごい小さく作れるんですよ。これは標準ライブラリをストリップしたり、エコシステムとして柔軟になっている。なんですが、そこからのベロシティがだいぶあるんですね。これはコンパイラのコード生成の特徴としてあるんですけど、ユーザーコードの量に対してのベロシティっていうのはRubyはだいぶ穏やかで、Iseqにするとそれは増えるんですけど、Rubyプログラム自体の表現力としては結構小さいなと思っていて。なので中ぐらいのCRubyインタープリタがあって、その上である程度ユーザーコードをいっぱい書いたとしても、同じ量をRustで書くっていうのと比べると、ユーザーコードが増えていくといい勝負になるんじゃないかなと思っています。
fujimura:ありがとうございます。
第2部に続きます。
深堀りRubyKaigi 2024 文字起こしレポート一覧
- ブラウザで動くMastodonを作るまでの道のり、これからのruby.wasmの開発方針。深掘りRubyKaigi 2024 文字起こしレポート vol.1
- Rubyでフロントエンドを書く未来、おもしろRuby in the browser事案。深掘りRubyKaigi 2024 文字起こしレポート vol.2
- CDNとWasm、WasmになってるCRubyはそれ自体が素晴らしい。深掘りRubyKaigi 2024 文字起こしレポート vol.3