3

ベンチマークはこちら

require 'benchmark'

# create random array
arr = 40000.times.map { rand(100000).to_s }

r1 = ''
r2 = ''
r3 = ''

Benchmark.bm do |x|
    x.report {
        r1 = (arr.map { |s|
            "[#{s}]"
        }).join
    }

    x.report {
        r2 = arr.inject('') { |memo, s|
            memo + "[#{s}]"
        }
    }

    x.report {
        r3 = ''
        arr.each { |s|
            r3 << "[#{s}]"
        }
    }
end

# confirm result is same
puts r1 == r2
puts r2 == r3

ここに結果があります

       user     system      total        real
   0.047000   0.000000   0.047000 (  0.046875)
   5.031000   0.844000   5.875000 (  5.875000)
   0.031000   0.000000   0.031000 (  0.031250)
true
true

injectより速くする方法はありますか?

4

1 に答える 1

6

私の推測は次のとおりです。他の 2 つの方法とは異なり、アプローチ withinjectは、ますます大きな文字列を作成し続けます。それらのすべて (最後のものを除く) は一時的なものであり、ガベージ コレクションを行う必要があります。それはメモリとCPUの無駄です。これは、画家シュレミエルのアルゴリズムの良い例でもあります。

... Spolsky が類推した非効率性は、宛先文字列の位置を最初から再計算する必要がある、C スタイルの null で終わる文字配列 (つまり、文字列) を繰り返し連結するという貧弱なプログラミング手法でした。前の連結から持ち越されないため、毎回文字列の。...

with のアプローチでmapは多くの小さな文字列が作成されるため、少なくともメモリの割り当てにそれほど時間はかかりません。

アップデート

コメントで Yevgeniy Anfilofyev が指摘したように、作成しないことで多くの大きな文字列の作成を回避できます。に追加し続けるだけmemoです。

r2 = arr.inject('') { |memo, s|
  memo << "[#{s}]"
}

String#+と の両方が文字列の新しい値をString#<<返すため、これは機能します。

于 2013-04-05T08:12:23.597 に答える