我正在用Ruby编写一个CSV库(我知道,标准版很棒!)主要是为了好玩。目前它比标准慢大约4倍,我发现它很奇怪,因为以下内容:我从stdlib看了csv.rb,它使用正则表达式来分割行,我希望它不会很快。在我的库中,我使用DFA,所以我确信它会在O(n)时间内运行 - 我几乎没有回溯,只有在我回溯一次以适应异常情况的情况下(escape char == quote char)并且它只发生在大约1%的时间。
所以我明显地描述了我的代码,这里占据了总运行时间的89%。它是为输入文件的每个字符运行的循环:
def consume token
if !@separator and [:BEFORE_FIELD, :FIELD, :BEFORE_SEPARATOR].include?(@state)
if @potential_separators.include? token
@separator = token
end
end
#puts "#{@state} - Token: #{token}"
@state = case @state
when :QUOTED_FIELD
if @escape.include? token
@last_escape_used = token
:MAYBE_ESCAPED_QUOTE
elsif token == @quote
:BEFORE_SEPARATOR
else
@field += token
:QUOTED_FIELD
end
when :FIELD
case token
when @newline
got_field
got_row
:BEFORE_FIELD
when @separator
got_field
:BEFORE_FIELD
else
@field += token
:FIELD
end
when :BEFORE_FIELD
case token
when @separator
got_field
:BEFORE_FIELD
when @quote
:QUOTED_FIELD
when @newline
got_field
got_row
:BEFORE_FIELD
else
@field += token
:FIELD
end
when :MAYBE_ESCAPED_QUOTE
if token == @quote
@field += @quote
:QUOTED_FIELD
elsif @last_escape_used == @quote
@state = :BEFORE_SEPARATOR
consume token
else
@field += @last_escape_used
@field += token
:QUOTED_FIELD
end
when :BEFORE_SEPARATOR
case token
when @separator
got_field
:BEFORE_FIELD
when @newline
got_field
got_row
:BEFORE_FIELD
else
raise "Error: Separator or newline expected! Got: #{token} at (#{@line}:#{@column})"
end
end
if token == @newline
@column = 1
@line += 1
else
@column += token.length
end
#puts "[#{@line}:#{@column} - #{token}] Switched to #{@state}"
#if token == @quote then exit end
@state
end
以下是分析输出:
此外,实际上consume
函数本身很慢而且它调用的函数很少,因为Self部分高达总运行时间的62%!
#consume函数中发生的事情的细节是:
我认为case @state
可能是罪魁祸首所以我做的第一件事就是把最常见的case
放在最顶层(我做了基准测试:没有变化)。代码对我来说似乎很干净,我并没有真正看到我可以获得多少收益,但我发现它比标准的lib慢得多,这很奇怪。
顺便说一句,我正在测试它的文件是一个2MB的CSV文件。我一行一行地阅读它并且不在内存中存储任何内容。如果我用一个无效的函数替换我的消费函数,我获得与Ruby标准CSV相同的速度(当然它什么都不做:))所以我想我可以得出结论,瓶颈不在I / O中。
答案 0 :(得分:0)
两点:
您似乎有很多some_array.include?(something)
。这很慢。尝试用这样的哈希替换它:
some_hash = {this_key => true, that_key => true, ...}
并像some_hash[something]
一样使用它。
您似乎有很多some_string += another_string
。这很慢。请尝试改为:
some_string << another_string