しょんぼり技術メモ

まいにちがしょんぼり

Ruby1.9ではBignumのインクリメントはFixnumのインクリメントに比べて倍ぐらい遅い

タイトルで完結シリーズ。しかも割と「いまさら」ではありますが。

システムの稼働中に延々とインクリメントする変数が必要だったので、実際に実験してみた。
先に結果を。

***** Hello, this is a test benchmark script for
      Incrementing Integer number. Is Fixnum faster than Bignum? *****

      your Ruby version is: 1.9.2
      your Ruby's Max value of Fixnum is: 4611686018427387903
      [ 4611686018427387903.class => Fixnum ]
      [ 4611686018427387904.class => Bignum ]

* Fixnum test
  range: [0(Fixnum) - 1000000(Fixnum)] 50times trial.
 -> 4.994632071 sec. (50times)
    0.09989264142 sec/trial.
    9.989264141999999e-08 sec/increment.

* Intermediate test
  range: [4611686018426887903(Fixnum) - 4611686018427887903(Bignum)] 50times trial.
 -> 8.038869072 sec. (50times)
    0.16077738144 sec/trial.
    1.6077738144000002e-07 sec/increment.

* Bignum test
  range: [4611686018427387904(Bignum) - 4611686018428387904(Bignum)] 50times trial.
 -> 11.139960695 sec. (50times)
    0.2227992139 sec/trial.
    2.227992139e-07 sec/increment.

FixnumとBignumの変数を100万回インクリメントする処理を50回やった平均。
Fixnumのテストでは[0〜1,000,000]まで、Bignumのテストでは[Fixnum_MAX+1 〜 Fixnum_MAX+1+1,000,000]まで。
また、Fixnum_MAXをまたぐ形(Intermediate)でも100万回のインクリメントを計測してみた。

FixnumとBignumの差が約6.2秒に対して、FixnumとIntermediate、IntermediateとBignumの差が約3.1秒なので、まあ妥当なところでしょう。

実験に使ったソースはこちら:

#!/bin/env ruby

def _bench
  bef = Time.now
  yield
  diff = Time.now - bef
  return diff
end

def bench_it(from, num_add, times)
  return _bench do
    times.times {
      num = from
      num_add.times {
        num += 1
      }
    }
  end
end

# from http://ja.w3support.net/index.php?db=so&id=535721
FIXNUM_MAX = (2**(0.size * 8 -2) -1)

puts "***** Hello, this is a test benchmark script for"
puts "      Incrementing Integer number. Is Fixnum faster than Bignum? *****"
puts
puts "      your Ruby version is: #{RUBY_VERSION}"
puts "      your Ruby's Max value of Fixnum is: #{FIXNUM_MAX}"
puts "      [ #{FIXNUM_MAX}.class => #{(FIXNUM_MAX).class} ]"
puts "      [ #{FIXNUM_MAX+1}.class => #{(FIXNUM_MAX+1).class} ]"
puts


num_add = 1_000_000
times   = 50

from  = 0
puts "* Fixnum test"
puts "  range: [#{from}(#{from.class}) - #{from+num_add}(#{(from+num_add).class})] #{times}times trial."
elapsed_time = bench_it(from, num_add, times)
puts " -> #{elapsed_time} sec. (#{times}times)"
puts "    #{elapsed_time / times} sec/trial."
puts "    #{elapsed_time / times / num_add} sec/increment."
puts


from = FIXNUM_MAX - (num_add / 2)
puts "* Intermediate test"
puts "  range: [#{from}(#{from.class}) - #{from+num_add}(#{(from+num_add).class})] #{times}times trial."
elapsed_time = bench_it(from, num_add, times)
puts " -> #{elapsed_time} sec. (#{times}times)"
puts "    #{elapsed_time / times} sec/trial."
puts "    #{elapsed_time / times / num_add} sec/increment."
puts


from = FIXNUM_MAX + 1
puts "* Bignum test"
puts "  range: [#{from}(#{from.class}) - #{from+num_add}(#{(from+num_add).class})] #{times}times trial."
elapsed_time = bench_it(from, num_add, times)
puts " -> #{elapsed_time} sec. (#{times}times)"
puts "    #{elapsed_time / times} sec/trial."
puts "    #{elapsed_time / times / num_add} sec/increment."