1

我有一个 2.6 GB 的文本文件,其中包含一个数据库表的转储,我试图将其拉入一个逻辑结构,以便所有字段都是唯一的。我用来执行此操作的代码在这里:

class Targetfile
  include Enumerable

  attr_accessor :inputfile, :headers, :input_array

  def initialize(file)
    @input_array = false
    @inputfile = File.open(file, 'r')
    @x = @inputfile.each.count
  end

  def get_headers
    @y = 1
    @inputfile.rewind
    @input_array = Array.new
    @headers = @inputfile.first.chomp.split(/\t/)
    @inputfile.each do |line|
      print "\n#{@y} / #{@x}"
      @y+=1
      self.assign_row(line)
    end
  end

  def assign_row(line)
    row_array = line.chomp.encode!('UTF-8', 'UTF-8', :invalid => :replace).split(/\t/)
    @input_array << Hash[ @headers.zip(row_array) ]
  end

  def send_build
    @input_array || self.get_headers
  end

  def each
    self.send_build.each {|row| yield row}
  end

end

该类已成功初始化,我留下了一个 Targetfile 类对象。

问题是,当我随后调用get_headers将文件转换为哈希数组的方法时,它立即开始减速。

直到项目编号 80,000 左右,我的眼睛才注意到这一点,但随后很明显,文件的每 3-4,000 行,就会发生某种暂停。这种停顿,每次发生,都需要稍长一点的时间,直到第 100 万行,它需要的时间超过 30 秒。

出于实际目的,我可以将文件切碎以避免这个问题,然后结合结果列表和唯一的 - 那 - 以获得我的最终输出。

然而,从好奇心的角度来看,我并不满意。

谁能告诉我为什么会发生这种暂停,为什么会变长,以及是否有任何方法可以优雅地避免它?真的,我只是想知道它是什么以及它为什么会发生,因为现在我已经注意到它,我在我运行的许多其他 Ruby 脚本中都看到了它,无论是在这台计算机上还是在其他计算机上。

4

4 回答 4

3

我建议在 DBM 中执行此操作,而不是 Ruby 或任何其他语言。DBM 可以非常快速地告诉您字段的唯一值,尤其是在它已经被索引的情况下。

试图用任何语言来做这件事就是在为通用计算设计的东西中复制数据库的基本功能。

相反,将 Ruby 与 Sequel 或 Active Record 之类的 ORM 一起使用,并向数据库发出查询并让它返回您想知道的内容。不要遍历每一行,这太疯狂了,要求它给你独特的价值,然后从那里开始。

我不会责怪 Ruby,因为在相同主机和 RAM 的情况下,任何其他语言都会出现同样的问题。C/C++ 可能会通过生成更紧凑的代码来延迟不可避免的事情,但是您的开发时间会大大减慢,尤其是当您学习像 C 这样的未知语言时。而且意外错误的风险会增加,因为您必须做更多的内务和防御工作比您在 Ruby、Python 或 Perl 中进行的编程。

将每种工具用于其设计用途,您将取得领先。

查看您的代码,您可能会通过不尝试将每一行保留在内存中来提高完成完整运行的机会。您说您正在尝试确定唯一性,因此只保留您感兴趣的唯一列值,您可以使用 Ruby 的 Set 类轻松完成。您可以将要确定唯一性的每个事物的值抛出,遍历文件,然后 Set 将仅保留唯一值。

于 2013-09-13T21:49:19.003 回答
1

这就是臭名昭著的垃圾收集器——Ruby 的内存管理机制。

注意:值得一提的是,Ruby,至少 MRI,并不是一种高性能语言。

每当内存开始用完时,垃圾收集器就会运行。垃圾收集器暂停程序的执行以释放任何无法再访问的对象。垃圾收集器仅在内存开始用完时运行。这就是为什么你会定期看到它。

没有什么可以避免这种情况,除了编写更多内存效率的代码,或者用可以更好/手动内存管理的语言重写。

此外,您的操作系统可能正在分页。你有足够的物理内存来完成这种任务吗?

于 2013-09-13T21:28:12.400 回答
0

您正在使用标头作为散列的键。它们是字符串,并且散列重复的字符串键。那是很多不必要的字符串。尝试将它们转换为符号是否可以加快速度:

@headers = @headers.map{|header| header.to_sym}
于 2013-09-13T22:01:47.090 回答
0

这是垃圾收集器。您可以通过在程序中放入 GC.start 来强制进行垃圾收集。让它定期运行。我必须为我编写的守护进程做同样的事情。它运作良好。 http://ruby-doc.org/core-1.9.3/GC.html

于 2014-05-09T05:24:47.050 回答