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

这是我的猜测:与其他两种方法不同,方法 withinject不断创建越来越大的字符串。所有这些(除了最后一个)都是临时的,必须进行垃圾收集。那是浪费内存和CPU。这也是Shlemiel the Painter 算法的一个很好的例子。

... Spolsky 进行类比的低效率是重复连接 C 风格的空终止字符数组(即字符串)的不良编程实践,其中必须从头重新计算目标字符串的位置每次都使用字符串,因为它不是从先前的连接中结转的。...

方法map创建了许多小字符串,因此,至少,它不会花费太多时间来分配内存。

更新

正如 Yevgeniy Anfilofyev 在评论中指出的那样,您可以通过不创建任何大字符串来避免创建许多大字符串。只需继续附加到memo.

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

这是有效的,因为两者都String#+String#<<字符串返回了一个新值。

于 2013-04-05T08:12:23.597 回答