web-dev-qa-db-ja.com

ルビーでシングルクォートとダブルクォートを使用するとパフォーマンスが向上しますか?

Rubyで一重引用符の代わりに二重引用符を使用すると、Ruby 1.8および1.9。

だから私が入力した場合

question = 'my question'

より速いですか

question = "my question"

Rubyは、二重引用符に遭遇したときに何かを評価する必要があるかどうかを判断しようとし、おそらくそれを行うのにいくつかのサイクルを費やすと思います。

123
dimus
$ 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=バージョンで動作するようにこれを更新し、ヘッダーをクリーンアップし、より高速なシステムでベンチマークを実行します。

この回答では、いくつかの重要なポイントを省略しています。単一引用符と二重引用符を使用する場合、特にこれらの 補間 および理由 パフォーマンスに大きな違いはありません に関する他の回答を参照してください。

85
zetetic

要約:速度差なし; this great collororative Ruby style guide 一貫性のあることをお勧めします。補間が必要ない場合は_'string'_を使用します(オプションAガイド)と同様ですが、通常は_"string"_でより多くのコードが表示されます。

詳細:

理論的には、コードがparsedの場合に違いが生じる可能性がありますが、一般的に解析時間を気にする必要はありません(実行時間と比較して無視できる)、この場合に大きな違いを見つけることができます。

重要なことは、isがgetsexecutedになったとき、まったく同じになることです

これをベンチマークすることは、Rubyがどのように機能するかについての理解不足を示しています。どちらの場合も、文字列は_tSTRING_CONTENT_に解析されます( the source in _parse.y_ )。つまり、CPUは_'string'_または_"string"_を作成するときにまったく同じ操作を実行します。まったく同じビットがまったく同じを反転しますこれをベンチマークすると、重要ではなく、他の要因(GCの起動など)による違いのみが表示されます;この場合、違いはあり得ないことに注意してください!これらのようなマイクロベンチマークは、正しく取得するのが困難です。私の宝石 fruity このための適切なツール。

_"...#{...}..."_の形式の補間がある場合、これは_tSTRING_DBEG_、_tSTRING_DVAR_の各式の_#{...}_の束、および最後の_tSTRING_DEND_。ただし、これは補間が存在する場合のみです。これはOPの目的ではありません。

以前はどこでも二重引用符を使用することを提案していました(後で実際に_#{some_var}_を追加するのが簡単になります)が、補間、_\n_などが必要でない限り、今は単一引用符を使用しています...文字列に式が含まれているかどうかを確認するために文字列を解析する必要がないため、視覚的には少し明確です。

103

ただし、連結と内挿を測定する人はいませんでした。

$ 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'特にスペースに関して。

35
Tim Snowhite

違いはありません-#{some_var}スタイルの文字列補間を使用している場合を除きます。しかし、実際にそれを実行した場合にのみ、パフォーマンスが低下します。

Zetetic's の例から変更:

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)
16
madlep

レクサーは#{}補間マーカーをチェックする必要がないため、単一引用符は二重引用符よりもわずかに高速になります。実装などに依存します。これは解析時のコストであり、実行時のコストではないことに注意してください。

そうは言っても、実際の質問は、二重引用符で囲まれた文字列を使用すると、「意味のある方法でパフォーマンスが低下する」かどうかということでした。パフォーマンスの違いは非常に小さいため、実際のパフォーマンスの問題と比較してもまったく重要ではありません。時間を無駄にしないでください。

もちろん、実際の補間は別の話です。 'foo'は、"#{sleep 1; nil}foo"よりほぼ正確に1秒速くなります。

13
Rein Henrichs

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 patchlevel 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 revision 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)
8
PhilT

二重引用符の入力には、単一引用符の2倍のキーストライクが必要です。私はいつも急いでいます。一重引用符を使用します。 :)そして、はい、それは「パフォーマンスの向上」だと考えています。 :)

8
aqn

どちらの方向にも大きな違いはありません。それは重要であるために巨大でなければなりません。

タイミングに実際の問題があることが確実な場合を除き、プログラマーの保守性を最適化します。

マシンタイムのコストは非常にわずかです。プログラマがコードを書いてそれを維持する時間は莫大です。

コードのメンテナンスが難しいことを意味する場合、数千回の実行で数秒、さらには数分の実行時間を節約する最適化とは何でしょうか?

スタイルを選択してそれを使い続けますが、not実行時間の統計的に意味のないミリ秒に基づいてそのスタイルを選択します。

3
Andy Lester

私も、単一引用符で囲まれた文字列は、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
PSkocik

実装によっては確かに可能ですが、インタープリターのスキャン部分は各文字を一度だけ見る必要があります。 #{}ブロックを処理するには、追加の状態(または状態の可能なセット)と遷移のみが必要です。

テーブルベースのスキャナーでは、遷移を判断するための単一のルックアップになり、とにかく各文字に対して発生します。

パーサーがスキャナー出力を取得するとき、ブロック内のコードを評価する必要があることは既にわかっています。そのため、オーバーヘッドは実際には#{}ブロックを処理するスキャナー/パーサーのメモリオーバーヘッドのみであり、どちらの方法でも支払います。

何かが足りない場合(またはコンパイラー構成の詳細を誤って記憶している場合を除く)

0
µBio

私は次を試しました:

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

間違いを犯さなかった場合、ほとんどの場合、単一引用符で囲まれた方がわずかに高速ですが、どちらもほぼ同じ時間がかかるようです。

0
Marcelo Xavier
~ > 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)
0
grilix

ティム・スノホワイトの答えを改造しました。

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)
0
Nick

あなたがすべて逃したものがあります。

こちらのドキュメント

これを試して

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)'

したがって、すべてのputを連結して記述するよりも確かに優れています。

私はRubyがドキュメント操作言語のラインに沿ってもっと教えられるのを見たいです。

結局のところ、Rails、Sinatra、および実行中のテストで実際にそれを行うのではないでしょうか?

0