ruby の Array は生のメモリに近いものだから、 push と pop は速いけど、 shift と unshift は遅い。
そう思っていたんだけど、思っていただけで測ったことがなかった。
で。測定してみた。
環境は ruby 2.5.0p0
まずはソースコード。
ruby2.5
require "benchmark"
r=Array.new(10) do
a=[0]*100_000_000
Array.new(3){ 1000*Benchmark.realtime{
a.push 1 # この行を、「a.pop」「a.unshift 1」「a.shift」 に変更する
} }
end
puts (0...3).map{ |ix|
"%.8f|" % ( r.map{ |e| e[ix] }.inject(&:+) / r.size )
}.join
測定結果
| 1回目 | 2回目 | 3回目 | |
|---|---|---|---|
| push | 0.01390 | 0.00030 | 0.00150 |
| pop | 0.00370 | 0.00040 | 0.00020 |
| unshift | 355.36490 | 0.00140 | 0.00020 |
| shift | 0.00370 | 0.00010 | 0.00000 |
push と pop は、予想通り速い。
でも push の初回はちょっと遅い。なんでだろ。
unshift は、一度目だけが遅い。
おそらく、1要素だけの unshift でも、3つ以上の要素を先頭に追加できるだけのメモリが確保されるのであろう。
shift も速い。メモリの移動はせず、先頭を指すポインタを動かしているのであろう。
というわけで、当初の予想は極一部しか当たっていなかった。
おまけの実験
せっかくなので
ruby2.5
require "benchmark"
r=Array.new(10) do
a=[0]*100_000_000
[
Benchmark.realtime{ a.shift }, #1
Benchmark.realtime{ a.shift }, #2
Benchmark.realtime{ a.unshift 3 }, #3
Benchmark.realtime{ a.unshift 4 }, #4
Benchmark.realtime{ a.unshift 5 }, #5
Benchmark.realtime{ a.unshift 6 }, #6
].map{ |x| x*1000 }
end
puts (0...6).map{ |ix|
"%.5f|" % ( r.map{ |e| e[ix] }.inject(&:+) / r.size )
}.join
という実験をしてみた。
結果は
| #1(shift) | #2(shift) | #3(unshift) | #4(unshift) | #5(unshift) | #6(unshift) |
|---|---|---|---|---|---|
| 0.00460 | 0.00090 | 0.00090 | 453.12790 | 0.00190 | 0.00070 |
こんな感じ。
あれ? #5 が遅いんじゃないの??