はじめに
Hamada.rbでCRubyのFloatクラスをbuiltinで書き直してみた記事です。どちらかといえば作業ログな内容です。
builtinとは?
過去にいくつか記事にしているので詳細はそちらを見て頂ければと思います
gamelinks007.hatenablog.com
gamelinks007.hatenablog.com
gamelinks007.hatenablog.com
やったこと
とりあえず、お試ししたいだけなので雑にinteger.rbに以下のコードを追加
class Float def to_s Primitive.attr! 'inline' Primitive.cexpr! 'flo_to_s(self)' end def inspect Primitive.attr! 'inline' Primitive.cexpr! 'flo_to_s(self)' end def hash Primitive.attr! 'inline' Primitive.cexpr! 'flo_hash(self)' end def to_f self end def abs Primitive.attr! 'inline' Primitive.cexpr! 'rb_float_abs(self)' end def magnitude Primitive.attr! 'inline' Primitive.cexpr! 'rb_float_abs(self)' end def zero? Primitive.attr! 'inline' Primitive.cexpr! 'flo_zero_p(self)' end def to_i Primitive.attr! 'inline' Primitive.cexpr! 'flo_to_i(self)' end def to_int Primitive.attr! 'inline' Primitive.cexpr! 'flo_to_i(self)' end def nan? Primitive.attr! 'inline' Primitive.cexpr! 'flo_is_nan_p(self)' end def infinite? Primitive.attr! 'inline' Primitive.cexpr! 'rb_flo_is_infinite_p(self)' end def finite? Primitive.attr! 'inline' Primitive.cexpr! 'rb_flo_is_finite_p(self)' end def next_float Primitive.attr! 'inline' Primitive.cexpr! 'flo_next_float(self)' end def prev_float Primitive.attr! 'inline' Primitive.cexpr! 'flo_prev_float(self)' end def positive? Primitive.attr! 'inline' Primitive.cexpr! 'flo_positive_p(self)' end def negative? Primitive.attr! 'inline' Primitive.cexpr! 'flo_negative_p(self)' end end
次にnumeric.cで以下の部分のコードを削除
rb_define_method(rb_cFloat, "to_s", flo_to_s, 0); rb_define_alias(rb_cFloat, "inspect", "to_s"); rb_define_method(rb_cFloat, "hash", flo_hash, 0); rb_define_method(rb_cFloat, "to_f", flo_to_f, 0); rb_define_method(rb_cFloat, "abs", rb_float_abs, 0); rb_define_method(rb_cFloat, "magnitude", rb_float_abs, 0); rb_define_method(rb_cFloat, "zero?", flo_zero_p, 0); rb_define_method(rb_cFloat, "to_i", flo_to_i, 0); rb_define_method(rb_cFloat, "to_int", flo_to_i, 0); rb_define_method(rb_cFloat, "nan?", flo_is_nan_p, 0); rb_define_method(rb_cFloat, "infinite?", rb_flo_is_infinite_p, 0); rb_define_method(rb_cFloat, "finite?", rb_flo_is_finite_p, 0); rb_define_method(rb_cFloat, "next_float", flo_next_float, 0); rb_define_method(rb_cFloat, "prev_float", flo_prev_float, 0); rb_define_method(rb_cFloat, "positive?", flo_positive_p, 0); rb_define_method(rb_cFloat, "negative?", flo_negative_p, 0);
あとはbenchmarkディレクトリ以下にbenchmark.ymlを以下のように作成します
prelude: |
n = 4.2
benchmark:
to_s: |
n.to_s
inspect: |
n.inspect
hash: |
n.hash
to_f: |
n.to_f
abs: |
n.abs
magnitude: |
n.magnitude
zero?: |
n.zero?
to_i: |
n.to_i
to_int: |
n.to_int
nan?: |
n.nan?
infinite?: |
n.infinite?
finite?: |
n.finite?
next_float: |
n.next_float
prev_float: |
n.prev_float
positive?: |
n.positive?
negative?: |
n.negative?
loop_count: 20000000最後にmake && make installでビルドして make benchmark/benchmark.yml -e COMPARE_RUBY=~/.rbenv/shims/ruby -e BENCH_RUBY=../install/bin/rubyでベンチマークを取ってみた

built-rubyはruby 3.0.0dev (2020-09-07T04:29:42Z master 17a27060a7) [x86_64-linux]にさっきのbuiltinをパッチとして当てたもの
だいたい同じくらいかちょっと速いくらいになる模様
知りたかったこと
すこし前にInteger#sizeをbuiltinで書くとちょっと速くなるというチケットを書いた
で、どうも自分自身のみをレシーバとして受け取るメソッドはbuiltinでちょっと速くなりそうな傾向があるような気がしたので試してみた感じです。
Cでの実装がどうなっているかにもよりそうだけどちょっと速くできるかもしれないことが分かったので良かった。