Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

emruby: ブラウザで動くRuby

銀座Rails #32
https://ginza-rails.connpass.com/event/207692/

  • Be the first to comment

  • Be the first to like this

emruby: ブラウザで動くRuby

  1. 1. emruby: ブラウザで動くRuby 銀座Rails #32 Yusuke Endoh 1
  2. 2. 自己紹介:遠藤侑介 (@mametter) • クックパッドで働くフルタイムRubyコミッタ • Ruby 3添付の静的解析ツールTypeProf作ってます • https://github.com/ruby/typeprof • でも今日はぜんぜん違う話をします 2
  3. 3. emruby: ブラウザの上で動くRuby https://mame.github.io/emruby/ 3
  4. 4. emrubyの狙い • ブラウザでRubyが動くのは楽しい • 頑張ればTryRuby(お試し環境)くらいにはなるか? • 将来的にJavaScriptの代替になるかはWASM次第? • Rust / Go / KotlinなどもWASM出力に対応してるので • おことわり • この発表にはRailsもRuby言語もほとんど出てきません • Rubyのビルドの知識が少し身につきます 4
  5. 5. Rubyをブラウザで動かす関連研究 • Opal: JavaScriptで書かれたRubyインタプリタ • https://github.com/opal/opal • Artichoke: Rustで書かれたRubyインタプリタ(WASM出力対応) • https://github.com/artichoke/artichoke • repl.it: Ruby 1.8をEmscriptenしたもの(らしい) • https://github.com/replit-archive/emscripted-ruby • Ruby on WebAssembly: mrubyをEmscriptenしたもの • https://github.com/blacktm/ruby-wasm • RubyのNaClサポート(2012~2017) • よくまとまってる記事 • https://blog.unasuke.com/2021/products-about-webassembly-and-ruby/ 5
  6. 6. アジェンダ • ➔WASM / Emscriptenとは • emrubyが動くまで • まとめ 6
  7. 7. WebAssembly (WASM) • ブラウザの上で動く実行ファイル形式 • 2017年頃からメジャーブラウザが対応している • JavaScriptより速くて (?) 小さいらしい 7
  8. 8. Emscripten • C/C++のプログラムをWASMに変換するコンパイラ • LLVMベース • デモ一覧(古そう):https://github.com/emscripten- core/emscripten/wiki/Porting-Examples-and-Demos • http://kripken.github.io/boon/boon.html • https://files.unity3d.com/jonas/AngryBots/ • http://coolwanglu.github.io/vim.js/emterpreter/vim.html 8
  9. 9. Emscriptenの基本的な使いかた 9 #include <stdio.h> int main() { printf("hello, world!¥n"); return 0; } emcc hello.c –o hello.js && node hello.js emcc hello.c –o hello.html
  10. 10. アジェンダ • WASM / Emscriptenとは • ➔emrubyが動くまで • まとめ 10
  11. 11. 前提知識:Rubyのふつうのビルド • Rubyソースのディレクトリで次のコマンドを打つ • ./configure: 環境ごとにビルド方法を調整する • どのシステム関数が使えるか、コンパイラオプションが使えるか • OS、コンパイラ、バージョンなどの違いを調べる •make: ソースコードをコンパイルする • まずminirubyという制限版ruby実行ファイルを作る • minirubyを使ってスクリプト(Rubyで書かれている)を動かし、 拡張ライブラリや最終的なruby実行ファイルを作る 11 ./configure && make
  12. 12. 話の流れ • minirubyをWASMにする • 本当のrubyをWASMにする • 最終目標:irbを動かす? 12
  13. 13. ./configure && makeのEmscripten化 • Emscriptenはconfigure+makeに対応している • emconfigure / emmakeはビルドをうまくだまして Emscriptenコンパイラを使わせる • これだけ……ではない • 実用プログラムがゼロ変更でビルドできることは無いと思う 13 emconfigure ./configure && emmake make
  14. 14. Emscriptenが未実装のC関数に対処する • 問題:Emscriptenで利用できないC関数がいっぱいある • popenがない • pthread_createはあるがpthread_killはない • pthread_createはあるがpthread_attr_getguardsizeがない • pthread_sigmaskはあるけど実際には動かない (!) 、など • configureの盲点をつくような未実装がいろいろあった • 対処:Rubyのconfigureを改善して対応した • コミッタなので、Ruby側を直接変更しまくった 14
  15. 15. Rubyは関数の引数の数にルーズだった • C言語では、関数に引数を余分に渡しても良い (!?) • C言語仕様違反だが、 多くのCコンパイラで動く • Rubyはこれに依存していた • Emscriptenのオプションで 対応した • -s EMULATE_FUNCTION_POINTER_CASTS=1 15 int foo(int a) { printf("%d¥n", a); } int main() { int(*foo2)(int,int) = (int(*)(int,int))foo; foo2(42, 43); // 42 } 1引数の関数fooを2引数で呼び出す例
  16. 16. miniruby.wasmできた! • 2018年はこの段階で公開した • 残念なお知らせ • EMULATE…オプションがEmscirptenから削除された • コンパイルできなくなった • どうしたか • 放置した → 3年経ったら、Ruby側が直っていた! • 微修正で2021年1月に再ビルドに成功した 16
  17. 17. 話の流れ • minirubyをWASMにする • ここまでできた • 本当のrubyをWASMにする • 最終目標:irbを動かす? 17
  18. 18. ruby.wasmを作るには • ふつうのrubyのビルドには、minirubyが必要 • しかしminiruby.wasmはLinuxで実行できない • クロスコンパイルする • ビルド環境とはちがう環境の実行ファイルを作ること • Linuxでruby.exe(Windowsの実行ファイル)を作る、とか • 今回はLinuxでruby.wasmを作る • emconfigureはかえってややこしくなるのでやめた 18
  19. 19. Rubyのクロスコンパイル • Rubyのconfigureはクロスコンパイルに対応している • minirubyの代わりにビルド環境のrubyを使ってくれる • これで一応ruby.wasmはできた • が、全然動かないのでデバッグ&ドキュメント&ソース読み 19 $ ./configure ¥ --build x86_64-pc-linux-gnu ¥ --host wasm32-unknown-emscripten ¥ CC=emcc LD=emcc AR=emar RANLIB=emranlib $ make ビルド環境 対象環境 Emscripten
  20. 20. 問題:Rubyの保守的GC • 保守的GCとは • マシンスタックの値がオブジェクトの参照であると仮定して マーク対象とするガベージコレクタの方式 • https://ja.wikipedia.org/wiki/%E3%83%9E%E3%83%BC%E3%82%AF%E3%83%BB%E3%82%A2%E3%83%B3%E3%83%89%E3%83%BB%E3%82%B9%E3%82%A4%E3%83%BC%E3% 83%97#%E4%BF%9D%E5%AE%88%E7%9A%84%E3%81%AA%E3%82%AC%E3%83%99%E3%83%BC%E3%82%B8%E3%82%B3%E3%83%AC%E3%82%AF%E3%82%BF • つまり意図的にC言語仕様違反なメモリアクセスをする • Emscriptenのメモリモデルでは全然動かない • 対処:Emscriptenが保守的GC用のAPIを用意していた • emscripten_scan_stack / emscripten_scan_registers • スタックの先頭と終端がわかる、これらを使うようにした 20
  21. 21. 余談:Fiberに対応する(未完) • Rubyは2018年末にFiberの一部をアセンブリで実装した • Emscriptenでx86アセンブリはコンパイルできないので コンパイルエラーになっていた • 対処:EmscriptenのAPIを使って実装した • emscripten_fiber_init / emscripten_fiber_swap • コンパイルオプション -s ASYNCIFY と合わせて使う • miniruby.wasmでは動いたが、ruby.wasmでは動かない • 原因未解明、今後の課題 • とりあえずFiber使わなければ問題ない 21
  22. 22. 問題:動的リンクができない • つまり、拡張ライブラリの require ができない • require "ripper"したらripper.soを動的リンクする • しかしEmscriptenは動的リンクに未対応(たぶん) • 解決:ripperを静的リンクした • 他にも必要な拡張ライブラリを色々足した 22 $ ./configure ¥ --with-static-linked-ext --with-ext=ripper … $ make
  23. 23. その他Emscripten特有っぽい話 • リンクが失敗する(htonsが見つからない、とか) • -lcでlibcを明示的にリンクすれば動いた • -fstack-protectorも対応してないようなので消した • すぐメモリ不足エラーになる • Emscriptenはデフォルトでメモリサイズを固定確保する • サイズ可変にするオプションをつけた (-s ALLOW_MEMORY_GROWTH=1) • stack overflowの検出が動かないので止めた、など 23
  24. 24. ruby.wasmできた! • require "ripper.so"も動く • ある程度複雑なRubyスクリプトも動く 24
  25. 25. 話の流れ • minirubyをWASMにする • 本当のrubyをWASMにする • ここまでできた • 最終目標:irbを動かす? 25
  26. 26. irbを動かすのに必要なもの • Rubyインタプリタ(できた) • ripper.soなどの拡張ライブラリ(できた) • irbのソースコード(あるけどまだ組み込んでない) • 端末エミュレータ(無い) 26
  27. 27. 仮想ファイルシステム • Emscriptenのfile_packagerツールで作れる • irbやrubygemsなど必要なRubyソースコードをまとめた • fs.jsとfs.dataができた • がんばってロードできるようにした • コンパイルオプションに-s FORCE_FILESYSTEM=1追加 • fs.jsを<script>で呼ぶだけ……なのだが意外と苦労した 27
  28. 28. xterm.jsを組み込む • xterm.js: ブラウザで動く端末エミュレータ • https://xtermjs.org/ • VS Codeでも使われている • 残念なお知らせ • Emscriptenは標準入出力の実装がいまいち • とりあえずの対応 • ライン編集はxterm.js側でやり、irbには行単位で送る • reline(irbの新しい編集機能)の活用は今後の課題 28
  29. 29. ということで https://mame.github.io/emruby/irb/ 29
  30. 30. CPU 100%を防ぐ • Emscriptenの生成物はほぼ同期で動く(asyncでない) • 入力待ちをポーリングでやるみたい(ゲーム想定?) • 対処:別スレッド(Web Worker)で動かすようにした • 通信方法はvim.wasmに習った(SharedArrayBuffer使用) https://rhysd.hatenablog.com/entry/2019/06/13/090519 • 残念なお知らせ:5月に動かなくなる見込み 30
  31. 31. ということで • (かなり妥協したけど)irbがブラウザで動いた! • rubygems、did_you_meanなども一応動いているっぽい 31
  32. 32. 落ち穂拾い • ruby.wasmのサイズ:29 MB • コンパイルオプションで調整して8 MB • -Os: 省サイズ重視で最適化する • -g0: デバッグ情報を省く 32
  33. 33. Emscripten所感 • 夢の技術ではない • 現実のC言語コードをゼロ変更でビルドできることは無い • いっぱい問題に遭遇する • が、とてもよくできている • 一生懸命調べればたいてい対処方法やAPIがある • 検索に頼らずドキュメントを通して読むのが早道 • 動いたらとても嬉しい 33
  34. 34. まとめ • ブラウザで動くRuby、emrubyを紹介しました • 大体Ruby側で対応したのでたったこれだけでビルドできる 34 $ ./configure ¥ --build x86_64-pc-linux-gnu ¥ --host wasm32-unknown-emscripten ¥ --with-static-linked-ext ¥ --with-ext=ripper,date,strscan,io/console,…,psych ¥ optflags=-Os debugflags=-g0 ¥ CC=emcc LD=emcc AR=emar RANLIB=emranlib $ make
  35. 35. 今後の予定 • ほそぼそとメンテナンスするつもり • WASMが大ヒットする日に備える • そのとき「RubyもWASM対応してます」と言いたい • RubyからJSやDOMを操作できたらいいなあ • 当面はOpalを使うのがいいと思います • WASM版TryRubyができたらいいなあ • Opalであまり問題はないですが 35

×