はじめに
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での実装がどうなっているかにもよりそうだけどちょっと速くできるかもしれないことが分かったので良かった。