代码之家  ›  专栏  ›  技术社区  ›  dimus

在ruby中使用单引号和双引号是否会提高性能?

  •  121
  • dimus  · 技术社区  · 16 年前

    所以如果我打字

    question = 'my question'
    

    question = "my question"
    

    我猜想ruby在遇到双引号时会试图找出是否需要对某些内容进行评估,并且可能会花费一些周期来完成这项工作。

    14 回复  |  直到 14 年前
        1
  •  89
  •   Community Mohan Dere    9 年前
    $ 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版本一起工作,并清理了标题,并在更快的系统上运行了基准测试。

    interpolation 原因是什么 there is no significant difference in performance 使用单引号与双引号时。

        2
  •  106
  •   the Tin Man    11 年前

    总结: great collaborative Ruby style guide 'string' "string" .

    细节:

    解析

    执行 一模一样

    基准测试这只表明对Ruby如何工作缺乏了解。在这两种情况下,字符串都将被解析为 tSTRING_CONTENT (见 the source in parse.y “字符串” 或 “字符串” . 完全相同的位将以完全相同的方式翻转。基准测试这只会显示由于其他因素(GC启动等)造成的不显著差异;记住,在这种情况下没有任何区别!像这样的微观基准很难获得正确的结果。看到我的宝石了吗 fruity 这是一个不错的工具。

    请注意,如果有插值的形式 "...#{...}..." tSTRING_DBEG tSTRING_DVAR 对于中的每个表达式 #{...} tSTRING_DEND . 不过,这只是在有插值的情况下,而插值不是OP的内容。

    #{some_var} 稍后),但我现在使用单引号,除非我需要插值, \n

        3
  •  35
  •   go2null Ruzbeh Irani    10 年前

    $ 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 concat single = 3.76 作为锦上添花,我还发现插值比 'a' + var + 'b' 特别是在空间方面。

        4
  •  16
  •   Community Mohan Dere    9 年前

    #{some_var} 样式字符串插值。但是,只有你真的这样做了,你才能得到性能的提升。

    修改自 Zetetic's

    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
  •  13
  •   Rein Henrichs    16 年前

    单引号可以比双引号稍微快一点,因为lexer不必检查 #{} 插值标记。取决于实现等。请注意,这是解析时间成本,而不是运行时成本。

    也就是说,实际的问题是使用双引号字符串是否“以任何有意义的方式降低性能”,答案是决定性的“否”。性能上的差异是如此之小,以至于与任何真正的性能问题相比都是微不足道的。不要浪费你的时间。

    'foo' 将比 "#{sleep 1; nil}foo" .

        6
  •  9
  •   PhilT    16 年前

    我想添加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)
    
        7
  •  9
  •   aqn    10 年前

        8
  •  3
  •   Andy Lester    16 年前

    两个方向上都没有显著差异。它必须是巨大的,才有意义。

    除了您确定在计时方面存在实际问题的时候,请针对程序员的可维护性进行优化。

    如果优化意味着代码更难维护,那么在数千次运行中节省几秒钟甚至几分钟的运行时间又有什么好处呢?

    选择一种风格,并坚持下去,但一定要这样做 不

        9
  •  1
  •   the Tin Man    11 年前

    我也认为单引号字符串可能会更快地解析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
    

        10
  •  1
  •   Marcelo Xavier    10 年前

    我尝试了以下方法:

    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
    

    这些是输出:

    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
    

    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
    

    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
    

    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
    

        11
  •  0
  •   µBio    16 年前

    这当然是可能的,这取决于实现,但是解释器的扫描部分应该只查看每个字符一次。它只需要一个额外的状态(或可能的状态集)和转换来处理{}块。

    在一个基于表格的扫描器中,这将是一个单一的查找来确定转换,并且对每个字符都会发生。

    当解析器获得扫描器输出时,已经知道它将必须评估块中的代码。因此,开销实际上只是扫描器/解析器处理#{}块的内存开销,您需要为此付出代价。

    除非我遗漏了什么(或者错误地记住了编译器的构造细节),这当然也是可能的:)

        12
  •  0
  •   grilix    13 年前
    ~ > 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)
    
        13
  •  0
  •   Douglas G. Allen    13 年前

    有一个你们都错过了。

    这里,医生

    试试这个

    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更多地学习文档操作语言。

        14
  •  0
  •   Nick    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)