Есть ли выигрыш в производительности при использовании одинарных кавычек и двойных кавычек в рубине?

Знаете ли вы, что использование двойных кавычек вместо одиночных кавычек в ruby ​​снижает производительность любым значимым способом в ruby ​​1.8 и 1.9.

поэтому, если я набираю

question = 'my question'

выполняется быстрее, чем

question = "my question"

Я предполагаю, что рубин пытается выяснить, нужно ли что-то оценивать, когда он сталкивается с двойными кавычками и, вероятно, проводит несколько циклов, делая именно это.

Ответ 1

$ 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, а также очистил заголовок и запустил тест в более быстрой системе.

В этом ответе отсутствуют некоторые ключевые моменты. См., В частности, эти другие ответы относительно интерполяции и причины нет существенной разницы в производительности при использовании single vs. двойные кавычки.

Ответ 2

Резюме: нет разницы в скорости; этот отличный совлокальный рубиновый стиль руководства рекомендует быть последовательным. Теперь я использую 'string', если не требуется интерполяция (опция A в руководстве) и, как и она, но вы обычно увидите больше кода с помощью "string".

Детали:

Теоретически, это может иметь значение, когда ваш код анализируется, но не только вам не нужно заботиться о времени синтаксического анализа вообще (незначительно по сравнению с временем выполнения), вы не сможете найти существенную разницу в этом случае.

Важно то, что когда он выполняется, он будет точно таким же.

Бенчмаркинг только показывает отсутствие понимания того, как работает Ruby. В обоих случаях строки будут анализироваться на tSTRING_CONTENT (см. источник в parse.y). Другими словами, процессор будет выполнять те же операции при создании 'string' или "string". Точно такие же биты будут переворачиваться точно так же. Бенчмаркинг это приведет только к различиям, которые не являются существенными и из-за других факторов (GC kicking in и т.д.); помните, в этом случае не может быть никакой разницы! Микроэлементов, подобных этим, сложно добиться. Посмотрите мой драгоценный камень fruity для достойного инструмента для этого.

Заметим, что если есть интерполяция формы "...#{...}...", она анализируется на tSTRING_DBEG, пучок tSTRING_DVAR для каждого выражения в #{...} и окончательный tSTRING_DEND. Это только в случае интерполяции, но это не то, о чем говорит OP.

Раньше я предлагал вам использовать двойные кавычки повсюду (упрощает фактически добавлять это #{some_var} позже), но теперь я использую одинарные кавычки, если мне не нужна интерполяция, \n и т.д. Мне это нравится и он несколько более явный, поскольку нет необходимости разбирать строку, чтобы увидеть, содержит ли она какое-либо выражение.

Ответ 3

Никто не измерил сцепление с интерполяцией, хотя:

$ 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', особенно в отношении пространств.

Ответ 4

Нет разницы - если вы не используете интерполяцию строки стиля #{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)

Ответ 5

Одиночные кавычки могут быть очень немного быстрее, чем двойные кавычки, потому что лексер не должен проверять маркеры интерполяции #{}. В зависимости от реализации и т.д. Обратите внимание, что это затраты времени синтаксического анализа, а не затраты времени выполнения.

Тем не менее, фактический вопрос заключался в том, использует ли двойные кавычки строки "снижение производительности любым значимым образом", на который ответ является решающим "нет". Разница в производительности настолько невероятно мала, что она совершенно ничтожна по сравнению с любой реальной проблемой производительности. Не тратьте впустую свое время.

Фактическая интерполяция - это совсем другая история. 'foo' будет почти ровно на 1 секунду быстрее, чем "#{sleep 1; nil}foo".

Ответ 6

Думаю, я бы добавил сравнение 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 версия 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)

Ответ 7

Двойные кавычки занимают в два раза больше ключевых ударов, чем одиночные кавычки. Я всегда тороплюсь. Я использую одинарные кавычки.:) И да, я считаю, что "выигрыш в производительности".:)

Ответ 8

В любом направлении нет существенной разницы. Это должно было быть огромным, поскольку это имело значение.

За исключением случаев, когда вы уверены, что существует настоящая проблема с синхронизацией, оптимизируйте для обслуживания, поддерживаемой программистом.

Стоимость машинного времени очень мала. Стоимость времени программиста для написания кода и его поддержки огромна.

Какая польза от оптимизации, чтобы сэкономить секунды, даже минуты работы над тысячами запусков, если это означает, что код сложнее поддерживать?

Выберите стиль и придерживайтесь его, но не выбирайте этот стиль на основе статистически незначимых миллисекунд времени выполнения.

Ответ 9

Я тоже думал, что одиночные кавычки могут быть быстрее проанализированы для 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

Повторяющиеся прогоны, похоже, не имеют большого значения. По-прежнему требуется примерно одно и то же время для анализа любой версии строки.

Ответ 10

Это, безусловно, возможно в зависимости от реализации, но часть сканирования интерпретатора должна смотреть только на каждый символ один раз. Это потребует только дополнительного состояния (или возможного набора состояний) и переходов для обработки блоков # {}.

В настольном сканере, который будет единственным поиском для определения перехода, и будет происходить для каждого персонажа в любом случае.

Когда парсер получает вывод сканера, он уже знал, что он должен будет вычислять код в блоке. Таким образом, накладные расходы - это действительно накладные расходы памяти в сканере/парсере для обработки блока # {}, который вы платите в любом случае.

Если я что-то не упустил (или не понял детали компилятора), что также возможно:)

Ответ 11

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

Ответ 12

Там вы все пропустили.

ЗДЕСЬ doc

попробуйте это

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

так что это, безусловно, лучше, чем concat и писать все эти puts.

Я хотел бы видеть, как Руби учил больше по языку манипуляции документами.

В конце концов, разве мы не делаем это в Rails, Sinatra и запусках тестов?

Ответ 13

Я изменил ответ Тима Сноуита.

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)

Ответ 14

Я попробовал следующее:

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

Если я не ошибся, мне кажется, что оба они берут примерно одно и то же время, хотя в большинстве случаев одинарная цитата немного быстрее.