Rubyでメソッドのインライン展開をする
Rubyでメソッドのインライン展開をするRinlineというgemを作って、RubyちこくKaigiで発表した。
見るならSpeakerdeckよりもGoogle slidesの方がオススメ。オリジナルデータだし、URLがリンクになっている。
この記事では、スライドの概略的なことと、スライドに書かなかった補完を書く。
Rinlineとは
Rubyでメソッドのインライン展開をするGem。メソッド呼び出しの回数を減らしてコードを高速化するのが目的。
次のような使い方がある。
require 'rinline' class C def foo bar end def bar puts 42 end end Rinline.optimize do |r| # Cクラスのfooメソッド内のメソッド呼び出しが最適化される r.optimize_instance_method(C, :foo) # Cクラスの全てのインスタンスメソッド内のメソッド呼び出しが最適化される。 r.optimize_instance_methods(C) # Cクラス内の全てのインスタンスメソッドとクラスメソッド内のメソッド呼び出しが最適化される。 r.optimize_class(C) # Cクラスと、Cクラスのネームスペース以下に定義されたクラス(C::Bクラスなど)が最適化される。 r.optimize_namespace(C) end
基本的にはoptimize_namespace
を使えば良いと思う。
で、速くなるの?
なると言えばなるし、ならないと言えばならない。
たとえば次のようなマイクロベンチマークに対しては確実に効果が出ている。
require 'benchmark' class C def plain m + n end def optimized m + n end def hand_optimized 1 + 2 end def m 1 end def n 2 end end require 'rinline' Rinline.optimize do |r| r.optimize_instance_method(C, :optimized) end i = C.new Benchmark.bm(20) do |x| x.report('plain') { 100000000.times { i.plain } } x.report('optimized') { 100000000.times { i.optimized } } x.report('hand_optimized') { 100000000.times { i.hand_optimized } } end
$ ruby benchmark/simple.rb user system total real plain 5.716059 0.000000 5.716059 ( 5.719808) optimized 3.791726 0.000000 3.791726 ( 3.793648) hand_optimized 3.660404 0.000000 3.660404 ( 3.662295)
ただし、実アプリケーションに対しては効果が現れないか、そもそも最適化がうまく通らない。
optcarrotに対して最適化を実行することは成功しているのだけど、最適化してもしなくてもFPSに優位な差がでなくて悲しい。
また、RuboCopに対して最適化をしようとするとエラーで死ぬ。
スライド中でも話したのだけど、define_method
を使って定義されているメソッドを埋め込もうとして死んでしまう。
どうやって実装しているの?
スライドで説明しているけど分かりづらいと思う。
optimize_instance_method
メソッドがクラスとメソッド名を受け取る。
この情報があるとUnboundMethodが取得できます。そしてUnboundMethodからはRubyVM::AST::Nodeが取得できます。
そこからゴニョゴニョするとメソッド定義のソースコードが取れるので、それをごちゃごちゃと書き換えていい感じにしてevalします。
メソッド定義のNodeを見た時に、その中のVCALLやFCALLは(class_evalとかされなければ)そのメソッドが定義されているクラスのinstance_method
メソッドでメソッドオブジェクトを取得できるというのがキモです。
これに興奮してノリで作ったのがRinline。
まとめると、動いているRubyプログラムからASTを取れるというのが画期的で、RubyVM::AST.ofが便利すぎるという話です。
まだPoCという感じだし実用的ではないのだけれど、RubyVM::AST.ofを使うとこんな面白いこともできてしまうのだぞという気持ちです。