【问题标题】:Ruby: Increasing Efficiency红宝石:提高效率
【发布时间】:2016-06-18 03:23:22
【问题描述】:

我正在处理大量数据,我担心大规模运营的效率。经过基准测试,执行这串代码的平均时间约为 0.004 秒。这行代码的目标是找出每个数组位置中两个值之间的差异。在之前的操作中,111.111 被加载到包含无效数据的位置的数组中。由于一些奇怪的时域问题,我需要这样做,因为我不能只删除这些值,我需要一些可区分的占位符。我可能可以在这里使用'nil'。无论如何,回到解释。这行代码检查以确保两个数组在当前位置都没有这个 111.111 占位符。如果这些值是有效的,那么我执行数学运算,否则我想删除这些值(或者至少将它们从我正在写入的新数组中排除)。我通过在该位置放置一个“nil”然后压缩数组来完成此操作。

每个数组中 4000 个数据点的 0.004 秒时间并不可怕,但是这行代码执行了 25M 次。我希望有人能够对我如何优化这行代码提供一些见解。

temp_row = row_1.zip(row_2).map do |x, y| 
  x == 111.111 || y == 111.111 ? nil : (x - y).abs 
end.compact

【问题讨论】:

  • zip 这里完全不需要。
  • 是的,创建 5000 个线程后提高了 4000% - 这简直是天方夜谭 :)
  • 无拉链:pastie.org/10881223

标签: ruby-on-rails arrays ruby optimization benchmarking


【解决方案1】:

您正在浪费 CPU 在三元语句中生成 nil,然后使用 compact 删除它们。相反,使用rejectselect 来查找不包含111.111 然后map 或类似内容的元素。

代替:

row_1 = [1, 111.111, 2]
row_2 = [2, 111.111, 4]

temp_row = row_1.zip(row_2).map do |x, y| 
  x == 111.111 || y == 111.111 ? nil : (x - y).abs 
end.compact
temp_row # => [1, 2]

我会开始:

temp_row = row_1.zip(row_2)
                .reject{ |x,y| x == 111.111 || y == 111.111 }
                .map{ |x,y| (x - y).abs }
temp_row # => [1, 2]

或者:

temp_row = row_1.zip(row_2)
                .each_with_object([]) { |(x,y), ary|
                  ary << (x - y).abs unless (x == 111.111 || y == 111.111)
                }
temp_row # => [1, 2]

对不同大小的数组进行基准测试显示了一些值得了解的事情:

require 'benchmark'

DECIMAL_SHIFT = 100
DATA_ARRAY = (1 .. 1000).to_a
ROW_1 = (DATA_ARRAY + [111.111]).shuffle
ROW_2 = (DATA_ARRAY.map{ |i| i * 2 } + [111.111]).shuffle

Benchmark.bm(16) do |b|
  b.report('ternary:') do
    DECIMAL_SHIFT.times do
      ROW_1.zip(ROW_2).map do |x, y| 
        x == 111.111 || y == 111.111 ? nil : (x - y).abs 
      end.compact
    end
  end

  b.report('reject:') do
    DECIMAL_SHIFT.times do
      ROW_1.zip(ROW_2).reject{ |x,y| x == 111.111 || y == 111.111 }.map{ |x,y| (x - y).abs }
    end
  end

  b.report('each_with_index:') do
    DECIMAL_SHIFT.times do
      ROW_1.zip(ROW_2)
           .each_with_object([]) { |(x,y), ary|
             ary += [(x - y).abs] unless (x == 111.111 || y == 111.111)
           }
    end
  end
end

# >>                        user     system      total        real
# >> ternary:           0.240000   0.000000   0.240000 (  0.244476)
# >> reject:            0.060000   0.000000   0.060000 (  0.058842)
# >> each_with_index:   0.350000   0.000000   0.350000 (  0.349363)

调整DECIMAL_SHIFTDATA_ARRAY 的大小以及111.111 的位置,看看会发生什么,以了解哪些表达式最适合您的数据大小和结构,并根据需要微调代码。

【讨论】:

  • 使用“nil”和“compact”产生的执行时间为 0.000867 秒,而使用“拒绝”产生的执行时间为 0.001219 秒。这些是在大约 4000 个点的数组上执行的。
  • 基准测试结果太低让我怀疑。没有可用的示例数据和基准代码,我们无法真正将苹果与苹果进行比较。
  • 我还运行了 2000 次迭代并对整个循环进行了基准测试。结果分别为 8.922337 秒和 10.834802 秒。我知道如果没有我提供示例数据,这有点棘手。不过,我非常感谢您提供的所有帮助。
  • 这很棘手,因为缺少代码和数据,迫使我们不得不发明一些东西,从而导致不准确的假设和广泛的答案。广泛的问题和答案不是主题,它们对您的帮助也不大,而且它们对那些在未来寻找类似解决方案的人的帮助更少,这就是 SO 存在的原因。这些是要记住的事情。
  • 我的意图绝对不是浪费这个社区的时间。我感谢会员提供的所有帮助和时间投入。我没有意识到存储在我的数组中的数据会对解决方案产生如此大的影响。我希望我缺少一个明显的解决方案。不幸的是,我无法提供构成数组的数据,也无法提供任何额外的代码。再次感谢所有的贡献,下次我提出问题时,我会牢记社区原则。
【解决方案2】:

您可以尝试parallel gem https://github.com/grosser/parallel 并在多个线程上运行它

【讨论】:

  • 啊,这绝对是一个非常有用的 gem,但它似乎不受 ruby​​ 2.3.0p0(2015-12-25 修订版 53290)[x64-mingw32] 的支持。
  • @srm985 真可惜
  • 在多个线程中运行不一定会加快速度,实际上可能会减慢速度,尤其是在使用过多线程时。有一个快乐的平衡,测试会找到它。
猜你喜欢
  • 2014-12-21
  • 2012-11-09
  • 2019-05-16
  • 2016-02-15
  • 1970-01-01
  • 2019-08-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多