为什么我的Ruby脚本会随着时间的推移而减慢?

时间:2013-09-13 21:10:48

标签: ruby performance file-io class-method

我有一个2.6千兆字节的文本文件,其中包含数据库表的转储,我正在尝试将其转换为逻辑结构,因此字段都可以是唯一的。我用来做这个的代码在这里:

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行,某种暂停正在发生。这种暂停,每次发生,需要稍长一点,直到第百万行,它需要超过30秒。

出于实际目的,我可以只删除文件以避免此问题,然后将结果列表和唯一的 - 组合 - 以获得我的最终输出。

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

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

4 个答案:

答案 0 :(得分:3)

我建议在DBM中这样做,而不是Ruby或任何其他语言。 DBM可以非常快速地告诉您字段的唯一值,特别是如果它已经编入索引。

尝试以任何语言执行此操作都会在为通用计算设计的内容中复制数据库的基本功能。

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

我不会责怪Ruby,因为在给定相同主机和RAM的情况下,任何其他语言都会出现同样的问题。 C / C ++可能通过生成更紧凑的代码来延迟不可避免的,但是你的开发时间会急剧减慢,特别是当你学习像C这样的未知语言时。由于你需要做更多的内务管理和防御,因此意外错误的风险会增加编程比你在Ruby,Python或Perl中做的要好。

将每个工具用于其设计目标,您将领先一步。

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

答案 1 :(得分:1)

这是臭名昭着的垃圾收集器--Ruby的内存管理机制。

  

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

只要内存开始耗尽,垃圾收集器就会运行。垃圾收集器暂停程序的执行以释放无法再访问的任何对象。垃圾收集器仅在内存开始耗尽时运行。这就是你定期看到它的原因。

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

此外,您的操作系统可能正在寻呼。你有足够的物理记忆来完成这项任务吗?

答案 2 :(得分:0)

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

@headers = @headers.map{|header| header.to_sym}

答案 3 :(得分:0)

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