LoginSignup
4
0

More than 1 year has passed since last update.

【令和最新版】Array#sample と Random#rand ってどっちが速いんかな

Posted at

背景

以前に samplerand についてベンチマークを比較した記事を書きましたが、いくつか見直すべき点があったため改めて投稿したいと思います。

検証

Kernel.#rand より Random.rand

前記事では Random.rand と言いながら Kernel.#rand(つまり単に rand) を混同して使っていました。
なので改めて以下のように Kernel.#randRandom.rand を比較してみます。

num = 10000
count = 1000000

Benchmark.bm 20 do |r|
  r.report 'Kernel.#rand' do
    count.times do
      rand(num)
    end
  end

  r.report 'Random.rand' do
    count.times do
      Random.rand(num)
    end
  end
end
結果
                           user     system      total        real
Kernel.#rand           0.190434   0.000193   0.190627 (  0.190932)
Random.rand            0.084481   0.000194   0.084675 (  0.084819)

Random.rand のほうが2倍以上高速でした。

range より +1

rand の引数には Range オブジェクトを受け取ることが出来ますが、1以上10000以下のような範囲なら 1..10000Range より 10000Ingeger を与えて +1 するほうが高速です(rand(10000)0〜9999 の値をランダムで返すため)。

Benchmark.bm 20 do |r|
  r.report 'Random.rand(range)' do
    num.times do
      Random.rand(range)
    end
  end

  r.report 'Random.rand(+1)' do
    num.times do
      Random.rand(10000) + 1
    end
  end
end
結果
                           user     system      total        real
Random.rand(range)     0.243092   0.000319   0.243411 (  0.244819)
Random.rand(+1)        0.084825   0.000072   0.084897 (  0.085032)

3倍近く早くなってます。

結果

以上を踏まえて Array#sample も加えてベンチマークを取ってみます。

num = 10000
range = (1..num)
array = range.to_a
count = 1000000
 
Benchmark.bm 20 do |r|
  r.report 'Array#sample' do
    count.times do
      array.sample
    end
  end

  r.report 'Kernel.#rand(range)' do
    count.times do
      rand(range)
    end
  end

  r.report 'Kernel.#rand(+1)' do
    count.times do
      rand(num) + 1
    end
  end

  r.report 'Random.rand(range)' do
    count.times do
      Random.rand(range)
    end
  end

  r.report 'Random.rand(+1)' do
    count.times do
      Random.rand(num) + 1
    end
  end
end
結果
                           user     system      total        real
Array#sample           0.143643   0.000186   0.143829 (  0.144033)
Kernel.#rand(range)    0.137866   0.000146   0.138012 (  0.138163)
Kernel.#rand(+1)       0.194007   0.000154   0.194161 (  0.194374)
Random.rand(range)     0.244600   0.000151   0.244751 (  0.244960)
Random.rand(+1)        0.086753   0.000071   0.086824 (  0.086926)

結果的には Random.rand+1 するのが予想通り最速でしたが、Array#sample もそこまで遅いわけではないようです。
また、Range オブジェクトを渡す場合は Random.rand より Kernel.#rand のほうが速いというのは意外でした。もし1始まり以外の範囲を使いたい場合は Kernel.#rand を使っても良さそうです。それと range.to_a はループの度にやると非常に遅いので事前に用意しておくのが重要です。

最後に

ランチェスターではパフォーマンスにうるさいサーバーサイドエンジニア社員を募集しております!
まずはざっくばらんにお話しさせてください。下記からご応募お待ちしております。
https://herp.careers/v1/lanchester/Bucw0mXRKogc
https://www.wantedly.com/companies/lanchester
▼採用動画について:https://moovy.jp/job/651

4
0
1

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
4
0