2025年4月16日から18日の3日間、愛媛県松山市にて、RubyKaigi 2025が開催されました。
タイミーでは、昨年に続き、世界中で開催される技術系カンファレンスに無制限で参加できる「Kaigi Pass」という制度を活用し、新卒内定者やインターン生を含む総勢16名が現地でカンファレンスに参加しました。
前回に引き続き参加レポートの第2回として、Day2 vol.1と題し、Day2で現地参加した各エンジニアの印象に残ったセッションとその感想をまとめてお届けします。
今後も、Day2 vol.2、Day3と続けてレポートを公開予定です。
各セッションごとに内容を整理し、参加者自身の視点から学びや気づきをまとめています。読者の皆様にとって、今後の学びの参考になれば幸いです。
Performance Bugs and Low-Level Ruby Observability APIs
概要
「遅すぎて不具合に感じる」というパフォーマンスの問題をPerformance Bugsとして紹介し、その問題は「遅さ」ではなく、CPU、メモリ、時間といったリソースの過剰消費の問題として捉えるべきとしました。その問題が本番環境やユーザー体験、コストに与える影響を評価することが重要とした上で、そのための調査にはRubyのObservability APIを使うと良いということが紹介されました。発表ではTracePoint APIやPostponed Job APIなど具体的なAPIと簡単なプロファイラの作り方を紹介していました。最後にPerformance Bugsとの向き合い方を紹介して終わっています。
主要APIの解説と活用例:
- low_level_toolkit gem: 低レベルAPIの利用例と簡単なツール群を提供。
- TracePoint API (内部イベント含む): メソッド呼び出し、クラス定義、例外発生に加え、オブジェクト生成(new_object)やGCの開始/終了(gc_start, gc_end)といったVM内部イベントもフック可能。オブジェクト割り当ての追跡やGCタイミングの計測デモが示された。コールバック内での制限(オブジェクト生成不可など)にも言及。
- Postponed Job API: 低レベルAPIのコールバック内で安全にRubyコード(オブジェクト生成やAPI呼び出しを含む)を実行させるための仕組み。「セーフポイント」と呼ばれるVMが安全な状態になったタイミングで実行される。GC完了時にRubyコードで情報を記録するデモが示された。
- Frame Profiling API: 低オーバーヘッドで現在のバックトレース(コールスタック)を取得できる。プロファイラ構築の基礎。
- Debugger Inspector API: フレームプロファイリングより高コストだが、はるかに詳細な情報(呼び出し元のオブジェクト、メソッド引数、ローカル変数、バインディング、命令シーケンスなど)にアクセスできる。デバッガ構築に利用される。呼び出し元のオブジェクトやそのバインディングを取得するデモが示された。
- GVL Instrumentation API: Global VM Lock (GVL)の取得・解放、スレッドの実行・待機状態遷移を追跡できる。スレッドがGVL待ちでどれだけ時間を費やしているかを計測するデモが示された。
感想
タイミー入社からの約5年間、自分はそれなりにパフォーマンスとは向き合ってきたと思っています。弊社でもDatadogを利用しているのですが、これまでのパフォーマンス改善でとても役に立ちました。パフォーマンス改善の話をすると、昔は超簡単なn+1の改善をすれば2倍3倍と早くすることができました。しかしながら明らかに低速な部分が減るとちょっとずつ丁寧な観測が要求されます。その際にメソッドコールやオブジェクト生成などが観測できるととても便利です。今回の発表では、そんな際に利用しているプロファイラがどうやって作られているのかを紹介しているものでした。言語やプロファイラの理解を深めるために一回触ったり作ったりしてみようと思います。プロファイラは小さいものを含めてまだ作ったことがないので楽しみです。
(@Juju_62q)
RubyKaigi前に #Rubygemsコードリーディング部でdd-trace-rbのソースコードを読んでいました。各Rubyバージョンへのパッチ対応をしつつ、C実装も独自に拡張しており、尚且つ型を書いていたので、とても練度の高いgemだなと。
発表では、TracePoint API、Postponed Job APIなどRuby VM内のAPIにはどんなものがあって、どのように活用できてどんなデータが取得できるのかという話がありました。
これらのAPIを使ってRelease GVL profilerをわずか107行で作ったという話がありましたね。
普段からモニタリングやログ探索の場面でDatadogにはかなりお世話になっており、便利に使っていました。内部実装に関しては知見がなかったので、プロファイラの実装に関してイメージが湧いたのは自分にとって有意義でした。
プロファイラは使うだけで作ったことはないのですが、イメージが湧いたので自分で作ってみようと思います。
P.S. IvoにセッションやDatadogへの感謝を直接伝えて、一緒に写真を撮ってもらいました!ありがとうございました!
(@dak2)
サービスの規模が大きくなっていくほどパフォーマンスの問題とも真摯に向き合っていく必要があります。自分はタイミーで初めて規模の大きいソフトウェア開発を経験したこともあり、パフォーマンスの問題との付き合い方は勉強中です。
これまではとにかく「遅ければ遅いほど問題だ」と考えていましたが、そうではなくパフォーマンス劣化によって「ユーザー体験や自社のコスト増にどの程度影響しているか」という視点が大事なんだという学びを得られました。
監視ツールでは純粋に処理に時間がかかっているものはわかりますが、それぞれがどの程度ユーザー体験やコスト増に繋がっているかはパッと見わからないです。この辺りの判断を行えるようになるためには各リソースにかかるコストを把握する必要があり、また様々あるドメインについて、レスポンスが悪化することによってどの程度ユーザーに不便をかけるのかも把握しておく必要がありそうだなと思いました。一人で全部把握するのは大変ですが、組織全体として誰かが知っていれば評価はできると思うので、誰がどの領域に詳しいか、誰も知らないものはどれかをきちんと把握し、これからもパフォーマンス問題に向き合っていきたいです。
今回紹介していただいたツールもパフォーマンス劣化の原因を特定するのに役立ちそうなので、一通り使えるようになりたいと思います。
(@rhiroe)
プロダクト開発は作って終わりではなく、日々モニタリングを行い改善していく必要があります。今までもdatadogを使ってモニタリングは行っていましたが、どのような仕組みでモニタリングを可能にしているのかは理解できていない状態でした。
発表では、低レベルなRubyの可観測性 (Observability) APIについての解説があり、複数のAPIが紹介されていました。また、パフォーマンスの問題をどう捉えるかという視点についても言及がされており、どれくらいユーザーやコストに対して影響を与えているかが大事であるというものでした。パフォーマンスは良い方が良い、というのは間違いないかなとは思っているのですが、どこからが修正すべき問題として捉えるのかどうか、というのは新しい観点であり新鮮でした。
今回の発表でプロファイラのイメージが少しだけ湧いたため、紹介されていたAPIやサンプルコードを用いてプロファイラの作成にも取り組んでみようと思いますし、今後の改善においては、ユーザーやコスト面の影響などを考えて優先度をつけていきたいです。
(@nissy325)
ZJIT: Building a Next Generation Ruby JIT
概要
ShopifyのRuby & Rails infrastructure teamのMaximeによるセッションでは、新しいRubyのJIT コンパイラであるZJITの開発概要が説明されました。ZJITは、将来20年以上にわたる利用を見据え、高性能、高保守性、高拡張性を目指しているようです。ZJITはメソッドベースのコンパイルと SSAに基づく中間表現を採用し、高速な関数呼び出しの実現を目指しています。初期のマイクロベンチマークでは、インタプリタやYJITよりも高速な結果が出ています。Ruby 3.5でオプショナルで利用可能になるとのこと。4.0では実験的に取り込まれる予定らしいです。今後は、より現実的なベンチマークでの性能向上とコンパイラの最適化に注力していくとのことです。
感想
一番気になっていたMaximeのセッション。このセッションではRubyの次の20年を見据えたZJITという新しいコンパイラや、YJITとの違いについて発表がなされました。
近年、Rubyのバージョンが上がるごとにYJITの性能も良くなっており、Rubyのバージョンを上げたら速くなった…!という体験をされている方も多いかと思います。
ただ、性能の伸びは徐々に鈍化しているようです。
Rubyは良い言語だけど、もし速度が遅ければ、パフォーマンス最適化要求の波に飲まれていずれ滅ぶという趣旨の発言もありました。
そういったモチベーションからZJITに取り組まれているとのこと。
ZJITはSSA(Static Single Assignment Form)に基づく中間表現を採用しているようです。SSAとは「すべての変数の使用に対して、その値を定義(代入)している場所がプログラム上で一箇所しかないように変数の名前を付け替えた中間表現形式」のようです。これは多くのコンパイラで広く使われている中間表現です。これによってコンパイラの設計が理解しやすくなり、拡張も容易になるとのこと。コンパイラについては何もわからないので、Deep Diveしてみようと思いました。
高速なJIT-to-JITの呼び出し、ポリモーフィックインラインキャッシュなどYJITでは難しかった高度な最適化機能の実装になるらしいです。
セッションの中で弊社のブログが紹介されましたね!これからも発信し続けていきたいです。
この記事にもあるようにRuby 3.4.2のYJITによる高速化の恩恵は受けられませんでしたが、次の YJIT、並びにZJITの動向に目が離せません。(ZJITのPRマージされたみたいですね!)
参考資料
- https://www.jstage.jst.go.jp/article/jssst/25/1/25_1_1_19/_pdf
- https://bugs.ruby-lang.org/issues/21221
- https://github.com/ruby/ruby/pull/13131
(@dak2)
Dissecting and Reconstructing Ruby Syntactic Structures
概要
Rubyの柔軟性と表現力を裏側で支えるParser(特にparse.y)が抱える課題と、解決アプローチを通して、Rubyの構文構造の根底にある原則とパターンについて学ぶことができます。
感想
これまでRubyでアプリケーション開発を行う上で、Parserの存在を特に意識することはありませんでした。しかし、この発表を通して、Rubyのシンプルに見える見た目の内部には、非常に複雑な構文構造が存在し、それがRubyの持つ柔軟性や豊かな表現力を支えていると実感することができ、発表者の@ydah_ さんの熱意も相まって、一気にこの分野への興味が出てきました。
重複した部分を特定し共通構造を抽出して抽象化するというアプローチは、日々のアプリケーション開発でも行われているものですが、それを長年にわたり多くのRubyコミッターによって開発されてきたparse.yに対して行うのは非常に困難だったのではと想像します。今回、Lramaの新しい記法を用いることで構造の抽象化を実現し、Rubyの柔軟な文法を維持しながら、保守性と可読性の向上を可能にしている点にとても感銘を受けました。
(@creamstew)
Speeding up Class#new
概要
このセッションでは、 RubyのClass.newのパフォーマンス改善について解説していました。現状のC言語実装では、RubyとCの呼び出し規約の切り替えなどにコストがかかる点が課題でした。このオーバーヘッドを解消するため、Ruby自身でClass.newを実装する新しい試みが紹介されました。これにより、多くの場合で高速化を実現しましたが、メモリ使用量の増加やスタックトレースのわずかな変化といったトレードオフも存在するとのことです。
感想
普段Railsアプリケーションの開発に携わる中で、Rubyのコードがどう動いているかについて気にすることはほとんどありません。ましてや、パフォーマンスを改善しようと考えたことはなかったので、Class.newのような基本的な部分に改善の余地があるとは想像もしていませんでした。このような改善に取り組む熱意を尊敬するとともに、こういった方々に我々は支えられているのだなと改めて感じました。
また、RubyとCがどのようにデータをやり取りしているかの深い部分を知ることができ(完全には理解できませんでしたが…)、よりRubyに詳しくなれたような気がします。
今後は、感謝の気持ちを忘れずに開発に臨むとともに、パフォーマンス改善をする際には「実は Rubyのコード自体にボトルネックがあるのでは?」という視点を持ちたいと思います。
(@otaksy)
“Class.newをCじゃなくRubyで動かす”、とだけ聞くと「Cの方が速いでしょ?Rubyで動かすと遅くならない?」と思いましたが、話を聞くとClass.newの呼び出し箇所が非常に多く、オブジェクト生成の部分だけを見た場合の速度はCで動かした方が速いが、RubyからCに切り替えるためのコストも含めるとRubyだけで動いた方が速いという説明を聞いて納得しました。
この改善によって一般的なユースケースではパフォーマンスの有意な改善が見られるそうなので楽しみです。Ruby3.5にこの変更が含まれる予定だそうですが、次はRuby4.0になるかもみたいな話もあり、楽しみが2倍です。
(@rhiroe)
Benchmark and profile every single change
概要
このセッションでは、Benchmark-Driven Development(ベンチマーク駆動開発)という手法が紹介されていました。その内容は、コーディングする前にベンチマークを設計し、コーディング前後でベンチマークの実行結果を比較することで、変更によるパフォーマンスの悪化を早期に発見できるというものです。ベンチマーク駆動開発を用いて、RubyフレームワークのSinatraを100倍高速化させたXinatraを実装する取り組みについても紹介されていました。
感想
これまでパフォーマンス改善を何度か経験してきましたが、多くの場合、パフォーマンスが無視できないほど悪化してから改善に着手していました。そのため、コーディング段階でベンチマークを実行し、パフォーマンスの悪化を防ぐというアプローチは、斬新でありながらも有効だと感じました。
また、N+1のような明確なボトルネックを解消した後に、チリツモでパフォーマンスが悪化している状態を何度も見てきたため、チリをツモらせないための解決策としても活用できそうです。
ただし、すべての開発にこの手法を適用すると、開発効率が落ちる可能性があるため、そのトレードオフは考慮する必要がありそうです。そのため、まずはパフォーマンスが特に重要な機能に絞って、この手法を実践してみようと思いました。
(@otaksy)
おわりに
RubyKaigi 2025 Day2 vol.1レポートでは、低レベルAPIやパフォーマンス改善などRubyの深層に迫るセッションを中心に、現地参加したエンジニアそれぞれの視点からの学びや気づきをお届けしました。
本レポートで取り上げたセッションでは、普段は見えないRubyの仕組みやパフォーマンスへの深い知見を得られたことで、Rubyの奥深さに一層興味が湧きました。
今後もDay2 vol.2、Day3とレポートを公開しますので、どうぞお楽しみに!