概要
- Ruby 3.5では オブジェクトの割り当て(allocation)が大幅に高速化 されました。
- ベンチマーク により、特にキーワード引数の最適化効果が顕著であることが示されています。
- Class#newの仕組み と、Ruby・C間の呼び出しコストの違いが解説されています。
- インライン化やYJITの有効化 によるパフォーマンス向上の詳細が述べられています。
- 本記事では 最適化の背景・技術的工夫 についても詳しく説明しています。
Ruby 3.5でのオブジェクト割り当て高速化
ベンチマークと測定方法
- Rubyアプリケーションでは 多くのオブジェクト割り当て が発生することが一般的であることを前提とすること
- Ruby 3.5では 割り当てパフォーマンスが最大6倍高速化 されていることを確認
- ベンチマーク手法 として、引数の種類(位置引数・キーワード引数)とYJIT有効/無効を比較すること
- initializeへの引数数を増やし、 パラメータ増加によるパフォーマンス変化 を測定すること
- 1イテレーションで複数回オブジェクトを割り当て、 ループ処理の影響を最小化 し割り当てコストを強調すること
ベンチマーク結果の解釈
- Ruby 3.5は 全ての割り当てタイプでRuby 3.4.2より高速 であること
- 位置引数の場合、 パラメータ数に関わらず一定の高速化比率 (YJIT無効で1.8倍、YJIT有効で2.3倍)であること
- キーワード引数の場合、 引数数が増えるほど高速化比率が向上 すること
- 例:キーワード引数3つで3倍、YJIT有効時は6.5倍以上の高速化を確認
Class#newのボトルネック分析
- Class#newは インスタンスの割り当てとinitializeの呼び出し のみを行うシンプルなメソッドであること
- 最適化余地は 割り当て処理の高速化 または initialize呼び出しのオーバーヘッド削減 にあること
- Rubyの仮想マシン(YARV)は スタックベース で値や引数を受け渡しすること
- Ruby→C間の呼び出しは 呼び出し規約の違いによる変換コスト が発生すること
- 位置引数はスタック→レジスタへのコピーのみで済むが、キーワード引数は Hash生成・設定が必要 となるため遅くなること
最適化の経緯と技術的工夫
- ...(ドットドットドット)によるパラメータ転送は、以前は 余計なオブジェクト割り当てが発生 していたこと
- ...の最適化により、 追加割り当てなしでパラメータ転送可能 となったこと
- Class#newのRuby実装を試みるも、 インラインキャッシュのヒット率低下 などの課題で断念すること
- YARV命令を追加し、 Class#newのインライン化 を実現することにより、実質的に高速化が可能となったこと
- インライン化とは、 呼び出し先の処理コードを呼び出し元に展開する ことで、呼び出しオーバーヘッドを削減すること
YJITとの連携と今後の展望
- YJIT有効時は さらなる高速化が可能 であること
- 今回の最適化は 今後のRuby高速化の基盤 となる提案であること
- Ruby 3.5以降、 オブジェクト割り当てが多いアプリケーションで顕著な効果 が期待できること
このように、Ruby 3.5では オブジェクト割り当ての根本的な最適化 がなされており、特に キーワード引数の利用が多いコードで劇的な高速化 が実現されています。今後のバージョンでも さらなるパフォーマンス向上の余地 が残されているため、Rubyユーザーにとって非常に有益なアップデートとなっています。