最初のものでは、代わりに:
File.open(ARGV[0], "r").each_line do |line|
使用する:
File.foreach(ARGV[0]) do |line|
そして代わりに:
incr += 1
if incr % 3 == 0
使用する:
if $. % 3 == 0
$.
最後に読み取った行の行番号のマジック変数です。
2番目のものでは、代わりに:
line.gsub("(","").gsub(")","").split(",").map{ |s| s.to_i}
使用する:
line.tr('()', '').split(',').map(&:to_i)
3番目のものでは、代わりに:
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 の速度を他の言語と比較するには、各ステップの複数のベンチマークに基づいて、各ステップを最速で達成する方法に関する知識が必要です。また、同一のハードウェアと OS で実行していて、すべての言語が最も効率的な速度の形式にコンパイルされていることも意味します。言語は、メモリ使用量と速度のトレードオフを行うため、ある言語が別の言語よりも遅い場合でも、メモリ効率が高い場合があります。
さらに、実稼働環境でコーディングする場合、正しく機能するコードを生成する時間は、「どちらが速いか」という方程式に織り込む必要があります。C は非常に高速ですが、ほとんどの問題では Ruby よりもプログラムを作成するのに時間がかかります。C は Ruby のように手を握らないからです。C コードの作成とデバッグに 1 週間かかる場合と、1 時間かかった 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/秒の差しかありません。同じことを行う 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)