LoginSignup
30
9

More than 3 years have passed since last update.

Rubyのインクリメント速度のバージョンごとの比較

Posted at

TL;DR

Rubyでは i++ ができない。
たまたま i++ しようとしたらエラーになったのでそういえばインクリメント演算子がRubyだと出来なかった気がするなと思い、念のために調べてみた。

公式ドキュメントじゃないけど、割と自明な話しだと思うので公式ではなく個人のテックブログを参照した。詳細に調べられていたので問題ないかなと思う。
そして恐らくだが以前も同じブログを参照した気がする。

Ruby にインクリメント演算子のようなものが無い理由

選択肢としてはi += 1i.succi.nextの3パターンあるがなにが違うのか気になったので調べたところ先人がすでに調べていた。

RubyのIntegerにおけるsuccとnext

ただこの記事も前述の記事も2014年〜2015年なので現在のRubyバージョンでは状況が変わっている可能性があるし、なんなら自分が問題を起こしたバージョン(ruby@2.4.1p111)とで結果が異なる可能性があるのではないか?と思い、せっかくなので調べてみた。

結論:

3つの中でsuccが比較的安定、速い。
次点でi += 1nextはFixnumで他に比べて遅い。

Rubyでインクリメントしたいとき、普段は i += 1i.succを使う。
パフォーマンスが気になる箇所では i.succ を使うのがベター。次点でi += 1i.nextを使ってたらレビューで指摘してあげたほうがいいかもしれない……くらいかな。

2つ目のブログで書かれている状態とほぼ同じ結果になったのでおそらくは原因も同じと推測される。
各バージョンのコードまでは検証するつもりがなかったので省略してるけど多分同じなんだろうと思う。

検証環境:

MacOS version 10.14.5(18F132)
MacBook Pro (Retina, 15-inch, Late 2013)
プロセッサ 2.3 GHz Intel Core i7
memory 16 GB 1600 MHz DDR3

検証項目:

各バージョンの最新 + 問題を起こしたバージョンで検証。
本来は何度か実行してその平均値で出すんだろうけどメンドイので1回しかやってないので何かの検証目的でいる場合はサンプルデータ1くらいの扱いでお願いします。

検証コードは2つ目のブログの検証コードをそのまま流用。
理由としては計測する方法が変わると期待する結果も変わってしまう可能性があるため、できるだけ条件を同じにしたかったから。

実行したコード(ファイル名が違うだけ)

example.ruby

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

  # Fixnum#succ
  x.report("Fixnum succ") do
    a = fixnum
    cnt.times{ a = a.succ }
  end

  # Fixnum#next
  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

  # Bignum#succ
  x.report("Bignum succ") do
    a = bignum
    cnt.times{ a = a.succ }
  end

  # Bignum#next
  x.report("Bignum_next") do
    a = bignum
    cnt.times{ a = a.next }
  end
end

ruby@2.4.1p111

Fixnumのときのnextだけが遅い。ブログの現象と同じ。

$ ruby example.rb
        user     system      total        real
 +=1 Fixnum  4.720000   0.000000   4.720000 (  4.724997)
 Fixnum succ  4.720000   0.000000   4.720000 (  4.727951)
 Fixnum next  5.870000   0.010000   5.880000 (  5.879860)
 +=1 Bignum 10.570000   0.000000  10.570000 ( 10.582265)
 Bignum succ 10.340000   0.010000  10.350000 ( 10.354154)
 Bignum_next 10.110000   0.010000  10.120000 ( 10.121698)

ruby@2.6.3

Fixnumでnextだけが遅いという現象が発生してたのでこれはなにか別のアプリケーションが影響してるのか?と思ってもう一回実行したんだけど結果ほぼ変わらずだったのでどうやらnextがFixnumのときだけ遅いっぽい。

$ ruby example.rb
       user     system      total        real
+=1 Fixnum  5.052053   0.007104   5.059157 (  5.068701)
Fixnum succ  4.829195   0.004266   4.833461 (  4.840140)
Fixnum next  6.159282   0.002336   6.161618 (  6.165148)
+=1 Bignum 10.592028   0.004909  10.596937 ( 10.601052)
Bignum succ 10.377451   0.003914  10.381365 ( 10.384787)
Bignum_next 10.320459   0.003760  10.324219 ( 10.327414)

ruby@2.5.5

こちらもブログの現象と同じといえる。

$ ruby example.rb
       user     system      total        real
+=1 Fixnum  4.719072   0.001677   4.720749 (  4.722440)
Fixnum succ  4.651602   0.001740   4.653342 (  4.654881)
Fixnum next  5.748883   0.001787   5.750670 (  5.752244)
+=1 Bignum 10.454205   0.007352  10.461557 ( 10.471455)
Bignum succ 10.872434   0.028426  10.900860 ( 10.935718)
Bignum_next 10.624259   0.024823  10.649082 ( 10.674851)

ruby@2.4.6

ブログの(ry

ruby example.rb
       user     system      total        real
+=1 Fixnum  4.430000   0.000000   4.430000 (  4.437439)
Fixnum succ  4.570000   0.020000   4.590000 (  4.615233)
Fixnum next  5.950000   0.020000   5.970000 (  6.008219)
+=1 Bignum 10.600000   0.030000  10.630000 ( 10.660327)
Bignum succ 10.050000   0.020000  10.070000 ( 10.093179)
Bignum_next 10.370000   0.020000  10.390000 ( 10.410649)

ruby@2.3.7

たまたまなのかもしれないけど一番古いRubyのバージョンが一番遅いだろうとなんとなく考えていたが結果が真逆だったのが面白い。
昔のほうが特定の条件下では高速に動作するのか。
とはいえそのためにあえて2.3.7使うひとはいないと思うけど、どうしても速度がほしいなら別の言語選ぶだろうしな。

ruby example.rb                                                                                                  [05 30 00:37:43]
       user     system      total        real
+=1 Fixnum  4.170000   0.000000   4.170000 (  4.173693)
Fixnum succ  4.150000   0.000000   4.150000 (  4.154439)
Fixnum next  5.280000   0.000000   5.280000 (  5.282451)
+=1 Bignum  9.170000   0.000000   9.170000 (  9.176064)
Bignum succ  9.230000   0.010000   9.240000 (  9.233617)
Bignum_next  9.420000   0.000000   9.420000 (  9.428198)
30
9
7

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
30
9