自分はRubyで数値をインクリメントするときはいつも「+=1」を使用しているのですが、succメソッドとnextメソッドもインクリメントに使用できるので、どれが一番速いのか調べてみました。
require 'benchmark'
Benchmark.bm do |x|
cnt = 100000000
fixnum = 1
bignum = 1 << 64
x.report("+=1 Fixnum") do
a = fixnum
cnt.times{ a += 1 }
end
x.report("Fixnum succ") do
a = fixnum
cnt.times{ a = a.succ }
end
x.report("Fixnum next") do
a = fixnum
cnt.times{ a = a.next }
end
x.report("+=1 Bignum") do
a = bignum
cnt.times{ a += 1 }
end
x.report("Bignum succ") do
a = bignum
cnt.times{ a = a.succ }
end
x.report("Bignum_next") do
a = bignum
cnt.times{ a = a.next }
end
end
実行結果
user system total real
+=1 Fixnum 5.550000 0.010000 5.560000 ( 5.607988)
Fixnum succ 5.100000 0.000000 5.100000 ( 5.104199)
Fixnum next 7.200000 0.000000 7.200000 ( 7.241638)
+=1 Bignum 11.610000 0.010000 11.620000 ( 11.641881)
Bignum succ 11.960000 0.030000 11.990000 ( 12.039480)
Bignum_next 11.330000 0.010000 11.340000 ( 11.352631)
結果だけ見てみると、Bignumの時はどれもほとんど差がありませんが、Fixnumだとnextだけ遅い結果が出ました。
そこでnumeric.cの中のsuccの実装を見てみると
VALUE
rb_int_succ(VALUE num)
{
if (FIXNUM_P(num)) {
long i = FIX2LONG(num) + 1;
return LONG2NUM(i);
}
if (RB_TYPE_P(num, T_BIGNUM)) {
return rb_big_plus(num, INT2FIX(1));
}
return rb_funcall(num, '+', 1, INT2FIX(1));
}
こんな感じになっていたのですが、別でfix_succなるものがありました。
static VALUE
fix_succ(VALUE num)
{
long i = FIX2LONG(num) + 1;
return LONG2NUM(i);
}
そしてメソッド定義の部分を見ると
rb_define_method(rb_cInteger, "succ", int_succ, 0);
rb_define_method(rb_cInteger, "next", int_succ, 0);
rb_define_method(rb_cFixnum, "succ", fix_succ, 0);
こんな感じでsuccは2つ定義されていました。
つまりは、succメソッドはFixnumかBignumの違いで呼ばれていた関数が違っていたのではないでしょうか。rb_int_succはfix_succに比べ判定式があるので、その分Fixnumにおいてnextが遅くなったのかもしれません。
まだしっかりと調査しきれていませんが、結論としては「+=1」でいいやって感じです。
追記(2014/04/30)
記事のタイトルをNumericからIntegerに修正しました。