¿Hay una ganancia de rendimiento al usar comillas simples vs comillas dobles en ruby?


126

¿Sabe si el uso de comillas dobles en lugar de comillas simples en ruby ​​disminuye el rendimiento de alguna manera significativa en ruby ​​1.8 y 1.9.

entonces 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 eso.


17
Ejecútalo medio millón de veces y verás. Lo más probable es que su sitio no reciba el tráfico suficiente para importar. La optimización prematura generalmente no vale la pena.
ceejayoz

6060
¿Por qué tanta gente espera que ruby ​​solo se use para la programación web?
johannes

17
No consideraría esta optimización prematura. Más de una "mejor práctica", ya que volver después de que su aplicación esté completa y optimizar para uno o dos sería un gran dolor de cabeza.
Omar

77
Para mí es solo estilo: utilizo comillas simples para cadenas 'estáticas' y dobles qoutes (u otras cadenas interpoladas) en otros casos.
tig

3
@Baddie: es una optimización prematura si estás optimizando un problema que no existe.
Andy Lester

Respuestas:


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)

Nota: He actualizado esto para que funcione con las versiones más nuevas de Ruby, limpié el encabezado y ejecuté el punto de referencia en un sistema más rápido.

Esta respuesta omite algunos puntos clave. Vea especialmente estas otras respuestas con respecto a la interpolación y la razón por la que no hay una diferencia significativa en el rendimiento cuando se utilizan comillas simples o dobles.


¿Estoy interpretando los resultados correctamente? ¿La asignación usando comillas dobles es realmente más rápida que la simple? ¿Cómo puede ser esto?
randomguy

Aparentemente sí, aunque la diferencia es menor. En cuanto a por qué, me gana.
Zetetic

Este punto de referencia sería mucho más convincente si tuviera en cuenta el tiempo de compilación y el tiempo de ejecución.
nohat

9
Las diferencias medidas no son significativas. Solo el orden (debido a la recolección de basura) puede marcar una diferencia importante. No hay diferencia de tiempo de ejecución entre 'y "como se analizan en la misma cosa.
Marc-André Lafortune

104

Resumen: sin diferencia de velocidad; Esta gran guía de estilo de colaboración Ruby recomienda ser coherente. Ahora lo uso a 'string'menos que sea necesaria la interpolación (opción A en la guía) y me guste, pero normalmente verá más código con "string".

Detalles:

Teóricamente, puede hacer una diferencia cuando se analiza su código , pero no solo si no le importa el tiempo de análisis en general (insignificante en comparación con el tiempo de ejecución), no podrá encontrar una diferencia significativa en este caso.

Lo importante es que cuando se ejecute será exactamente igual .

La evaluación comparativa de esto solo muestra una falta de comprensión de cómo funciona Ruby. En ambos casos, las cadenas se analizarán en a tSTRING_CONTENT(consulte la fuente enparse.y ). En otras palabras, la CPU realizará exactamente las mismas operaciones al crear 'string'o "string". Los mismos bits exactos darán la vuelta de la misma manera. La evaluación comparativa solo mostrará diferencias que no son significativas y que se deben a otros factores (activación de GC, etc.) recuerde, no puede haber ninguna diferencia en este caso! Micro puntos de referencia como estos son difíciles de acertar. Vea mi joya fruitypara una herramienta decente para esto.

Tenga en cuenta que si hay una interpolación de la forma "...#{...}...", esta se analiza en a tSTRING_DBEG, un montón de tSTRING_DVARpara cada expresión #{...}y una final tSTRING_DEND. Sin embargo, eso es solo si hay interpolación, que no es de lo que se trata el OP.

Solía ​​sugerirle que use comillas dobles en todas partes (hace que sea más fácil agregarlas #{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 necesita analizar la cadena para ver si contiene alguna expresión.


3
Parece mucho más importante que la diferencia de rendimiento minuto. ¡Es comillas dobles!
Venkat D.

Gracias por señalarme tu respuesta. ¿Podría aclarar por qué dice que la evaluación comparativa es engañosa? Estoy de acuerdo en que las diferencias son probablemente insignificantes, pero ¿es el punto de referencia de alguna manera incorrecto? (Alguien ya resaltó #{n}que estaría haciendo la conversión de números). ¿No muestra las diferencias en el análisis?
PhilT

1
Gracias por vincularte a la guía de estilo. No puedo creer que no haya encontrado eso antes.
PhilT

1
La guía de estilo mencionada en su respuesta se ha actualizado para sugerir la adopción de un estilo coherente, ya sea con comillas simples o dobles, e indica que las cadenas con comillas dobles son más frecuentes en la comunidad Ruby.
philtr

Usa comillas dobles. La programación es difícil. La sintaxis es inherentemente compleja. Las comillas dobles significan nunca cometer un error o perder el tiempo con un error al hacer una cadena dinámica. Con comillas dobles, tiene una cosa menos en que pensar.
Kelsey Hannan el

35

Sin embargo, nadie midió la concatenación frente a la 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, tenga en cuenta assign interp = 2.62vs 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.


+1. Este es el único punto de referencia de interpolación que compara manzanas con manzanas.
Mark Thomas

1
La evaluación comparativa puede ser engañosa; mira mi respuesta por qué. En cuanto a la comparación entre concatenación e interpolación, debería ser obvio que la interpolación no puede ser más lenta que la concatenación. En cualquier caso, ¡eso no es realmente parte de la pregunta!
Marc-André Lafortune

¿Puedes agregar << a esta prueba?
Nick

16

No hay diferencia, a menos que esté utilizando la #{some_var}interpolación de cadenas de estilo. Pero solo obtienes el éxito de rendimiento si realmente haces eso.

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)

Interesante. La interpolación parece un poco más cara. ¿Era esto 1.8? Sería bueno ver si 1.9 cambia algo.
Zetetic

zetetic - si. Esto fue contra Ruby 1.8.7
madlep

1
La versión de interp es interpolar y concatenar, además de convertir un número en una cadena dos veces. La interpolación gana si haces que los resultados sean los mismos. Ver gist.github.com/810463 . La verdadera conclusión es preocuparse más por to_s que por comillas simples o dobles.
Brian Deterling

La evaluación comparativa de esto solo puede ser engañosa y muestra un malentendido sobre cómo funciona Ruby. Mira mi respuesta.
Marc-André Lafortune

13

Las comillas simples pueden ser ligeramente más rápidas que las dobles porque el lexer no tiene que verificar 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 dobles entre comillas "disminuye el rendimiento de alguna manera significativa", a lo que la respuesta es un decisivo "no". 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 tu 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".


44
+1 por señalar que el costo es en tiempo de compilación, no en tiempo de ejecución, por lo que las respuestas basadas en puntos de referencia altamente votados anteriormente son engañosas.
nohat

"Este es un costo de tiempo de análisis, no un costo de tiempo de ejecución". Es la frase clave.
The Tin Man

9

Las comillas dobles requieren el doble de golpes clave para escribir que las comillas simples. Siempre tengo prisa. Yo uso comillas simples. :) Y sí, considero que es una "ganancia de rendimiento". :)


¿Por qué las comillas dobles tomarían el doble de golpes clave? Ambos están representados por una sola clave. Además, muchos IDE agregan las comillas de cierre automáticamente.
Matt Dressel

3
Incluso si el IDE cierra automáticamente la cotización, las comillas dobles aún requieren un 100% más de golpes clave. ;-)
Clint Pachl

Matt Dressel: las comillas dobles requieren el doble del número de pulsaciones de teclas porque también debe pulsar la tecla Mayús. Oh: :) en caso de que te lo hayas perdido en mi comentario original. :) Las teclas con cable requieren más esfuerzo y posiblemente más tiempo para ejecutarse. :)
aqn

1
A veces sigo este consejo por flojera. Pero desafortunadamente en algunos otros idiomas, es todo lo contrario (por ejemplo, las comillas simples necesitan Shift + algo, mientras que las comillas dobles son una sola pulsación de tecla). Lamentable porque si dos personas con diferentes diseños de teclado trabajan en el mismo proyecto, uno de ellos tendrá que sacrificar algunas pulsaciones de teclas :)
Halil Özgür

"Soy un hombre apurado": a menos que presione Mayús y 2 (o cualquier otra tecla que sea) una tras otra, no ahorrará tiempo utilizando comillas simples.
Machisuji

8

Pensé que agregaría una comparación de 1.8.7 y 1.9.2. Los corrí algunas veces. La variación 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 (revisión 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 tiene que hacer conversiones de números a cadenas. Ver gist.github.com/810463 .
Brian Deterling

Vea mi respuesta sobre por qué obtiene estos números.
Marc-André Lafortune

Buen punto sobre el Interp. Acababa de copiar la respuesta anterior como base para la mía. Eso me enseñará
PhilT

3

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

Excepto en los momentos en que esté seguro de que hay un problema real con el tiempo, optimice para mantener el programador.

Los costos del tiempo de máquina son muy muy pequeños. El costo del tiempo del programador para escribir código y mantenerlo es enorme.

¿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 un estilo y manténgalo, pero no elija ese estilo basado en milisegundos estadísticamente insignificantes de tiempo de ejecución.


1

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 algo incorrecto. Es lógico pensar que cualquiera de las versiones se analizará en las mismas representaciones de cadenas internas, por lo que para obtener la respuesta sobre 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

Las corridas repetidas no parecen hacer mucha diferencia. Todavía lleva casi el mismo tiempo analizar cualquier versión de la cadena.


0

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

En un escáner basado en tablas, se realizará una búsqueda única para determinar la transición, y de todos modos sucederá para cada personaje.

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

A menos que me falte algo (o no recuerde los detalles de construcción del compilador), que también es posible :)


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

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 ciertamente es mejor que concat y escribir todos esos put.

Me gustaría ver a Ruby enseñado más en la línea de un lenguaje de manipulación de documentos.

Después de todo, ¿no hacemos eso realmente en Rails, Sinatra y en las pruebas en ejecución?


0

Modifiqué la 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

Intenté 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 estos son los resultados:

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 equivoqué, me parece que ambos toman aproximadamente el mismo tiempo, a pesar de que las citas simples son ligeramente más rápidas en la mayoría de los casos.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.