バックエンドエンジニアの田中悠大(@ytnk531)です。 RubyKaigi 2022で三重県に来ています。2日目(9/9)に聴いたセッションについて、簡単に紹介させていただきます。
ruby/debug - The best investment for your productivity
ruby/debug - The best investment for your productivity - RubyKaigi 2022
Rubyのデバッグツールであるruby/debugを紹介する発表でした。
ある調査によると、Rubyプログラムのデバッグにはbinding.pryを使っている開発者が多いようなのですが、ruby/debugを使ってもっと効率よくデバッグしよう、という趣旨でした。
ruby/debugはデバッグに役立つ機能を3つ提供しています。
- Step-debugging
デバッグ対象のコードを実行中に停止して、1行ずつ実行できる機能。定義されている変数や呼び出せるメソッドを確認したり、任意のコード片を実行したりできる。 - Frame navigation
デバッガで止めているコードの呼び出し元をたどる機能。呼び出し元やそのさらに元の実行コンテキスト(フレーム)切り替えることができる。 - Breakpoint commands
コードの実行を止める位置(ブレークポイント)を、コマンドで指定できる機能。ソースコードの編集せずに、メソッド名や行番号によってブレークポイントを設置できる。
さらに、便利な機能としてScriptable breakpointの紹介もありました。ブレークポイントに到達した際に、実行したいデバッグコマンドを指定できる機能です。変数の確認など、毎回実行したいコードを予め書いておいて自動的させることで、デバッグの手間を減らすことができます。
# doを使うと、指定したデバッガのコマンドを実行してプログラムの実行を再開します binding.b do: 'watch @a' # preを使うと、指定したデバッガのコマンドを実行してプログラムの実行を停止します binding.b pre: 'p bar()'
READMEに詳しい説明があります。
ruby/debugはVSCode拡張も提供しているので、手軽に利用できるようです。ruby/debugには便利な機能がつまっているので、使いこなして効率的なデバッグを行えば、開発スピードをかなり上げられそうだと感じました。
Implementing Object Shapes in CRuby
Implementing Object Shapes in CRuby - RubyKaigi 2022
主にインスタンス変数へのアクセスの高速化を目的として実装が進められている、Object Shapeの仕組みと実装の詳細について解説する発表でした。
Rubyはインスタンス変数を配列に格納しています。同時に、各インスタンスはインスタンス変数名とその変数が格納されている配列のインデックスの組み合わせを保持しています。インスタンス変数にアクセスする際は、この組み合わせをたどって配列のインデックスを探し、得られたインデックスを使ってインスタンス変数の配列にアクセスします。
この挙動を実現するために、Rubyではクラスのインスタンス変数の定義が変更されていないかなどの確認するつくりになっており、計算コストの面で改善の余地があるそうです。Object Shapeは、この問題を解決してより効率的にインスタンス変数へのアクセスを行えるようにするアイディアです。
Object Shapeには、オブジェクトのプロパティが記録されています。プロパティというのは、保持しているインスタンス変数とそれがfrozenであるかどうかです。
Object Shapeはプロパティが変わるたびに新しく生成されます。その際、もとのObject ShapeのIDを保持していて、親のObject Shapeを辿れるようになっています。この親子関係によって、ツリー構造が作られます。Object Shapeはインデックスを持っていませんが、インスタンス変数の数はIDと対応しているので、Object Shapeのツリーを辿ることによって、所望のインスタンス変数が追加されたShapeのID-1がインデックスとして利用できます。
Object Shapeは、同じプロパティを持つインスタンス間であれば共有されます。また、キャッシュすることによりアクセスを高速化できます。
今の所、Object Shapeを利用した実装によるパフォーマンス向上は数%とあまり大きくないそうです。JITと組み合わせることにより高速化が見込まれるようで、今後の進展に大きな期待が持てそうです。
Method-based JIT compilation by transpiling to Julia
Method-based JIT compilation by transpiling to Julia - RubyKaigi 2022
高速なRubyプログラムを書くために、RubyをJuliaに変換するトランスパイラを作ったというお話でした。 こちらに発表資料があります。
Rubyではメソッドの再定義が可能ですが、この性質によりメソッド実行のたびにメソッドが再定義されていないか確認する必要があります。この挙動はデータ分析など特定のシチュエーションでは特に使わないことが多いため、メソッドを再定義できなくなる代わりに速くするような方法を考えたそうです。
PythonにはnumbaというJITコンパイラがあります。numbaは特定のオプションを与えると、メソッドを再定義できなくなる代わりに極力CPUの命令だけで計算するような高速なマシンコードを生成できます。このようなマシンコードを得る方法を考えたところ、numbaとマシンコードの生成方法はJuliaのアーキテクチャに近いので、RubyをJuliaにトランスパイルしてみよう、と考えたそうです。
Juliaへのトランスパイルにあたっては、RubyのコードをASTに変換し、それをJuliaの文法でフォーマットし直します。Ruby上の配列や一部の型は、そのままだとJuliaでは動かないので、RubyのリテラルをJuliaで扱える形にするメソッドをJulia側に実装したそうです。
Juliaへのトランスパイルで生成されたマシンコードは、期待通り少ない命令で高速に実行することができたそうです。(特定のケースでは遅い、という話をしていた気がするのですが、詳細を失念してしまいました)。
高速に動作させるために、一度他の言語に変換して実行する、という非常にワイルドな解決方法でとても興味深い内容でした。実際にかなり高速化しているのもすごいです。
おわりに
2日目も非常に興味深い発表ばかりでした。最終日も気になったセッションがあればレポートを投稿する予定です。
メドピアでは一緒に働く仲間を募集しています。 ご応募をお待ちしております!
■募集ポジションはこちら
https://medpeer.co.jp/recruit/entry/
■開発環境はこちら