在第一个中,而不是:
File.open(ARGV[0], "r").each_line do |line|
利用:
File.foreach(ARGV[0]) do |line|
而不是:
incr += 1
if incr % 3 == 0
利用:
if $. % 3 == 0
$.
是最后读取行的行号的魔术变量。
在第二个中,而不是:
line.gsub("(","").gsub(")","").split(",").map{ |s| s.to_i}
利用:
line.tr('()', '').split(',').map(&:to_i)
在第三个中,而不是:
line.split("),(").map{ |s| s.gsub("(","").gsub(")","").split(",").map{ |s| s.to_i}}
利用:
line.scan(/(?:\d+,?)+/).map{ |s| s.split(',', 0).map(&:to_i) }
以下是该行的工作方式:
line.scan(/(?:\d+,?)+/)
=> ["1,2,3,", "1,2,3,"]
line.scan(/(?:\d+,?)+/).map{ |s| s.split(',',0) }
=> [["1", "2", "3"], ["1", "2", "3"]]
line.scan(/(?:\d+,?)+/).map{ |s| s.split(',', 0).map(&:to_i) }
=> [[1, 2, 3], [1, 2, 3]]
我没有运行任何基准来比较速度,但更改也应该更快,因为gsub
调用已经消失。我所做的更改不一定是最快的做事方式,它们是您自己的代码的更优化版本。
尝试将 Ruby 的速度与其他语言进行比较需要了解完成每个步骤的最快方法,基于该步骤的多个基准。这也意味着您在相同的硬件和操作系统上运行,并且您的语言都被编译为最高效的速度形式。语言在内存使用与速度之间进行权衡,因此,虽然一种语言可能比另一种慢,但它也可能更节省内存。
另外,在生产环境中编码时,必须将生成正确工作的代码的时间考虑到“哪个更快”等式中。C 非常快,但是对于大多数问题来说,编写程序比 Ruby 需要更长的时间,因为 C 不像 Ruby 那样握住你的手。当 C 代码需要一周的时间来编写和调试时,与需要一个小时的 Ruby 代码相比,哪个更快?只是要考虑的事情。
在我完成之前,我没有通读@tadman 的答案和评论。使用:
map(&:to_i)
曾经慢于:
map{ |s| s.to_i }
速度差异取决于您运行的 Ruby 版本。最初使用&:
是在一些猴子补丁中实现的,但现在它已内置到 Ruby 中。当他们做出改变时,它加快了很多:
require 'benchmark'
foo = [*('1'..'1000')] * 1000
puts foo.size
N = 10
puts "N=#{N}"
puts RUBY_VERSION
puts
Benchmark.bm(6) do |x|
x.report('&:to_i') { N.times { foo.map(&:to_i) }}
x.report('to_i') { N.times { foo.map{ |s| s.to_i } }}
end
哪个输出:
1000000
N=10
2.0.0
user system total real
&:to_i 1.240000 0.000000 1.240000 ( 1.250948)
to_i 1.400000 0.000000 1.400000 ( 1.410763)
这要经过 10,000,000 个元素,仅导致 0.2/秒的差异。做同一件事的两种方式之间并没有太大区别。如果您要处理更多数据,那么这很重要。对于大多数应用程序来说,这是一个没有实际意义的问题,因为其他事情将成为瓶颈/减速,所以无论哪种方式适合您编写代码,都要牢记速度差异。
为了展示 Ruby 版本的不同之处,下面是使用 Ruby 1.8.7 的相同基准测试结果:
1000000
N=10
1.8.7
用户系统总真实
&:to_i 4.940000 0.000000 4.940000 (4.945604)
to_i 2.390000 0.000000 2.390000 (2.396693)
至于gsub
与tr
:
require 'benchmark'
foo = '()' * 500000
puts foo.size
N = 10
puts "N=#{N}"
puts RUBY_VERSION
puts
Benchmark.bm(6) do |x|
x.report('tr') { N.times { foo.tr('()', '') }}
x.report('gsub') { N.times { foo.gsub(/[()]/, '') }}
end
有了这些结果:
1000000
N=10
1.8.7
用户系统总真实
tr 0.010000 0.000000 0.010000 (0.011652)
gsub 3.010000 0.000000 3.010000 (3.014059)
和:
1000000
N=10
2.0.0
用户系统总真实
tr 0.020000 0.000000 0.020000 (0.017230)
gsub 1.900000 0.000000 1.900000 (1.904083)
这是我们可以从更改正则表达式模式中看到的那种差异,这会强制更改获得所需结果所需的处理:
require 'benchmark'
line = '((1,2,3),(1,2,3))'
pattern1 = /\([\d,]+\)/
pattern2 = /\(([\d,]+)\)/
pattern3 = /\((?:\d+,?)+\)/
pattern4 = /\d(?:[\d,])+/
line.scan(pattern1) # => ["(1,2,3)", "(1,2,3)"]
line.scan(pattern2) # => [["1,2,3"], ["1,2,3"]]
line.scan(pattern3) # => ["(1,2,3)", "(1,2,3)"]
line.scan(pattern4) # => ["1,2,3", "1,2,3"]
line.scan(pattern1).map{ |s| s[1..-1].split(',').map(&:to_i) } # => [[1, 2, 3], [1, 2, 3]]
line.scan(pattern2).map{ |s| s[0].split(',').map(&:to_i) } # => [[1, 2, 3], [1, 2, 3]]
line.scan(pattern3).map{ |s| s[1..-1].split(',').map(&:to_i) } # => [[1, 2, 3], [1, 2, 3]]
line.scan(pattern4).map{ |s| s.split(',').map(&:to_i) } # => [[1, 2, 3], [1, 2, 3]]
N = 1000000
Benchmark.bm(8) do |x|
x.report('pattern1') { N.times { line.scan(pattern1).map{ |s| s[1..-1].split(',').map(&:to_i) } }}
x.report('pattern2') { N.times { line.scan(pattern2).map{ |s| s[0].split(',').map(&:to_i) } }}
x.report('pattern3') { N.times { line.scan(pattern3).map{ |s| s[1..-1].split(',').map(&:to_i) } }}
x.report('pattern4') { N.times { line.scan(pattern4).map{ |s| s.split(',').map(&:to_i) } }}
end
在 Ruby 2.0-p427 上:
user system total real
pattern1 5.610000 0.010000 5.620000 ( 5.606556)
pattern2 5.460000 0.000000 5.460000 ( 5.467228)
pattern3 5.730000 0.000000 5.730000 ( 5.731310)
pattern4 5.080000 0.010000 5.090000 ( 5.085965)