2017/04/09(日) [n年前の日記]
#2 [ruby] 配列の初期化って時間がかかるのだな
昨日書いた Scale2x の処理を行う Ruby スクリプトを、もうちょっと最適化できないものかと弄っていたのだけど。
ループ内をアレコレ弄って「あまり速くならねえな」とガッカリした直後、ふと、配列の初期化はしなくてもいいんじゃないかと気が付いて。配列の確保だけするようにしてみたら処理時間が二桁減って愕然。 *1
配列の初期化って結構処理時間を食うのだな…。いやまあ、メモリを確保して中身を書き換えていくわけだから当然だろうけど。にしても、まさか条件分岐が山ほど入ってるループ処理部分より配列の初期化で時間を食ってたとは…。
 ループ内をアレコレ弄って「あまり速くならねえな」とガッカリした直後、ふと、配列の初期化はしなくてもいいんじゃないかと気が付いて。配列の確保だけするようにしてみたら処理時間が二桁減って愕然。 *1
配列の初期化って結構処理時間を食うのだな…。いやまあ、メモリを確保して中身を書き換えていくわけだから当然だろうけど。にしても、まさか条件分岐が山ほど入ってるループ処理部分より配列の初期化で時間を食ってたとは…。
◎ 測ってみた。 :
こんな感じで。環境は Windows10 x64 + Ruby 2.2.6-p396 mingw32版。
結果。
配列の中身をゼロクリアするとやっぱり遅くなるようで…。多次元配列じゃなく一次元配列にしてゼロクリアしたら速くならないか、と思ったけど甘かった。でもないか。いや、しかし、多次元配列の確保より、一次元配列でゼロクリアするほうが遅いんだよな…。
ちなみに、Ruby 2.2 ではなく Ruby 2.3 でも試してみたけど、さほど違いは無く。ていうかむしろ微妙に遅くなったり。まあ、誤差かもしれないけど。
# Array.new のベンチマーク
require 'benchmark'
w, h = 512, 256
Benchmark.bm(7) do |x|
  # 最初にやってたソレ
  x.report("[][][]") do
    a = Array.new(h).map { Array.new(w).map { Array.new(4, 0) } }
  end
  # 書き方を変えてみた
  x.report("[][][]B") do
    a = Array.new(h) { Array.new(w) { Array.new(4, 0) } }
  end
  # ループを馬鹿みたいに回してみた
  x.report("loop") do
    a = []
    h.times do
      b = []
      w.times do
        b << [0, 0, 0, 0]
      end
      a << b
    end
  end
  # 初期化を省いた
  x.report("[][]") do
    a = Array.new(h).map { Array.new(w) }
  end
  # 一次元配列にしてみた
  x.report("[]") do
    a = Array.new(w * h * 4, 0)
  end
end
結果。
user system total real [][][] 0.047000 0.000000 0.047000 ( 0.049730) [][][]B 0.047000 0.000000 0.047000 ( 0.041508) loop 0.031000 0.000000 0.031000 ( 0.032914) [][] 0.000000 0.000000 0.000000 ( 0.000188) [] 0.000000 0.000000 0.000000 ( 0.000549)
配列の中身をゼロクリアするとやっぱり遅くなるようで…。多次元配列じゃなく一次元配列にしてゼロクリアしたら速くならないか、と思ったけど甘かった。でもないか。いや、しかし、多次元配列の確保より、一次元配列でゼロクリアするほうが遅いんだよな…。
ちなみに、Ruby 2.2 ではなく Ruby 2.3 でも試してみたけど、さほど違いは無く。ていうかむしろ微妙に遅くなったり。まあ、誤差かもしれないけど。
*1: もっとも、渡された配列の中身をそのまま使う形になった ―― ポインタだけを書き換えてる形になったようなものだから、元の配列の内容を弄られるとおかしなことになるけれど、それは元々そうなってたから今更だし。
 
[   ツッコむ ]
以上です。