¿Hay una ganancia de rendimiento en el uso de comillas simples vs comillas dobles en ruby?


¿Sabe si usar comillas dobles en lugar de comillas simples en ruby disminuye el rendimiento de alguna manera significativa en ruby 1.8 y 1.9?

Así que si escribo

question = 'my question'

Es más rápido que

question = "my question"

Me imagino que ruby intenta averiguar si algo necesita ser evaluado cuando encuentra comillas dobles y probablemente pasa algunos ciclos haciendo precisamente eso.

Author: Andrew Grimm, 2009-12-03

14 answers

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

Nota: He actualizado esto para que funcione con las versiones más nuevas de Ruby, y limpiado el encabezado, y ejecutar el benchmark en un sistema más rápido.

Esta respuesta omite algunos puntos clave. Vea especialmente estas otras respuestas concernientes a interpolación y la razón no hay diferencia significativa en el rendimiento cuando se usan comillas simples vs.dobles.

 85
Author: zetetic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:54:54

Resumen: no hay diferencia de velocidad; esta gran guía colaborativa de estilo Ruby recomienda ser consistente. Ahora uso 'string' a menos que sea necesaria la interpolación (opción A en la guía) y me gusta, pero normalmente verá más código con "string".

Detalles:

Teóricamente, puede hacer una diferencia cuando su código es analizado , pero no solo debe no preocuparse por el tiempo de análisis en general (insignificante en comparación con el tiempo de ejecución), usted no será capaz de encontrar una diferencia significativa en este caso.

Lo importante es que cuando se ejecuta será exactamente el mismo.

Benchmarking esto solo muestra una falta de comprensión de cómo funciona Ruby. En ambos casos, las cadenas se analizarán a tSTRING_CONTENT (ver la fuente en parse.y). En otras palabras, la CPU pasará exactamente por las mismas operaciones al crear 'string' o "string". Los mismos bits se voltearán exactamente igual manera. Benchmarking esto solo mostrará diferencias que no son significativas y debido a otros factores (GC pateando, etc.); recuerde, no puede haber ninguna diferencia en este caso! Micro benchmarks como estos son difíciles de hacer bien. Ver mi joya fruity para una herramienta decente para esto.

Tenga en cuenta que si hay interpolación de la forma "...#{...}...", esto se analiza a un tSTRING_DBEG, un montón de tSTRING_DVAR para cada expresión en #{...} y tSTRING_DEND. Eso es solo si hay interpolación, sin embargo, que no es de lo que se trata la operación.

Solía sugerirle que use comillas dobles en todas partes (hace que sea más fácil agregar eso #{some_var} más adelante), pero ahora uso comillas simples a menos que necesite interpolación, \n, etc... Me gusta visualmente y es un poco más explícito, ya que no hay necesidad de analizar la cadena para ver si contiene alguna expresión.

 100
Author: Marc-André Lafortune,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-10 23:53:21

Sin embargo, nadie midió concatenación vs interpolació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)

Específicamente, note assign interp = 2.62 vs concat single = 3.76. Como guinda del pastel, también encuentro que la interpolación es más legible que 'a' + var + 'b', especialmente con respecto a los espacios.

 35
Author: Tim Snowhite,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-12 19:17:47

No hay diferencia - a menos que esté usando #{some_var} interpolación de cadena de estilo. Pero solo obtienes el golpe de rendimiento si realmente lo haces.

Modificado del ejemplo de 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

Salida

               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
Author: madlep,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:34:34

Las comillas simples pueden ser un poco más rápidas que las comillas dobles porque el lexer no tiene que comprobar los marcadores de interpolación #{}. Dependiendo de la implementación, etc. Tenga en cuenta que este es un costo de tiempo de análisis, no un costo de tiempo de ejecución.

Dicho esto, la pregunta real era si el uso de cadenas entre comillas dobles "disminuye el rendimiento de alguna manera significativa", a lo que la respuesta es un "no"decisivo. La diferencia en el rendimiento es tan increíblemente pequeña que es completamente insignificante en comparación con cualquier problema de rendimiento real. No pierdas el tiempo.

La interpolación real es una historia diferente, por supuesto. 'foo' será casi exactamente 1 segundo más rápido que "#{sleep 1; nil}foo".

 13
Author: Rein Henrichs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-10-03 20:52:11

Pensé en añadir una comparación de 1.8.7 y 1.9.2. Corrí un par de veces. La varianza fue de aproximadamente +-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 revisión 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
Author: PhilT,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-10-18 20:05:41

Las comillas dobles requieren el doble de teclas que las comillas simples. Siempre tengo prisa. Uso comillas simples. :) Y sí, considero que una "ganancia de rendimiento". :)

 8
Author: aqn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-25 14:57:25

No hay diferencia significativa en ninguna dirección. Tendría que ser enorme para que la materia.

Excepto cuando esté seguro de que hay un problema real con el tiempo, optimice la capacidad de mantenimiento del programador.

Los costos del tiempo de la máquina son muy, muy pequeños. Los costos de tiempo del programador para escribir código y mantenerlo son enormes.

¿ De qué sirve una optimización para ahorrar segundos, incluso minutos de tiempo de ejecución en miles de ejecuciones si significa que el código es más difícil de mantener?

Elija con un estilo y quédese con él, pero no elija ese estilo basado en milisegundos estadísticamente insignificantes de tiempo de ejecución.

 3
Author: Andy Lester,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-10-18 20:32:51

Yo también pensé que las cadenas entre comillas simples podrían ser más rápidas de analizar para Ruby. No parece ser el caso.

De todos modos, creo que el punto de referencia anterior está midiendo lo incorrecto, sin embargo. Es lógico pensar que cualquiera de las versiones se analizará en las mismas representaciones internas de cadenas, por lo que para obtener la respuesta en cuanto a cuál es más rápido de analizar, no deberíamos medir el rendimiento con variables de cadena, sino más bien la velocidad de Ruby de analizar cadenas.

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

Corridas repetidas no parece hacer mucha diferencia. Todavía se necesita más o menos el mismo tiempo para analizar cualquier versión de la cadena.

 1
Author: PSkocik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-10 23:54:05

Ciertamente es posible dependiendo de la implementación, pero la parte de escaneo del intérprete solo debe mirar cada carácter una vez. Solo necesitará un estado adicional (o un posible conjunto de estados) y transiciones para manejar #{} bloques.

En un escáner basado en tabla, eso va a ser una sola búsqueda para determinar la transición, y sucederá para cada personaje de todos modos.

Cuando el analizador obtiene la salida del escáner, ya se sabe que tendrá que evaluar el código en la cuadra. Por lo tanto, la sobrecarga es solo la sobrecarga de memoria en el escáner/analizador para manejar el bloque # {}, que se paga de cualquier manera.

A menos que me esté perdiendo algo (o recordar incorrectamente los detalles de construcción del compilador), lo cual también es ciertamente posible:)

 0
Author: µBio,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-12-03 01:28:08
~ > 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
Author: grilix,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-26 17:43:22

Hay uno que todos se perdieron.

AQUÍ doc

Prueba esto

require 'benchmark'
mark = <<EOS
a string
EOS
n = 1000000
Benchmark.bm do |x|
  x.report("assign here doc") {n.times do;  mark; end}
end

Me dio

`asign here doc  0.141000   0.000000   0.141000 (  0.140625)`

Y

'concat single quotes  1.813000   0.000000   1.813000 (  1.843750)'
'concat double quotes  1.812000   0.000000   1.812000 (  1.828125)'

Así que es ciertamente mejor que concat y escribir todos esos puts.

Me gustaría ver que Ruby enseñara más a lo largo de las líneas de un lenguaje de manipulación de documentos.

Después de todo, ¿realmente no hacemos eso en Rails, Sinatra y ejecutando pruebas?

 0
Author: Douglas G. Allen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-01-30 02:59:44

Modded respuesta de 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

Resultados:

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
Author: Nick,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-28 13:49:28

He intentado lo siguiente:

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

Y estas son las salidas:

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

Si no me equivoco, me parece que ambos toman aproximadamente el mismo tiempo, a pesar de que una sola cita es un poco más rápido en la mayoría de los casos.

 0
Author: Marcelo Xavier,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-11-10 15:06:52