【问题标题】:ruby: how to iterate elements in a hash efficientlyruby:如何有效地迭代哈希中的元素
【发布时间】:2012-08-12 06:52:35
【问题描述】:

我有一个非常大的哈希值,我想对其进行迭代。 Hash.each 似乎太慢了。 有什么有效的方法吗?

如何将此哈希转换为数组?


在每个循环中,我都在做非常简单的字符串操作:

name_hash.each {|name, str|

  record += name.to_s + "\|" + str +"\n"

}

hash以人名为key,一些相关内容为value:

name_hash = {:"jose garcia" => "ca:tw#2@1,2@:th#1@3@;ar:tw#1@4@:fi#1@5@;ny:tw#1@6@;"}

【问题讨论】:

  • 我试过 .each,1M 的记录哈希需要 5 多个小时
  • 我在问您是否尝试过您认为的解决方案。
  • 哈希迭代本身应该是“快的”。在 Ruby 2x 中,它是用“链接链”实现的(为了保持良好的顺序属性)。迭代期间正在做什么? (1M - 100 万?? - 是“相当数量”的项目,所以即使每个项目都需要 0.01 秒或 100/秒,也需要 2.7 小时。也就是说,问题可能在内部 i> each 块而不是每个方法/迭代本身。也许有更好的方法来解决这个问题?)
  • 请附上完整的相关代码。正如 djconnel 在答案中所示,实际迭代非常快。因此,高度怀疑在 内部 each 块所做的事情是瓶颈。 DigitalRoss 还建议,假设哈希数据来自或利用数据库/模型本身,可能会有更好/不同的解决方案。
  • 一个没有被问到的问题是,您正在使用的机器上有多少可用 RAM? 100 万条记录的 5 小时似乎很长,除非您所在的机器受到内存限制并且正在交换。

标签: ruby-on-rails ruby arrays hash


【解决方案1】:

考虑以下示例,它使用 100 万个元素的哈希:

#! /usr/bin/env ruby
require 'benchmark'

h = {}
1_000_000.times do |n|
  h[n] = rand
end

puts Benchmark.measure { h.each { |k, v| } }

a = nil
puts Benchmark.measure { a = h.to_a }
puts Benchmark.measure { a.each { |k, v| } }

我在我的系统上运行它(运行 Ruby 1.8.5),我得到:

  0.350000   0.020000   0.370000 (  0.380571)
  0.300000   0.020000   0.320000 (  0.307207)
  0.160000   0.040000   0.200000 (  0.198388)

因此,对数组的迭代确实更快(0.16 秒,而散列则为 0.35 秒)。但是生成数组需要 0.3 秒。因此,网络进程比 0.35 秒慢 0.46 秒。

所以似乎最好只迭代哈希,至少在这个测试用例中。

【讨论】:

  • 而且 所有 发布的数字都远远少于 5 小时 ;-) 小型基准测试 +1;虽然微观,但他们清楚地表明问题不是each(哈希或数组)的实现..这意味着性能瓶颈来自内部 each 块。
  • 哇,Ruby 1.8.5?!为什么这么老?
  • Redhat Enterprise Linux 版本 5...我们的代码相当成熟,因为我从事支持而不是开发工作,所以我们升级缓慢。
【解决方案2】:

String#+ 很慢。这应该会改善它

 record = name_hash.map{|line| line.join("|")}.join("\n")

如果你用它来输出到某个地方,你不应该创建一个巨大的字符串,而是逐行写入输出。

【讨论】:

  • 我比我的回答更喜欢这个!
  • 对于任何想知道这与我的答案有什么区别的人,map 使用一个参数调用会产生一个数组 [key,value]。我发布的两个参数示例只是直接分配键/值。 #{}String#+ 快,但我不确定哪个版本的块参数更快。
【解决方案3】:

在 ruby​​ 中更惯用的方法:

record = name_hash.map{|k,v| "#{k}|#{v}"}.join("\n")

我不知道这与速度相比如何,但部分问题可能是因为您不断在字符串上添加一点点,并在每次迭代时创建新的(越来越长的)字符串对象。连接是在 C 中完成的,可能性能更好。

【讨论】:

  • 你的回答也很好。 #{...} 比 + 更好。
  • 我去掉了反斜杠,因为它不会显示在输出中。
【解决方案4】:

迭代大型集合很慢,每个方法都不是限制它的原因。在你的循环中你在做什么这么慢?如果需要转换为数组,可以调用some_hash.to_a

【讨论】:

    【解决方案5】:

    我曾认为 ruby​​ 1.9.x 使哈希迭代更快,但可能是错误的。如果是简单的结构,您可以尝试不同的哈希值,例如 https://github.com/rdp/google_hash,这是我为使 #each 更可靠而修改的哈希值...

    【讨论】:

      【解决方案6】:

      可能“通过单个数据库查询”

      将大型哈希转换为数组需要创建一个大型对象,并且需要两次迭代,尽管其中一次在解释器内部并且可能非常快。

      这不太可能比仅遍历 Hash 更快,但它可能适用于大型对象。

      查看the Standard Library Benchmark package,了解测量运行时间的简单方法。

      我还冒昧地猜测,这里真正的问题是您有一个类似 Hash 的 ActiveRecord 对象,该对象在枚举的每个循环中强制往返于您的数据库服务器。您真正想要的可能是绕过 AR 并运行原生查询以在一次单次往返中一次检索所有内容。

      【讨论】:

      • 为什么将哈希转换为数组需要“大量新对象[s]”?此外,帖子中没有[可靠]表明数据来自[关系]数据库..
      • 嗯,既然你提到它,大部分对象将被重用,或者它们将是不可变的内联值。我会更新答案。至于数据库,好吧,他确实用 Rails 标记了这个问题,而且报告的时间似乎太慢了。
      • 我并不是说这不是什么 .. 那样愚蠢 :) 希望我对主帖的评论会非法获取更多信息。
      • @DigitalRoss 我没有使用 AR。它只是在处理一个 txt 文件。
      • 那为什么要标记 ruby​​-on-rails?
      猜你喜欢
      • 1970-01-01
      • 2015-08-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-07
      • 1970-01-01
      相关资源
      最近更新 更多