ruby - 为什么就地修改 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。那不是问题。这是一个更大、更复杂的计算的抽象。问题是,就地修改字符串似乎比将字符串分解为字符串数组、创建新的字符串数组并将它们重新连接在一起要快。然而事实并非如此。
此外,这种行为在整数数组中是看不到的。在整数数组上使用第三种方法与前两种方法一样快。这似乎是字符串特有的。
解决方案
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)
推荐阅读
- android - CommandInvokationFailure:重新打包资源失败。(同时使用移动广告 GGDPR)unity, android
- python-3.x - 模块初始化错误:无法在 AWS lambda 加载本机模块“Crypto.Cipher._raw_ecb”
- django - 如何将结果缩小到每个用户 1 个?
- bootstrap-4 - Bootstrap响应式设计中带有字体真棒圆圈的居中标题
- python - 如何根据某些条件将 CSS 应用于 QHeaderView 部分
- bash - 如何检查期望块内的命令的成功或失败
- php - 在 PHP 中从 3 个文本文件进行所有组合
- rest - 可以使用 spark 集群代替 java REST 应用程序吗
- spring - 我什么时候需要将 Spring Cloud Config 与 Vault 一起使用,而不仅仅是使用 Spring Cloud Vault?
- java - layout_constraintGuide_percent 不起作用 - android