どういうこと?
現在開発中のRubyでは一部の実装をRubyで書くことができるようになっています。(ちなみに、Ruby 2.7ではいくつかのメソッドがRuby(とC)で実装されています)
詳しい話は、RubyKaigi 2019 での ko1さんのこのセッションを見ていただければと思います。
ざっくりいうと、キーワード引数やCコード内で例外処理でよしなにしているケースではRubyで実装したほうが速くなることがあるという感じです。
ちなみに、最近ではいくつかのメソッドを完全にRubyで実装するというケースもあります。(Integer#to_iとか)
今回はそのケースを紹介します。
高速化できるケース
具体的に速くなるケースとしては
- freezeされたオブジェクト(インスタンス)を返すメソッド
- 返す値が固定(必ずtrueを返すなど)
この二件が同時に満たされたときですね。
freezeされたオブジェクト(インスタンス)が速いのはまあ何となくわかります。
ですが、返す値が固定というのは「どうしてだろう?」となりますね。
なぜ高速化するのか?
これはおそらくですが、Rubyのメソッドキャッシュによるものではないかと思います。
Rubyにはインラインメソッドキャッシュというキャッシュがあります。
インラインメソッドキャッシュとは、以下のようなコードがあった時「実行されるメソッドの定義は常に同じだろう」と考え、それをキャッシュしておくことで高速化するというものです。
5,times{ puts :hoge }
つまりメソッドが返す値が固定である場合、インラインメソッドキャッシュが効きやすく高速になるということです。
ちなみに、インラインメソッドキャッシュの詳しい内容は以下の記事などを読んでいただければと思います。
具体例
以下のようなコードで大体1.3倍くらいとかの高速化ができます。
class TrueClass def to_s "true".freeze end alias_method :inspect, :to_s def |(bool) true end end
ちなみに、ベンチマークはこんな感じです。
benchmark: to_s: | true.to_s inspect: | true.inspect or: | true | false loop_count: 1000000
これを ruby 2.8.0dev (2020-08-20T04:24:55Z master 6509652c13) [x86_64-linux]にパッチとして当ててベンチマークを実行すると以下のような結果になりました。
sh@MyComputer:~/rubydev/build$ make benchmark/trueclass.yml -e COMPARE_RUBY=~/.rbenv/shims/ruby -e BENCH_RUBY=../install/bin/ruby # Iteration per second (i/s) | |compare-ruby|built-ruby| |:--------|-----------:|---------:| |to_s | 66.001M| 91.927M| | | -| 1.39x| |inspect | 70.464M| 97.220M| | | -| 1.38x| |or | 61.434M| 86.484M| | | -| 1.41x|
compare-rubyがパッチを当てる前のもので、built-rubyがパッチを当てたものです。
おおよそ1.3~1.4倍くらい速くなりそうです。