使用散列运行缓慢地在Ruby中迭代表

时间:2012-04-25 10:11:33

标签: ruby-on-rails ruby hash

我有

的以下代码
h2.each {|k, v|
   @count += 1
   puts @count
   sq.each do |word|
       if Wordsdoc.find_by_docid(k).tf.include?(word)
       sum += Wordsdoc.find_by_docid(k).tf[word] * @s[word]
       end
     end
   rec_hash[k] = sum
   sum = 0
   }

h2 - >是一个包含文件ID的哈希,哈希包含超过1000个这样的哈希 Wordsdoc - >是我数据库中的模型/表格... sq - >是一个包含大约10个单词的哈希

我正在做的是我正在查看每个文档ID,然后对于sq中的每个单词,如果单词存在,则在Wordsdoc表中查找(Wordsdoc.find_by_docid(k).tf.include?(单词),这里tf是{word => value}

的散列

如果是的话,我在Wordsdoc中获得该单词的值,并将其与@s中单词的值相乘,这也是{word =>的散列。值}

这似乎运行得很慢。 Tt每秒处理一个文档。有没有办法更快地处理?

非常感谢您对此的帮助!

3 个答案:

答案 0 :(得分:2)

你做了很多重复的查询。虽然ActiveRecord可以在后台进行一些缓存以加快速度,但是它可以做什么是有限的,并且没有理由让它变得更难。

减速最明显的原因是Wordsdoc.find_by_docid(k)。对于k的每个值,您将其调用10次,每次调用它时都有可能再次调用它。这意味着您为h2中的每个条目使用相同的参数调用该方法10-20次。对数据库的查询是昂贵的,因为数据库在硬盘上,并且在任何系统中访问硬盘都很昂贵。您可以在进入Wordsdoc.find_by_Docid(k)循环之前轻松调用sq.each 一次,然后将其存储在变量中 - 这样可以节省大量查询并使循环变为更快。

另一个优化 - 虽然不如第一个那么重要 - 是在单个查询中获取所有Wordsdoc记录。几乎所有中高级(以及一些低级别的!)编程语言和库在批量处理时工作得更好更快,而且ActiveRecord也不例外。如果您可以查询Wordsdoc所有条目,并按docid的{​​{1}}个键进行过滤,则可以转换1000个查询(之后)第一次优化。在第一次优化之前,它是10000-20000个查询)到一个巨大的查询。这将使ActiveRerocd和底层数据库能够以更大的块检索您的数据,并为您节省大量的光盘访问权。

你可以做一些小的优化,但我指定的两个应该绰绰有余。

答案 1 :(得分:1)

您正在拨打Wordsdoc.find_by_docid(k)两次。

您可以将代码重构为:

wordsdoc = Wordsdoc.find_by_docid(k)
if wordsdoc.tf.include?(word)
  sum += wordsdoc.tf[word] * @s[word]
end

......但它仍然是丑陋和低效的。

您应该批量预取所有记录,请参阅:https://makandracards.com/makandra/1181-use-find_in_batches-to-process-many-records-without-tearing-down-the-server

例如,类似的东西应该更有效率:

Wordsdoc.find_in_batches(:conditions => {:docid => array_of_doc_ids}).each do |wordsdoc|
  if wordsdoc.tf.include?(word)
    sum += wordsdoc.tf[word] * @s[word]
  end
end

此外,您只能使用:select => :tf方法中的find_in_batches从Wordsdoc表中检索某些列。

答案 2 :(得分:0)

由于你有很多事情要做,我只是打算为你提供检查的东西。

  1. 一本名为Eloquent Ruby的书处理文档并迭代文档以计算单词的使用次数。他所有的例子都是关于他正在维护的文档系统,所以它甚至可以为你解决其他问题。
  2. inject是一种方法,可以加快您sum部分的目标。
  3. 如果您正在异步执行此操作,则延迟工作。这意味着如果这是一个网络应用程序,你必须等待1000秒才能完成这项工作,然后才能在屏幕上显示它的答案。
  4. 去找他们。