您是否知道在ruby中使用双引号而不是单引号会以任何有意义的方式在ruby 1.8和1.9中降低性能。
所以如果我输入
question = 'my question'
比它快吗
question = "my question"
我想像一下,ruby会尝试找出某些事物在遇到双引号时是否需要评估,并可能花费一些周期来进行评估。
您是否知道在ruby中使用双引号而不是单引号会以任何有意义的方式在ruby 1.8和1.9中降低性能。
所以如果我输入
question = 'my question'
比它快吗
question = "my question"
我想像一下,ruby会尝试找出某些事物在遇到双引号时是否需要评估,并可能花费一些周期来进行评估。
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)
注意:我已经对其进行了更新,以使其能够与较新的Ruby版本一起使用,并清理了标头,并在更快的系统上运行了基准测试。
'
和"
被解析为同一事物之间,它们之间没有运行时差异。
摘要:无速度差异;这份出色的协作Ruby风格指南建议保持一致。我现在使用'string'
除非需要插值(指南中的选项A)并且喜欢它,但是您通常会看到更多带有"string"
。
细节:
从理论上讲,它在解析代码时会有所不同,但不仅您不应该一般地关心解析时间(与执行时间相比可以忽略不计),而且在这种情况下您将找不到明显的区别。
重要的是,执行时将完全相同。
进行基准测试仅表明缺乏对Ruby工作原理的理解。在这两种情况下,字符串都将被解析为tSTRING_CONTENT
(请参见中的源代码parse.y
)。换句话说,创建'string'
或时,CPU将经历完全相同的操作"string"
。完全相同的位将以完全相同的方式翻转。以此为基准只能显示不重要的差异,并且是由于其他因素(GC启动等)引起的;请记住,在这种情况下没有任何区别!像这样的微观基准很难正确。看我的宝石fruity
为这个体面的工具。
请注意,如果存在形式的内插,则将其"...#{...}..."
解析为a tSTRING_DBEG
,一束tSTRING_DVAR
对于每个in中的表达式#{...}
以及一个finaltSTRING_DEND
。但是,只有在存在插值的情况下,OP才不会这样做。
我曾经建议您在所有地方都使用双引号(这样#{some_var}
以后可以更容易地添加它),但是现在我使用单引号,除非需要插值\n
等,...我在视觉上喜欢它,并且它稍微更明确了,因为没有需要分析字符串以查看它是否包含任何表达式。
#{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)
特别要注意assign interp = 2.62
VS concat single = 3.76
。锦上添花,我还发现插值比'a' + var + 'b'
空格更易读。
没什么区别-除非您使用#{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)
单引号可能比双引号稍微快一点,因为词法分析器不必检查#{}
插值标记。取决于实现方式等。请注意,这是解析时成本,而不是运行时成本。
也就是说,实际的问题是使用双引号的字符串是否“以任何有意义的方式降低了性能”,对此答案是决定性的“否”。性能上的差异是如此之小,以至于与任何实际性能问题相比,它都是微不足道的。不要浪费你的时间。
当然,实际插值是一个不同的故事。'foo'
几乎比快1秒"#{sleep 1; nil}foo"
。
双引号输入的击键次数是单引号的两倍。我总是很着急。我使用单引号。:)是的,我认为这是“性能提升”。:)
以为我会添加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
红宝石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)
红宝石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)
任一方向都没有显着差异。事情要大很多。
除了确定时序确实存在问题之外,请针对程序员的可维护性进行优化。
机器时间的花费非常小。程序员花费时间编写和维护代码的费用巨大。
如果可以节省几秒钟的时间,甚至可以节省数千秒的运行时间(甚至数千分钟),那么优化有什么好处呢?
挑选一个风格,坚持下来的,但千万不能挑基于运行时的统计显着毫秒风格。
我也认为单引号的字符串可能更快地解析为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
重复运行似乎没有多大区别。解析任一版本的字符串仍需要花费几乎相同的时间。
~ > 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)
你们都错过了一个。
这里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和编写所有看跌期权要好。
我希望看到Ruby在文档处理语言方面教得更多。
毕竟,我们不是真的在Rails,Sinatra和运行测试中这样做吗?
我修改了蒂姆·斯诺怀特的答案。
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)
我尝试了以下方法:
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
如果我没有记错的话,尽管在大多数情况下用单引号括起来稍微快一点,但在我看来这似乎都需要花费大约相同的时间。