性能:迭代除元素之外的数组

时间:2011-06-21 23:25:54

标签: ruby-on-rails ruby arrays performance indexing

我正在使用Ruby on Rails 3.0.7,我想迭代一个对象数组(类istances),除了id等于1的元素({{1} }引用数组[id]索引)。

我知道我可以在1语句“内部”使用if语句并执行此操作,检查每个“当前”\“已考虑”元素each。但是,由于数组中包含大量数据,我想找到另一种方法,以更高效的方式完成相同的事情(避免每次都运行if id == 1。)

我该怎么办?

4 个答案:

答案 0 :(得分:7)

  1. 让程序正常工作
  2. 资料
  3. 优化
  4. Donald Knuth said:

      

    我们应该忘记小事   效率,约占97%   时间:过早优化是   万恶之源。

    现在,您可以执行以下操作:

    def f
      do_something
    end
    
    f 0
    for i in 2..n
      f i
    end
    

    甚至:

    def f
      yield 0
      for i in 2..@n
        yield i
      end
    end
    
    f do |i|
      do_something
    end
    

    但你可能不希望想要做任何类似的事情,如果你这样做了,那只会在发现重要之后才会发生。

    最后,假设这个丑陋的技巧实际上让你的服务器运行得如此之快。值得吗?

答案 1 :(得分:1)

if声明是一种非常便宜的操作。您可以使用标准基准测试工具进行检查。

require "benchmark"

array = [1] * 100_000

Benchmark.bm do |bm|
  bm.report "with if" do
    array.each_with_index do |element, i|
      next if i == 1
      element - 1
    end
  end

  bm.report "without if" do
    array.each do |element|
      element - 1
    end
  end
end

结果:

                user     system      total        real
with if     0.020000   0.000000   0.020000 (  0.018115)
without if  0.010000   0.000000   0.010000 (  0.012248)

在100 000个元素阵列上差异大约为0.006秒。你不应该关心它,除非它成为一个瓶颈,我怀疑它会。

答案 2 :(得分:1)

a = ['a', 'b', 'c']
a.each_with_index.reject {|el,i| i == 1}.each do |el,i|
  # do whatever with the element
  puts el
end

恕我直言是一种更好的选择方式,而不是使用您自己的显式if语句。不过,我相信它会产生与使用if大致相同的性能,甚至可能会略低一些。

如果在基准测试之后,其他人已经建议您知道这个时间肯定比您允许的时间慢,并且选择导致缓慢,那么这可以很容易地修改以通过多种方式删除选择:

a = ['a', 'b', 'c']
n = 1
(a.first(n) + a.drop(n + 1)).each do |el|
  # do whatever with the element
  puts el
end

不幸的是我相信这也会比运行简单的if慢。我认为可能具有速度潜力的是:<​​/ p>

a = ['a', 'b', 'c']
n = 1
((0...n).to_a+((n+1)...a.size).to_a).map{|i| a[i]}.each do |el|
  # do whatever with the element
  puts el
end

但这又有可能变慢。

修改

基准为this gist。这些结果实际上让我感到惊讶,拒绝是迄今为止最慢的选择,其次是范围。完全没有删除元素后效果最高的是使用firstdrop来选择它周围的所有元素。

结果为百分比,不使用选择作为基线:

with if             146%
with first and drop 104%
without if          100%

显然,这高度依赖于你对元素的处理,这可能是Ruby可以执行的最快的操作。操作越慢,这些差异越小。一如既往:基准,基准,基准

答案 3 :(得分:1)

测试一个实际的for循环可能值五分钟。它可能在Ruby圈子中不受欢迎,但这并不意味着它永远不值得使用。当你调用每个或map或者其他什么时,这些方法都会以任何方式使用for循环。避免绝对。

它还取决于阵列可以达到多大,在某些n,一个可能变得比另一个快。在这种情况下,绝对不值得。

如果您不需要特定元素,则可能不需要将该行数据存储在数据库中。第1行和其他行之间有什么区别,换句话说,为什么要跳过它? id = 1的行总是会有相同的数据吗?如果是这样,将其存储为常数可能会更好,并会使您的问题没有实际意义。性能几乎总是会花费更多的内存。

除非Rails 3以不同的方式执行操作,并且您提取数据并使用id作为查找程序键,否则id = 1将位于元素0中。

不幸的是,Knuth的引用经常被误解,并习惯于为程序员受过足够教育而无法编写的糟糕,效率低下的代码提供借口,并考虑了5秒钟。当然,花一个星期试图加速你不知道的代码是一个问题或是一个小问题,但这更像是Knuth所说的。表演是计算机科学中最容易被误解和滥用的概念之一。