クックパッドは RubyKaigi 2024 に参加していました!イベントレポート

はじめに

こんにちは。レシピ事業部プロダクト開発グループの堀内 (@Sota_Horiuchi)です。普段はバックエンドの開発を行っている新卒2年目のエンジニアです。

RubyKaigi 2024が 2024 年 5 月 15 日から 17 日に沖縄県那覇市で開催され、クックパッドからは総勢 24 名が参加しました。参加したメンバーのうち 13 名が新卒 3 年目までのエンジニアであり、社内の若手バックエンドエンジニアがほぼ参加していました。 また、クックパッドは Wi-Fi スポンサーとして協賛しており、更に 16 日の夜には Cookpad Drinkup at RubyKaigi 2024 と称して懇親会を開きました。 ドリンクアップの話はまた後日記事として公開されると思います。

RubyKaigi 2024 について

セッションは全部で 53(去年 51 )で、スポンサー数は 97(去年 89 )でした。スポンサー数は個人スポンサーが存在していた 2011 年を抜けば過去最大の値でした。 今回最も大きく変わっていたのはイベントの数であり、今年は 23(去年 9 )のイベント(パーティ)が設定されていました。夜な夜な那覇市のどこかで Rubyist の集まりが開かれており、自分も連日さまざまなところで飲みいろいろな方と交流ができました。 また、夜だけでなく昼には他社のエンジニア同士で集まりランチに向かうという取り組みがちらほら起こっており、他社の Rubyist の方と交流することができました。

RubyKaigi 2024の全体感

今回の RubyKaigi のセッションをテーマ別にざっくり分類してみると、パフォーマンスに関するセッションが多く見られました。

パフォーマンスについては Matz さんの Keynote でも第1にパフォーマンス、第2にパフォーマンス...とにかくこれからは(も)パフォーマンス改善に注力していきたいと述べられていました。具体的には VM の高速化、メモリ効率の改善、スレッド=パフォーマンス改善という需要への対応、ソフトウェア面での高速化等で、今後どれだけ改善していくか楽しみです。

また、パフォーマンス以外では、型やパーサー、 wasm の話も多くありました。型については day 3 の Ruby Committers and the World でも白熱した議論が起こり面白かったです。Ruby の型についてはまだまだ方が付きそうにありませんね。 パーサーの話で言えば、Matz さんからシンタックス・モラトリアムという、少なくとも今年いっぱいは文法に手を加えないようにし、パーサ周りの改善に注力していこうという提案がなされました。手書きパーサの Prism と文法定義から生成する Lrama が今後それぞれどのように発展し、互いに影響を与えていくのか注目です。世はまさに大パーサ時代。

パフォーマンス

先ほどRubyKaigi 2024ではパフォーマンスの話が多かったと述べましたが、ここでは特に印象に残ったパフォーマンスのトークについて紹介します。

取り上げるのはRubyそのものの実行速度の向上を目的とした、Speeding up Instance Variables with Red-Black Trees です。

本トークは Ruby のインスタンス変数へのアクセスを平衡二分探索木である赤黒木で高速化したという内容です。既にこの内容は Ruby 3.4にマージされており、Pull Request は右です (https://github.com/ruby/ruby/pull/8744 )。 この高速化においては、Ruby 3.2 で導入された、インスタンス変数の管理方法である Object Shape が前提となっています。( RubyKaigi 2022でCRubyに実装された時の発表 https://rubykaigi.org/2022/presentations/jemmaissroff.html

このトークを取り上げた理由としては、私自身がデータ構造やアルゴリズムを使った高速化について興味があったのと、思ったより前提となるこの Object Shape の日本語解説が少なく、Object Shape を理解する上で誰かの参考になればと思い取り上げました。

そもそも Ruby (CRuby) ではインスタンス変数を配列で保存しているため1、インスタンス変数にアクセスするには、当該配列においてインスタンス変数と対応している要素のindexを知る必要があります。Ruby VM ではインラインキャッシュと呼ばれるしくみにより、index をキャッシュすることで高速化を行なっています。しかし、同じインスタンス変数名があるクラスが複数登場するとキャッシュがうまく機能しないという問題がありました。そんな時、Ruby 3.2で導入された Object Shape がこれを解決しました。 Object Shape により「別のインスタンスだが、同名インスタンス変数のindexが同じである」ということを認識することができるようになり、index が同じなので使いまわせるとして、キャッシュヒット率を向上させることができるようになりました。

それでもキャッシュヒットしない(キャッシュミス)が起こる事例はかなりあるようで、例えば下のようなメモ化は Object Shape と相性が悪いようです。

class Sample
  def initialize(x)
    @hoge = x
  end

  def foo(x)
    @_foo ||= x
  end

  def bar(x)
    @_bar ||= x
  end
end

Object Shape ではインスタンス変数の初呼び出しにより Shape が決定されます。そのため、インスタンスにおける foo と bar の最初の呼び出しの順番が異なるだけで違う Shape になってしまい、キャッシュを使うことができません。結果としてキャッシュミスが起こります。キャッシュミスが起こるとインスタンス変数の index を獲得する必要があります。Object Shape を使った場合、そのクラスが複雑な Shape でない場合はインスタンス変数の index を獲得するために O(|インスタンス変数|) の計算処理が必要でした。 そのため、キャッシュミス時の index の高速取得が求められていました。

Object Shape 自体はそもそも木構造でそれぞれの Shape を管理していて且つそれぞれのインスタンス変数の index が各ノードに格納されています。本トークのアイディアはこの木構造をできるだけ均一の高さにすることで index の探索を早くするというものです。

個人的な感想として、赤黒木は同じ平衡二分探索木であるAVL木に比べて木の形が歪になりやすいことが知られているので、ノードの検索は AVL 木の方が速いはずです。そのため、AVL 木で実装した場合とどちらが速いのか気になりました。 また、本トークを聴くまでは Object Shape についてあまり詳しく知らなかったので、本セッションにより Object Shape の気持ちも多少感じることができました。 加えて、Ruby の高速化のため Object Shape に優しいコード(インスタンス変数の代入は同じ順序で行う方が良い)を書こうという気になれました。 ただ一方で社内の人と議論をした際、「Ruby のユーザーとしてはどう書いてもある程度速くなってほしい」という話題になり、普段から Object Shape を考えながらコードを書くことは難しいよねという話になりました。 もちろん普段から常に意識することは難しいですが、例えばメモ化をできるだけ控えるとか、コンストラクタにおける変数初期化の際に変数の順番を一定にするツールを使う(あるのか?)とかである程度 Object Shape フレンドリーなコードを書くことができるのかなとも思いました。参考2

今後もどのような高速化手法が出てくるか楽しみです。

終わりに

次回の RubyKaigi 2025 は愛媛県松山市にて開催予定です。 愛媛県には千と千尋の神隠しの舞台になった道後温泉やゆるキャラのバリィさん3などがいるのでとても楽しみです。