@ledsun blog

Hのキーがhellで、Sのキーがslaveだ、と彼は思った。そしてYのキーがyouだ。

Ruby meets WebAssembly

rubykaigi.org

ご本人のブログです。 発表資料もあります。

kateinoigakukun.hatenablog.com

CRubyをruby.wsmにコンパイルしてブラウザ上で動かせるようにした人の発表です。

https://github.com/ruby/ruby.wasm/blob/main/packages/npm-packages/ruby-wasm-wasi/example/hello.html をデモしていました。 次のようなタグの中に書いたRubyスクリプトがブラウザで動かせます。

<script type="text/ruby">
  puts "Hello, world!"
</script>

さらに、https://github.com/ruby/ruby.wasm/blob/main/packages/npm-packages/ruby-wasm-wasi/example/lucky.html を使ったデモでは、 RubyスクリプトからJavaScriptのグローバルオブジェクトを参照しDOMを操作していました。

require "js"
document = JS.global[:document]
button = document.getElementById "draw"

おお。

さらにさらにCDNエッジでWebAssembly化したRubyランタイムを動かすデモ( https://irb-wasm.vercel.app/ )ではirbがうごきます。 gemをrequireできるし、エラー表示も普通にRubyだし、 本当にRubyが動いているかんじです。 というインパクトのあるデモからの、実装上の苦労話が聞けました。

WebAssemblyはポータブルな仕様なので、出来ないことがたくさんあります。 例えば時刻を取ることができません。 つまり時計が存在する環境で動かされているとは限らないからです。 まあでも、それでは不便なので、今ではWASIという、WebAssembly向けのシステムコールインタフェースの仕様があります。 WASIの時刻を取るインタフェースを使えば、例えばブラウザではDate.now()が実行されて時刻が取得できるみたいな感じらしいです。

最近はCDNエッジでもWebAssemblyが動きます。 ただし、大抵のCDNエッジはワンバイナリのWebAssemblyを要求します。 そこで、wasi-vfsというバイナリファイル上に仮想的にRubyスクリプトを埋め込めるファイルシステムをつくって、そこからruby スクリプトを読み込む仕組みを作ったそうです。 作ったって・・・すごいですよね。

リポジトリの コミット履歴 https://github.com/kateinoigakukun/wasi-vfs/commits/v0.2.0 を見ると半年くらいで作ったみたいです。 rustで書かれているんですかね? なんかよくわからないですけど、すごいですね。という感想を抱きました。

つくったruby.wasmはnpmパッケージで公開されているそうです。 ruby-head-wasm-wasi - npm なんですかね? 前出のデモで使っている https://cdn.jsdelivr.net/npm/ruby-head-wasm-wasi@0.3.0-2022-09-06-f/dist/browser.script.iife.js との関係がよくわかりません。 バンドルする前のソースコードhttps://github.com/ruby/ruby.wasm/blob/a64de622c5d2a7cbd2147ef789d38a6e6f5cdb18/packages/npm-packages/ruby-wasm-wasi/src/browser.script.tsのようです。

  1. ruby.wasmを読み込む
  2. RubyVMを初期化
  3. HTML上の<script type="text/ruby">を探してきてRubyスクリプトを取得
  4. RubyVM で Rubyスクリプトを実行

という感じでruby.wasmで気持ちよくRubyスクリプトを動かすための仕掛けみたいです。 今の<script type="text/ruby">はsrc属性に対応していないので、外部のRubyスクリプトを読み込めません。 この辺をハックしたら読み込めるように出来そうです。

じつはここからが本題で、WebAssemblyにRubyを移植するための大きな課題が二つあったそうです。

  1. 例外処理
  2. FIber
  3. 保守的GC

これらは、関数の中にはいる外に出るのを呼び出した順には行いません。 例外がおきたら、関数スタックを全部飛ばして、例外処理に入ります。 Fiberも、Fiberの動きは上手くイメージできないのですが、あるFiberの処理から突然別のFiberの処理に切り替わります。 GCは、作ったオブジェクトを全部なめるのが難しいそうです。 どうもこの辺の動きがWebAssemblyでは素直に書けないそうです。 WebAssemblyはスタックマシンだからとかなんか言ってたような気もしますが、なにもわかりません。

これを解決するためにAsyncifyというアルゴリズム?ライブラリ?プログラムを変換するツール?を使ったそうです。

twitter.com

どうやらツールみたいです。

現在のruby.wasmには制限があります。

  • スレッド
  • C拡張ライブラリの実行
  • ファイルサイズ
  • 性能

スレッドとC拡張ライブラリはWASIにいまのところ対応するインタフェースがないようです。 ファイルサイズは25MBくらいあるそうです。 性能はmrubyと同じぐらいで、CRubyの半分くらいだそうです。

いきなりCRubyとおなじことができるとは思ってなかったので、大分動いていて、現時点で相当すごいです・・・。

これらの成果は2021年度Rubyアソシエーション開発助成金成果報告でのもののようです。 助成金出して、スーパーすごい若手のコミッターを獲得する流れになっています。 Rubyアソシエーションの開発助成金の仕組み、すごいですね。 そしてRubyKaigiの基調講演でメジャーデビュー。 おお、なんかすごいエコシステムだ。

参考