Rubyの性能向上: CをRubyで書き直す
Rubyの性能比較
- 最近の言語比較リポジトリでは、RubyはRやPythonより速い一方で、3番目に遅い言語と評価されている。
- ベンチマークは「Loops」と「Fibonacci」の2種類で構成され、それぞれループと条件分岐、関数呼び出しのオーバーヘッドおよび再帰性能を強調している。
RubyとNode.jsの性能比較
- M3 MacBook Proでは、Ruby 3.3.6はループの例で28秒、フィボナッチの例で12秒かかる。
- Node.jsはどちらの例も約1秒で終わる。
- M2 MacBook Airでは、Rubyの性能はさらに悪化する。
ベンチマークの意味
- こうしたベンチマークは、実際にはそれほど大きな意味を持たないかもしれない。
- Pythonは最も遅い言語と評価されたが、GitHubでは最も多く使われている言語である。
- プログラミング言語は効率的であるべきだが、言語の有用性と生産性は性能より重要である。
YJITの適用
- YJITを適用すると、フィボナッチの性能は大きく改善する。
- ループの例では、性能向上はわずかである。
Rubyコードの最適化
Range#eachはCで書かれているため、YJITで最適化できない。
Integer#timesはRuby 3.3でCからRubyに変換され、YJITによる最適化が可能になった。
Array#eachはRuby 3.4でCからRubyに変換された。
Integer#times の最適化
Integer#succは i += 1 より高速に動作する。
- YJITは
Integer#timesを最適化し、性能を大きく向上させる。
Array#each の最適化
Array#eachはRuby 3.4でCからRubyに変換され、YJITによる最適化が可能になった。
Primitive モジュールを使って、CコードをRuby内で評価する。
Ruby Microbenchリポジトリ
- さまざまなRubyバージョンとYJITを使ってベンチマークを実行する。
- Ruby 3.4 YJITでは性能が大きく向上している。
Range#each の最適化
Range クラスを純粋なRubyで実装することで、性能を向上させられる。
YJIT標準ライブラリ
- YJITチームは、CコードをRubyに置き換えることで性能向上を進めている。
with_yjit ブロックを使い、YJITが有効なときにはRuby実装を利用する。
YJIT最適化の調査
- YJITはRuby VMのバイトコードを機械語に変換し、性能を最適化する。
Integer#succ の機械語コードを分析することで、YJITの最適化過程を理解する。
1件のコメント
Hacker Newsのコメント
ループの例は10億回繰り返し、ネストしたループを使用している。このベンチマークは最初の2行で99%以上の時間を消費すると推測される
uがコンパイル時点で分からなくても、内側のループはいくつかの命令に置き換えられる可能性があるRubyの今後のバージョンへの言及があり、Ruby 3.4.0は今年のクリスマスに、Ruby 3.5.0は来年のクリスマスにリリース予定とのこと
Rubyへの愛着は今も残っている。Matzに感謝
Integer#succの性能改善PRが2024年初頭にあり、それによってInteger#succを使う理由を理解したInteger#succはループメソッドを書き換える際に使われ、インタプリタではopt_succ (i = i.succ)がputobject 1; opt_plus (i += 1)より高速に処理される#succは可読性のためによく使っており、UUIDライブラリの#bytesメソッドで2回使うことで、コードを読むときに「ビットスライシングモード」を維持できるTruffleRubyに関する経験を共有しており、TruffleRubyはNode.jsより速く、BunやGolangに近い
Rubyは非常に速くなっており、TruffleRubyはさらに印象的だ
YJITがRustで書かれているとは知らなかった
Pythonはベンチマークで最も遅い言語だったが、2024年10月時点ではGithubで最も多く使われている言語だった
より多くの言語を含む古い言語比較リポジトリがある
Advent of Codeの解法に大きな変化をもたらしており、驚くほどよく似ている