Ruby:重复执行select vs AND谓词?

时间:2018-09-17 11:28:39

标签: ruby performance

如果我想选择同时满足谓词arrp_1的数组p_2的所有元素,那么我有两种实现方式:

选项1:

arr.select{|x| x.p_1}.select{|x| x.p_2}

选项2:

arr.select{|x| x.p_1 && x.p_2}

两者之间有显着差异吗?在我的用例中,谓词p_1p_2所减少的列表要多得多,而p_2p_1要昂贵。因此,我怀疑将p_1放在p_2之前会更快。但是上面的两个选项都有区别吗?

2 个答案:

答案 0 :(得分:0)

您似乎已经知道谓词的性能特征和数据的形状,这很棒!

有区别吗?简而言之,是的-评估顺序不同:

# Option 1
arr[0].p_1
arr[1].p_1
arr[2].p_1
...
arr[n].p_1
arr[0].p_2
arr[1].p_2
arr[2].p_2
...
arr[n].p_2

# Option 2
arr[0].p_1
arr[0].p_2
arr[1].p_1
arr[1].p_2
arr[2].p_1
arr[2].p_2
...
arr[n].p_1
arr[n].p_2

现在,重要吗?这取决于非常情况和上下文的副作用。作为示例,让我们探索一些场景:

阻止,缓冲的I / O

假设p_2昂贵得多的原因是因为它执行某些I / O操作,例如写入磁盘。可能是这种输出操作被缓冲的情况,虽然Ruby运行时可能从p_2调用返回,但是直到再次调用p_2时,输出仍在刷新,从而阻塞了它。

在这种特殊情况下,选项2更快,因为p_1的计算可以在相互阻塞的p_2调用之间的过渡期间继续进行。

缓存未命中

假设p_1之所以快是因为可以缓存其计算。我们还假设调用p_2会以某种方式破坏高速缓存,使得随后的p_1调用会丢失高速缓存:

  • 也许它也添加到缓存中,并且缓存被填满,逐出值
  • 也许缓存被时间驱逐,并且由于p_1花费的时间太长,在p_2次调用之间清除了缓存的数据

在这种情况下,选项1更快,因为分组的p_1调用能够利用缓存。

共享有限的内存

假设p_1p_2调用都需要大量内存。也许通过交错它们,必须使两者所需的资源始终可用,从而达到系统的内存限制,从而损害性能。

在这种情况下,选项1更快,因为一旦完成所有p_1调用,就可以释放用于保留其资源的内存,以供以后的p_2调用使用。

答案 1 :(得分:-1)

根据您所说的,我已经建立了基准:

require 'benchmark'

N = 1000

# the fast method
def p1(arr_param)
  # lazy init of the arr_param, so it returns 20 times true and 80 times false
  (arr_param << Array.new(20, true) << Array.new(80, false)).flatten! if arr_param.empty?

  # shorter sleep
  t = Time.now.to_f
  while true
    break if Time.now.to_f - t > 0.000_01
  end
  arr_param.shift
end

# the slow method
def p2
  # longer sleep
  t = Time.now.to_f
  while true
    break if Time.now.to_f - t > 0.001
  end
  true
end

# testing arrays
arr = (1..100).to_a
truth_arr = []

Benchmark.bm(7) do |b|
  b.report('chain') { N.times { arr.select { |_| p1(truth_arr) }.select { |_| p2 } } }
  b.report('and') { N.times { arr.select { |_| p1(truth_arr) && p2 } } }
end

结果是:

#=>              user     system      total        real
#=> chain    78.422000   0.000000  78.422000 ( 78.789006)
#=> and      78.375000   0.000000  78.375000 ( 79.313160)

因此,似乎两种方法都一样快。不过,比我更有知识的人将不得不解释为什么。