ルビで単一引用符の代わりに二重引用符を使用すると、Ruby 1.8および1.9で意味のある方法でパフォーマンスが低下するかどうかを知っていますか。
だから私がタイプした場合
question = 'my question'
より速いですか
question = "my question"
rubyは、二重引用符に遭遇したときに何かを評価する必要があるかどうかを判断しようとし、おそらくそれを行うためにいくつかのサイクルを費やしていると思います。
ルビで単一引用符の代わりに二重引用符を使用すると、Ruby 1.8および1.9で意味のある方法でパフォーマンスが低下するかどうかを知っていますか。
だから私がタイプした場合
question = 'my question'
より速いですか
question = "my question"
rubyは、二重引用符に遭遇したときに何かを評価する必要があるかどうかを判断しようとし、おそらくそれを行うためにいくつかのサイクルを費やしていると思います。
回答:
$ ruby -v
ruby 1.9.3p0 (2011-10-30 revision 33570) [x86_64-darwin11.0.0]
$ cat benchmark_quotes.rb
# As of Ruby 1.9 Benchmark must be required
require 'benchmark'
n = 1000000
Benchmark.bm(15) do |x|
x.report("assign single") { n.times do; c = 'a string'; end}
x.report("assign double") { n.times do; c = "a string"; end}
x.report("concat single") { n.times do; 'a string ' + 'b string'; end}
x.report("concat double") { n.times do; "a string " + "b string"; end}
end
$ ruby benchmark_quotes.rb
user system total real
assign single 0.110000 0.000000 0.110000 ( 0.116867)
assign double 0.120000 0.000000 0.120000 ( 0.116761)
concat single 0.280000 0.000000 0.280000 ( 0.276964)
concat double 0.270000 0.000000 0.270000 ( 0.278146)
注:新しいRubyバージョンで動作するようにこれを更新し、ヘッダーをクリーンアップして、より高速なシステムでベンチマークを実行しました。
この回答はいくつかの重要なポイントを省略しています。特に、補間に関するこれらの他の回答と、一重引用符と二重引用符を使用してもパフォーマンスに大きな違いがない理由を参照してください。
'
し、"
彼らは同じことに解析されるように。
概要:速度の違いはありません。このすばらしい協調的なRubyスタイルガイドでは、一貫性を保つことが推奨されています。私が今使っ'string'
補間が必要とされない限り(ガイドのオプションA)とそれのような、しかし、あなたは一般的に多くのコードが表示されます"string"
。
詳細:
理論的には、コードが解析されるときに違いが生じる可能性がありますが、一般的に解析時間を気にしないだけでなく(実行時間と比較して無視できるほど)、この場合は大きな違いを見つけることができません。
重要なことは、isが実行されたときとまったく同じになることです。
これをベンチマークすることは、Rubyがどのように機能するかについての理解の欠如を示すだけです。どちらの場合も、文字列はaに解析されますtSTRING_CONTENT
(のソースをparse.y
参照)。つまり、CPUは、'string'
またはを作成するときにまったく同じ操作を実行し"string"
ます。まったく同じビットはまったく同じ方法で反転します。これをベンチマークすると、有意ではなく、他の要因(GCの開始など)による違いのみが表示されます。この場合、違いはありません!このようなマイクロベンチマークを正しく行うのは困難です。fruity
このための適切なツールについては、私の宝石を参照してください。
フォームの内挿がある場合"...#{...}..."
、これはに解析され、の各式と最終のにtSTRING_DBEG
束ねられることに注意してください。ただし、これはOPの目的ではない補間がある場合に限られます。tSTRING_DVAR
#{...}
tSTRING_DEND
以前はどこでも二重引用符を使用することを提案していました(#{some_var}
後で実際に追加するのが簡単になります)が、補間\n
などが必要でない限り、単一引用符を使用します...視覚的に好きです。文字列を解析して、式が含まれているかどうかを確認する必要があります。
#{n}
数値変換をしていると強調した)。解析の違いを示していませんか?
ただし、連結と補間を測定する人は誰もいませんでした。
$ ruby -v
ruby 1.8.7 (2008-08-11 patchlevel 72) [i686-darwin9.6.2]
$ cat benchmark_quotes.rb
require 'benchmark'
n = 1000000
Benchmark.bm do |x|
x.report("assign single") { n.times do; c = 'a string'; end}
x.report("assign double") { n.times do; c = "a string"; end}
x.report("assign interp") { n.times do; c = "a string #{'b string'}"; end}
x.report("concat single") { n.times do; 'a string ' + 'b string'; end}
x.report("concat double") { n.times do; "a string " + "b string"; end}
end
$ ruby -w benchmark_quotes.rb
user system total real
assign single 2.600000 1.060000 3.660000 ( 3.720909)
assign double 2.590000 1.050000 3.640000 ( 3.675082)
assign interp 2.620000 1.050000 3.670000 ( 3.704218)
concat single 3.760000 1.080000 4.840000 ( 4.888394)
concat double 3.700000 1.070000 4.770000 ( 4.818794)
具体的には、assign interp = 2.62
vs に注意してくださいconcat single = 3.76
。ケーキのアイシングとして、'a' + var + 'b'
特にスペースに関してよりも補間が読みやすくなっていることもわかりました。
違いはありません-あなたが使用しているのでない限り #{some_var}
スタイル文字列補間。しかし、実際にそれを行った場合にのみ、パフォーマンスの打撃を受けます。
Zeteticの例から変更:
require 'benchmark'
n = 1000000
Benchmark.bm do |x|
x.report("assign single") { n.times do; c = 'a string'; end}
x.report("assign double") { n.times do; c = "a string"; end}
x.report("assign interp") { n.times do; c = "a #{n} string"; end}
x.report("concat single") { n.times do; 'a string ' + 'b string'; end}
x.report("concat double") { n.times do; "a string " + "b string"; end}
x.report("concat interp") { n.times do; "a #{n} string " + "b #{n} string"; end}
end
出力
user system total real
assign single 0.370000 0.000000 0.370000 ( 0.374599)
assign double 0.360000 0.000000 0.360000 ( 0.366636)
assign interp 1.540000 0.010000 1.550000 ( 1.577638)
concat single 1.100000 0.010000 1.110000 ( 1.119720)
concat double 1.090000 0.000000 1.090000 ( 1.116240)
concat interp 3.460000 0.020000 3.480000 ( 3.535724)
字句解析器はチェックする必要がないため、一重引用符は二重引用符よりもわずかに高速です。 #{}
補間マーカー。実装などによって異なります。これは解析時間のコストであり、実行時のコストではないことに注意してください。
とは言っても、実際の問題は、二重引用符で囲まれた文字列を使用すると、「意味のある方法でパフォーマンスが低下する」かどうかでした。パフォーマンスの違いは非常に小さいため、実際のパフォーマンスの問題と比較すると、まったく意味がありません。時間を無駄にしないでください。
もちろん、実際の補間は別の話です。'foo'
はほぼ正確に1秒速くなり"#{sleep 1; nil}foo"
ます。
二重引用符は、単一引用符の2倍のキーストライクで入力できます。私はいつも急いでいます。一重引用符を使用します。:)そして、はい、私はそれを「パフォーマンスの向上」と考えています。:)
1.8.7と1.9.2の比較を追加すると思います。私は彼らを数回走らせました。分散は約+ -0.01でした。
require 'benchmark'
n = 1000000
Benchmark.bm do |x|
x.report("assign single") { n.times do; c = 'a string'; end}
x.report("assign double") { n.times do; c = "a string"; end}
x.report("assign interp") { n.times do; c = "a #{n} string"; end}
x.report("concat single") { n.times do; 'a string ' + 'b string'; end}
x.report("concat double") { n.times do; "a string " + "b string"; end}
x.report("concat interp") { n.times do; "a #{n} string " + "b #{n} string"; end}
end
ruby 1.8.7(2010-08-16パッチレベル302)[x86_64-linux]
assign single 0.180000 0.000000 0.180000 ( 0.187233)
assign double 0.180000 0.000000 0.180000 ( 0.187566)
assign interp 0.880000 0.000000 0.880000 ( 0.877584)
concat single 0.550000 0.020000 0.570000 ( 0.567285)
concat double 0.570000 0.000000 0.570000 ( 0.570644)
concat interp 1.800000 0.010000 1.810000 ( 1.816955)
ruby 1.9.2p0(2010-08-18リビジョン29036)[x86_64-linux]
user system total real
assign single 0.140000 0.000000 0.140000 ( 0.144076)
assign double 0.130000 0.000000 0.130000 ( 0.142316)
assign interp 0.650000 0.000000 0.650000 ( 0.656088)
concat single 0.370000 0.000000 0.370000 ( 0.370663)
concat double 0.370000 0.000000 0.370000 ( 0.370076)
concat interp 1.420000 0.000000 1.420000 ( 1.412210)
一重引用符で囲まれた文字列の方がRubyの方が解析が速いかもしれないと私も思いました。それはそうではないようです。
とにかく、私は上記のベンチマークが間違ったものを測定していると思います。どちらのバージョンも同じ内部文字列表現に解析されるため、どちらの解析がより速いかという答えを得るには、文字列変数を使用してパフォーマンスを測定するのではなく、Rubyが文字列を解析する速度を測定する必要があります。
generate.rb:
10000.times do
('a'..'z').to_a.each {|v| print "#{v}='This is a test string.'\n" }
end
#Generate sample ruby code with lots of strings to parse
$ ruby generate.rb > single_q.rb
#Get the double quote version
$ tr \' \" < single_q.rb > double_q.rb
#Compare execution times
$ time ruby single_q.rb
real 0m0.978s
user 0m0.920s
sys 0m0.048s
$ time ruby double_q.rb
real 0m0.994s
user 0m0.940s
sys 0m0.044s
繰り返し実行しても大きな違いはないようです。どちらのバージョンの文字列を解析する場合でも、ほぼ同じ時間がかかります。
実装によっては確かに可能ですが、インタプリタのスキャン部分は各文字を1回だけ見る必要があります。#{}ブロックを処理するには、追加の状態(または可能な状態のセット)と遷移のみが必要です。
テーブルベースのスキャナーでは、トランジションを決定するための単一のルックアップとなり、いずれにしても各キャラクターに対して発生します。
パーサーがスキャナー出力を取得したとき、ブロック内のコードを評価する必要があることはすでにわかっています。そのため、オーバーヘッドは、実際には#{}ブロックを処理するためのスキャナー/パーサーのメモリオーバーヘッドにすぎません。
私が何かを欠落していない限り(またはコンパイラの構成の詳細を覚えていない場合)、それも確かに可能です:)
~ > ruby -v
jruby 1.6.7 (ruby-1.8.7-p357) (2012-02-22 3e82bc8) (Java HotSpot(TM) 64-Bit Server VM 1.6.0_37) [darwin-x86_64-java]
~ > cat qu.rb
require 'benchmark'
n = 1000000
Benchmark.bm do |x|
x.report("assign single") { n.times do; c = 'a string'; end}
x.report("assign double") { n.times do; c = "a string"; end}
x.report("concat single") { n.times do; 'a string ' + 'b string'; end}
x.report("concat double") { n.times do; "a string " + "b string"; end}
end
~ > ruby qu.rb
user system total real
assign single 0.186000 0.000000 0.186000 ( 0.151000)
assign double 0.062000 0.000000 0.062000 ( 0.062000)
concat single 0.156000 0.000000 0.156000 ( 0.156000)
concat double 0.124000 0.000000 0.124000 ( 0.124000)
あなたがすべて逃した1つがあります。
ここにドキュメント
これを試して
require 'benchmark'
mark = <<EOS
a string
EOS
n = 1000000
Benchmark.bm do |x|
x.report("assign here doc") {n.times do; mark; end}
end
それは私に与えました
`asign here doc 0.141000 0.000000 0.141000 ( 0.140625)`
そして
'concat single quotes 1.813000 0.000000 1.813000 ( 1.843750)'
'concat double quotes 1.812000 0.000000 1.812000 ( 1.828125)'
ですから、連結してそれらのプットをすべて書くよりは確かに優れています。
Rubyがドキュメント操作言語に沿ってもっと教えてほしいです。
結局のところ、Rails、Sinatra、およびテストの実行で実際にそれを実行しませんか?
私はTim Snowhiteの答えを変更しました。
require 'benchmark'
n = 1000000
attr_accessor = :a_str_single, :b_str_single, :a_str_double, :b_str_double
@a_str_single = 'a string'
@b_str_single = 'b string'
@a_str_double = "a string"
@b_str_double = "b string"
@did_print = false
def reset!
@a_str_single = 'a string'
@b_str_single = 'b string'
@a_str_double = "a string"
@b_str_double = "b string"
end
Benchmark.bm do |x|
x.report('assign single ') { n.times do; c = 'a string'; end}
x.report('assign via << single') { c =''; n.times do; c << 'a string'; end}
x.report('assign double ') { n.times do; c = "a string"; end}
x.report('assing interp ') { n.times do; c = "a string #{'b string'}"; end}
x.report('concat single ') { n.times do; 'a string ' + 'b string'; end}
x.report('concat double ') { n.times do; "a string " + "b string"; end}
x.report('concat single interp') { n.times do; "#{@a_str_single}#{@b_str_single}"; end}
x.report('concat single << ') { n.times do; @a_str_single << @b_str_single; end}
reset!
# unless @did_print
# @did_print = true
# puts @a_str_single.length
# puts " a_str_single: #{@a_str_single} , b_str_single: #{@b_str_single} !!"
# end
x.report('concat double interp') { n.times do; "#{@a_str_double}#{@b_str_double}"; end}
x.report('concat double << ') { n.times do; @a_str_double << @b_str_double; end}
end
結果:
jruby 1.7.4 (1.9.3p392) 2013-05-16 2390d3b on Java HotSpot(TM) 64-Bit Server VM 1.7.0_10-b18 [darwin-x86_64]
user system total real
assign single 0.220000 0.010000 0.230000 ( 0.108000)
assign via << single 0.280000 0.010000 0.290000 ( 0.138000)
assign double 0.050000 0.000000 0.050000 ( 0.047000)
assing interp 0.100000 0.010000 0.110000 ( 0.056000)
concat single 0.230000 0.010000 0.240000 ( 0.159000)
concat double 0.150000 0.010000 0.160000 ( 0.101000)
concat single interp 0.170000 0.000000 0.170000 ( 0.121000)
concat single << 0.100000 0.000000 0.100000 ( 0.076000)
concat double interp 0.160000 0.000000 0.160000 ( 0.108000)
concat double << 0.100000 0.000000 0.100000 ( 0.074000)
ruby 1.9.3p429 (2013-05-15 revision 40747) [x86_64-darwin12.4.0]
user system total real
assign single 0.100000 0.000000 0.100000 ( 0.103326)
assign via << single 0.160000 0.000000 0.160000 ( 0.163442)
assign double 0.100000 0.000000 0.100000 ( 0.102212)
assing interp 0.110000 0.000000 0.110000 ( 0.104671)
concat single 0.240000 0.000000 0.240000 ( 0.242592)
concat double 0.250000 0.000000 0.250000 ( 0.244666)
concat single interp 0.180000 0.000000 0.180000 ( 0.182263)
concat single << 0.120000 0.000000 0.120000 ( 0.126582)
concat double interp 0.180000 0.000000 0.180000 ( 0.181035)
concat double << 0.130000 0.010000 0.140000 ( 0.128731)
私は以下を試しました:
def measure(t)
single_measures = []
double_measures = []
double_quoted_string = ""
single_quoted_string = ''
single_quoted = 0
double_quoted = 0
t.times do |i|
t1 = Time.now
single_quoted_string << 'a'
t1 = Time.now - t1
single_measures << t1
t2 = Time.now
double_quoted_string << "a"
t2 = Time.now - t2
double_measures << t2
if t1 > t2
single_quoted += 1
else
double_quoted += 1
end
end
puts "Single quoted did took longer in #{((single_quoted.to_f/t.to_f) * 100).round(2)} percent of the cases"
puts "Double quoted did took longer in #{((double_quoted.to_f/t.to_f) * 100).round(2)} percent of the cases"
single_measures_avg = single_measures.inject{ |sum, el| sum + el }.to_f / t
double_measures_avg = double_measures.inject{ |sum, el| sum + el }.to_f / t
puts "Single did took an average of #{single_measures_avg} seconds"
puts "Double did took an average of #{double_measures_avg} seconds"
puts "\n"
end
both = 10.times do |i|
measure(1000000)
end
そしてこれらは出力です:
1。
Single quoted did took longer in 32.33 percent of the cases
Double quoted did took longer in 67.67 percent of the cases
Single did took an average of 5.032084099982639e-07 seconds
Double did took an average of 5.171539549983464e-07 seconds
2。
Single quoted did took longer in 26.9 percent of the cases
Double quoted did took longer in 73.1 percent of the cases
Single did took an average of 4.998066229983696e-07 seconds
Double did took an average of 5.223457359986066e-07 seconds
3。
Single quoted did took longer in 26.44 percent of the cases
Double quoted did took longer in 73.56 percent of the cases
Single did took an average of 4.97640888998877e-07 seconds
Double did took an average of 5.132918459987151e-07 seconds
4。
Single quoted did took longer in 26.57 percent of the cases
Double quoted did took longer in 73.43 percent of the cases
Single did took an average of 5.017136069985988e-07 seconds
Double did took an average of 5.004514459988143e-07 seconds
5。
Single quoted did took longer in 26.03 percent of the cases
Double quoted did took longer in 73.97 percent of the cases
Single did took an average of 5.059069689983285e-07 seconds
Double did took an average of 5.028807639983705e-07 seconds
6。
Single quoted did took longer in 25.78 percent of the cases
Double quoted did took longer in 74.22 percent of the cases
Single did took an average of 5.107472039991399e-07 seconds
Double did took an average of 5.216212339990241e-07 seconds
7。
Single quoted did took longer in 26.48 percent of the cases
Double quoted did took longer in 73.52 percent of the cases
Single did took an average of 5.082368429989468e-07 seconds
Double did took an average of 5.076817109989933e-07 seconds
8。
Single quoted did took longer in 25.97 percent of the cases
Double quoted did took longer in 74.03 percent of the cases
Single did took an average of 5.077162969990005e-07 seconds
Double did took an average of 5.108381859991112e-07 seconds
9。
Single quoted did took longer in 26.28 percent of the cases
Double quoted did took longer in 73.72 percent of the cases
Single did took an average of 5.148080479983138e-07 seconds
Double did took an average of 5.165793929982176e-07 seconds
10。
Single quoted did took longer in 25.03 percent of the cases
Double quoted did took longer in 74.97 percent of the cases
Single did took an average of 5.227828659989748e-07 seconds
Double did took an average of 5.218296609988378e-07 seconds
私がミスをしなかった場合、シングルクォートの方がほとんどの場合少し高速ですが、両方にほぼ同じ時間がかかるようです。