ルビーで単一引用符と二重引用符を使用するとパフォーマンスが向上しますか?


126

ルビで単一引用符の代わりに二重引用符を使用すると、Ruby 1.8および1.9で意味のある方法でパフォーマンスが低下するかどうかを知っていますか。

だから私がタイプした場合

question = 'my question'

より速いですか

question = "my question"

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


17
50万回実行して見てください。おそらく、あなたのサイトは問題となるほど十分なトラフィックを得ていません。時期尚早の最適化は、一般的にはそれに値しません。
ceejayoz

60
なぜそんなに多くの人々がルビーがウェブプログラミングのためだけに使われると期待するのですか
ヨハネス2009

17
この時期尚早な最適化は考慮しません。アプリが完了した後で戻ってシングルまたはダブルのいずれかを最適化するため、「ベストプラクティス」の詳細は大きな頭痛の種になるでしょう。
オマール

7
私にとってはスタイルです。「静的」文字列には一重引用符を使用し、他の場合には二重引用符(または他の補間文字列)を使用します。
TIG

3
@Baddie:存在しない問題を最適化して取り除くのは時期尚早の最適化です。
アンディレスター

回答:


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

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


結果を正しく解釈していますか?二重引用符を使用した代入は、実際には単一よりも速いですか?どうすればいいの?
randomguy

明らかにそうですが、違いはわずかです。理由について-私を打ち負かします。
zetetic

このベンチマークは、コンパイル時間と実行時間を考慮に入れると、はるかに説得力があります。
nohat

9
測定された差は意味がありません。(ガベージコレクションのため)順序だけが重要な違いを生む可能性があります。間には、実行時の違いはありません'し、"彼らは同じことに解析されるように。
マルク=アンドレ・Lafortune

104

概要:速度の違いはありません。このすばらしい協調的な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などが必要でない限り、単一引用符を使用します...視覚的に好きです。文字列を解析して、式が含まれているかどうかを確認する必要があります。


3
分のパフォーマンスの違いよりもはるかに重要だと思われます。それは二重引用符です!
Venkat D.

あなたの答えを教えてくれてありがとう。これをベンチマークすると誤解を招くと言う理由を明確にしていただけますか?違いはおそらくごくわずかですが、ベンチマークは何らかの点で間違っているのでしょうか。(誰かがすでに#{n}数値変換をしていると強調した)。解析の違いを示していませんか?
PhilT 2012年

1
スタイルガイドへのリンクをありがとう。これまで出会ったことがなかったなんて信じられない。
PhilT

1
回答で言及されているスタイルガイドは、一重引用符でも二重引用符でも一貫したスタイルを採用することを提案するように更新さており、Rubyコミュニティでは二重引用符付きの文字列のほうが普及していることを示しています。
philtr 2014

二重引用符を使用します。プログラミングは難しいです。構文は本質的に複雑です。二重引用符は、文字列を動的にするときに間違いを犯したり、エラーで時間を浪費したりしないことを意味します。二重引用符を使用すると、考えるべきことが1つ少なくなります。
Kelsey Hannan

35

ただし、連結と補間を測定する人は誰もいませんでした。

$ 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.62vs に注意してくださいconcat single = 3.76。ケーキのアイシングとして、'a' + var + 'b'特にスペースに関してよりも補間が読みやすくなっていることもわかりました。


+1。これは、リンゴとリンゴを比較する唯一の補間ベンチマークです。
Mark Thomas、

1
ベンチマークは誤解を招く可能性があります。理由については私の答えを参照してください。連結と補間の比較については、補間が連結よりも遅くなることはないことは明らかです。いずれにせよ、それは本当に問題の一部ではありません!
マルク=アンドレ・Lafortune

このテストに<<を追加できますか?
Nick

16

違いはありません-あなたが使用しているのでない限り #{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)

面白い。補間は少し高価に見えます。これは1.8でしたか?1.9が何かを変更するかどうかを確認するとよいでしょう。
zetetic 2009

ゼーテティック-うん。これはRuby 1.8.7に対するものでした
madlep 09

1
interpバージョンは、補間と連結の両方に加えて、数値を文字列に2回変換します。結果を同じにすると、補間が優先されます。gist.github.com/810463を参照してください。本当の要点は、一重引用符や二重引用符よりもto_sについてもっと心配することです。
Brian Deterling

これだけのベンチマークは誤解を招くだけで、Rubyの動作に関する誤解を示しています。私の答えを見てください。
マルク=アンドレ・Lafortune

13

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

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

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


4
コストは実行時ではなくコンパイル時であることを示すための+1。したがって、上記の投票数の多いベンチマークベースの回答は誤解を招くものです。
nohat

「これは解析時間のコストであり、実行時のコストではありません。」キーフレーズです。
Tin Man

9

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


二重引用符でキーストが2倍になるのはなぜですか?どちらも1つのキーで表されます。さらに、多くのIDEは終了引用符を自動的に追加します。
マットドレッセル2013

3
IDEが自動的に引用符を閉じたとしても、二重引用符は依然として100%多いキーストライクが必要です。;-)
Clint Pachl 2013

Matt Dressel:二重引用符は、Shiftキーも押す必要があるため、2回キーを押す必要があります。Oh::)元のコメントで見逃した場合に備えて。:)コード付きキーの実行には、より多くの労力と、間違いなく、より多くの時間が必要です。:)
aqn 2013

1
時々私は怠惰からこのアドバイスに従います。しかし、残念なことに、他のいくつかの言語では、それは逆です(例えば、単一引用符はShift +何かが必要ですが、二重引用符は単一のキーストロークです)。残念ながら、キーボードレイアウトが異なる2人が同じプロジェクトで作業する場合、そのうちの1人がいくつかのキーストロークを犠牲にする必要があります。)
HalilÖzgürMar

「私は急いでいる男」-Shiftキーと2キー(または他のキー)を続けて押さない限り、一重引用符を使用しても時間を節約できません。
町筋2016

8

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)

Interpは数値から文字列への変換を行う必要があります。gist.github.com/810463を参照してください。
Brian Deterling

これらの数値が得られる理由については、私の回答を参照してください。
マルク=アンドレ・Lafortune

Interpの良い点。私は以前の答えを私の根拠としてコピーしただけです。それが教えてくれます。
PhilT 2012年

3

どちらの方向にも大きな違いはありません。それが問題になるには、それは巨大でなければなりません。

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

マシン時間のコストは非常に小さいです。プログラマーがコードを記述して保守するための時間のコストは莫大です。

コードの保守が困難であることを意味する場合、数千回の実行で数秒、場合によっては数分の実行時間を節約するための最適化はどのように優れていますか?

スタイルを選択してそれを使い続けますが、実行時間の統計的に重要でないミリ秒に基づいてそのスタイルを選択しないでください。


1

一重引用符で囲まれた文字列の方が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

繰り返し実行しても大きな違いはないようです。どちらのバージョンの文字列を解析する場合でも、ほぼ同じ時間がかかります。


0

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

テーブルベースのスキャナーでは、トランジションを決定するための単一のルックアップとなり、いずれにしても各キャラクターに対して発生します。

パーサーがスキャナー出力を取得したとき、ブロック内のコードを評価する必要があることはすでにわかっています。そのため、オーバーヘッドは、実際には#{}ブロックを処理するためのスキャナー/パーサーのメモリオーバーヘッドにすぎません。

私が何かを欠落していない限り(またはコンパイラの構成の詳細を覚えていない場合)、それも確かに可能です:)


0
~ > 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

あなたがすべて逃した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、およびテストの実行で実際にそれを実行しませんか?


0

私は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)

0

私は以下を試しました:

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

私がミスをしなかった場合、シングルクォートの方がほとんどの場合少し高速ですが、両方にほぼ同じ時間がかかるようです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.