首页 > 解决方案 > 为什么就地修改 Ruby 字符串这么慢?

问题描述

我很好奇是否有人可以解释为什么就地修改 Ruby 字符串如此缓慢。考虑将长字符串中的 x 更改为 y。这是一种方法:

s = "x"*1000000
s = s.chars.map {|c| c == "x" ? "y" : c }.join

还有一个:

s = "x"*1000000
s = (0...s.length).map {|i| s[i] == "x" ? "y" : s[i] }.join

这些都在 <0.25 秒内完成。现在考虑:

s = "x"*1000000
(0...s.length).each do |i|
  s[i] = "y" if s[i] == "x"
end

这需要 60 秒,增加了 250 倍。

请不要,重复,不要回答有更快的方法可以使用 Regexp、String#gsub 或 String#tr 或其他方式将 x 更改为 y。那不是问题。这是一个更大、更复杂的计算的抽象。问题是,就地修改字符串似乎比将字符串分解为字符串数组、创建新的字符串数组并将它们重新连接在一起要快。然而事实并非如此。

此外,这种行为在整数数组中是看不到的。在整数数组上使用第三种方法与前两种方法一样快。这似乎是字符串特有的。

标签: ruby

解决方案


require 'fruity'

3.times do
  compare do
    t1 do
      s = 'x' * 100_000
      s.chars.map { |c| c == 'x' ? 'y' : c }.join
    end

    t2 do
      s = 'x' * 100_000
      (0...s.length).map { |i| s[i] == 'x' ? 'y' : s[i] }.join
    end

    t3 do
      s = 'x' * 100_000
      (0...s.length).each do |i|
        s[i] = 'y' if s[i] == 'x'
      end
      s
    end
  end
  puts '-' * 5
end
# >> Running each test once. Test will take about 2 seconds.
# >> t1 is faster than t2 by 10.000000000000009% ± 10.0%
# >> t2 is faster than t3 by 10.000000000000009% ± 10.0%
# >> -----
# >> Running each test once. Test will take about 2 seconds.
# >> t1 is faster than t2 by 10.000000000000009% ± 10.0%
# >> t2 is faster than t3 by 10.000000000000009% ± 10.0%
# >> -----
# >> Running each test once. Test will take about 2 seconds.
# >> t1 is similar to t2
# >> t2 is similar to t3
# >> -----
require 'benchmark'

puts 'Ruby %s' % RUBY_VERSION

3.times do
  Benchmark.bmbm do |x|
    s = 'x' * 100_000
    x.report("t1")  { 
      s.chars.map { |c| c == 'x' ? 'y' : c }.join 
    }

    s = 'x' * 100_000
    x.report("t2")  { 
      (0...s.length).map { |i| s[i] == 'x' ? 'y' : s[i] }.join 
    }

    s = 'x' * 100_000
    x.report("t3")  { 
      (0...s.length).each do |i|
        s[i] = 'y' if s[i] == 'x'
      end  
    }
  end
  puts "\n" * 2
end
# >> Ruby 2.7.1
# >> Rehearsal --------------------------------------
# >> t1   0.038257   0.002995   0.041252 (  0.041296)
# >> t2   0.032536   0.000467   0.033003 (  0.033034)
# >> t3   0.030509   0.000018   0.030527 (  0.030539)
# >> ----------------------------- total: 0.104782sec
# >> 
# >>          user     system      total        real
# >> t1   0.019340   0.000012   0.019352 (  0.019354)
# >> t2   0.040595   0.000228   0.040823 (  0.040834)
# >> t3   0.017477   0.000118   0.017595 (  0.017603)
# >> 
# >> 
# >> Rehearsal --------------------------------------
# >> t1   0.032491   0.001619   0.034110 (  0.034195)
# >> t2   0.032207   0.000259   0.032466 (  0.032531)
# >> t3   0.030463   0.000145   0.030608 (  0.030631)
# >> ----------------------------- total: 0.097184sec
# >> 
# >>          user     system      total        real
# >> t1   0.019730   0.000011   0.019741 (  0.019743)
# >> t2   0.043388   0.000212   0.043600 (  0.043639)
# >> t3   0.016638   0.000009   0.016647 (  0.016647)
# >> 
# >> 
# >> Rehearsal --------------------------------------
# >> t1   0.030882   0.000480   0.031362 (  0.031434)
# >> t2   0.030787   0.000647   0.031434 (  0.031444)
# >> t3   0.026310   0.000009   0.026319 (  0.026320)
# >> ----------------------------- total: 0.089115sec
# >> 
# >>          user     system      total        real
# >> t1   0.015613   0.000004   0.015617 (  0.015618)
# >> t2   0.037264   0.000367   0.037631 (  0.037699)
# >> t3   0.016907   0.000097   0.017004 (  0.017003)

推荐阅读