包含数字的字符串与数字相比要慢多少?

时间:2011-06-22 19:37:05

标签: ruby string performance integer

假设我想取一个数字并将其数字作为数组返回Ruby中 对于这个特定目的或者字符串函数和数字函数一般来说哪个更快?

这些是我认为最常用的算法:

使用字符串:n.to_s.split(//).map {|x| x.to_i}

使用数字:

array = []
until n = 0
    m = n % 10
    array.unshift(m)
    n /= 10 
end

3 个答案:

答案 0 :(得分:8)

差异似乎小于一个数量级,Fixnum s的基于整数的方法更快。对于Bignum s,相对性能开始或多或少均匀,随着数字位数的增长,字符串方法显着胜出。

作为字符串

程序

#!/usr/bin/env ruby

require 'profile'

$n = 1234567890
10000.times do
    $n.to_s.split(//).map {|x| x.to_i}
end

输出

  %   cumulative   self              self     total
 time   seconds   seconds    calls  ms/call  ms/call  name
 55.64     0.74      0.74    10000     0.07     0.10  Array#map
 21.05     1.02      0.28   100000     0.00     0.00  String#to_i
 10.53     1.16      0.14        1   140.00  1330.00  Integer#times
  7.52     1.26      0.10    10000     0.01     0.01  String#split
  5.26     1.33      0.07    10000     0.01     0.01  Fixnum#to_s
  0.00     1.33      0.00        1     0.00  1330.00  #toplevel

作为整数

程序

#!/usr/bin/env ruby

require 'profile'

$n = 1234567890
10000.times do
    array = []
    n = $n
    until n == 0
        m = n%10
        array.unshift(m)
        n /= 10
    end
    array
end

输出

  %   cumulative   self              self     total
 time   seconds   seconds    calls  ms/call  ms/call  name
 70.64     0.77      0.77        1   770.00  1090.00  Integer#times
 29.36     1.09      0.32   100000     0.00     0.00  Array#unshift
  0.00     1.09      0.00        1     0.00  1090.00  #toplevel

附录

该模式似乎也适用于较小的数字。对于$n = 12345,基于字符串的方法大约为800ms,基于整数的方法大约为550ms。

当我越过边界进入Bignum s时,比如$n = 12345678901234567890,我得到2375毫秒的两种方法。看起来差别很大,我可能会认为内部本地供电Bignum是字符串式的。但是,documentation似乎暗示了其他原因。

出于学术目的,我再次将位数加倍$n = 1234567890123456789012345678901234567890。我得到了大约4450ms的字符串方法和9850ms的整数方法,这是一个明显的逆转,排除了我以前的假设。

摘要

Number of digits | String program | Integer program | Difference
---------------------------------------------------------------------------
5                | 800ms          | 550ms           | Integer wins by 250ms
10               | 1330ms         | 1090ms          | Integer wins by 240ms
20               | 2375ms         | 2375ms          | Tie
40               | 4450ms         | 9850ms          | String wins by 4400ms

答案 1 :(得分:7)

史蒂文的回应令人印象深刻,但我看了几分钟并且无法将其提炼成一个简单的答案,所以这是我的。

对于Fixnums

使用我在下面提供的数字方法最快。使用num.to_s.each_char.map(&:to_i)也很快(也更容易)。


对于Bignums

使用num.to_s.each_char.map(&:to_i)最快。


解决方案

如果速度老实说是您使用的代码(意为don't be evil)的决定因素,那么此代码是作业的最佳选择。< / p>

class Integer
  def digits
    working_int, digits = self, Array.new
    until working_int.zero?
      digits.unshift working_int % 10
      working_int /= 10
    end
    digits
  end
end

class Bignum
  def digits
    to_s.each_char.map(&:to_i)
  end
end

Here是我考虑得出这个结论的方法。

答案 2 :(得分:1)

我使用Steven Xu的代码示例和String#each_byte-version为'benchmark'做了一个解决方案。

require 'benchmark'
MAX = 10_000

#Solution based on http://stackoverflow.com/questions/6445496/how-much-slower-are-strings-containing-numbers-compared-to-numbers/6447254#6447254
class Integer
  def digits
    working_int, digits = self, Array.new
    until working_int.zero?
      digits.unshift working_int % 10
      working_int /= 10
    end
    digits
  end
end

class Bignum
  def digits
    to_s.each_char.map(&:to_i)
  end
end

[
  12345,
  1234567890,
  12345678901234567890,
  1234567890123456789012345678901234567890,
].each{|num|
puts "========="
puts "Benchmark #{num}"
Benchmark.bm do|b|

   b.report("Integer%        ") do
     MAX.times { 
        array = []
        n = num
        until n == 0
            m = n%10
            array.unshift(m)
            n /= 10
        end
        array     
     }
   end

   b.report("Integer% <<     ") do
     MAX.times { 
        array = []
        n = num
        until n == 0
            m = n%10
            array << m
            n /= 10
        end
        array.reverse
     }
   end

   b.report("Integer#divmod  ") do
     MAX.times { 
        array = []
        n = num
        until n == 0
          n, x = *n.divmod(10)
          array.unshift(x)
        end
        array     
     }
   end

   b.report("Integer#divmod<<") do
     MAX.times { 
        array = []
        n = num
        until n == 0
          n, x = *n.divmod(10)
          array << x
        end
        array.reverse
     }
   end

   b.report("String+split//  ") do
     MAX.times { num.to_s.split(//).map {|x| x.to_i} }
   end

   b.report("String#each_byte") do
     MAX.times { num.to_s.each_byte.map{|x| x.chr } }
   end

   b.report("String#each_char") do
     MAX.times { num.to_s.each_char.map{|x| x.to_i } }
   end

   #http://stackoverflow.com/questions/6445496/how-much-slower-are-strings-containing-numbers-compared-to-numbers/6447254#6447254
   b.report("Num#digit       ") do
     MAX.times { num.to_s.each_char.map{|x| x.to_i } }
   end
 end
}

我的结果:

Benchmark 12345
      user     system      total        real
Integer%          0.015000   0.000000   0.015000 (  0.015625)
Integer% <<       0.016000   0.000000   0.016000 (  0.015625)
Integer#divmod    0.047000   0.000000   0.047000 (  0.046875)
Integer#divmod<<  0.031000   0.000000   0.031000 (  0.031250)
String+split//    0.109000   0.000000   0.109000 (  0.109375)
String#each_byte  0.047000   0.000000   0.047000 (  0.046875)
String#each_char  0.047000   0.000000   0.047000 (  0.046875)
Num#digit         0.047000   0.000000   0.047000 (  0.046875)
=========
Benchmark 1234567890
      user     system      total        real
Integer%          0.047000   0.000000   0.047000 (  0.046875)
Integer% <<       0.046000   0.000000   0.046000 (  0.046875)
Integer#divmod    0.063000   0.000000   0.063000 (  0.062500)
Integer#divmod<<  0.062000   0.000000   0.062000 (  0.062500)
String+split//    0.188000   0.000000   0.188000 (  0.187500)
String#each_byte  0.063000   0.000000   0.063000 (  0.062500)
String#each_char  0.093000   0.000000   0.093000 (  0.093750)
Num#digit         0.079000   0.000000   0.079000 (  0.078125)
=========
Benchmark 12345678901234567890
      user     system      total        real
Integer%          0.234000   0.000000   0.234000 (  0.234375)
Integer% <<       0.234000   0.000000   0.234000 (  0.234375)
Integer#divmod    0.203000   0.000000   0.203000 (  0.203125)
Integer#divmod<<  0.172000   0.000000   0.172000 (  0.171875)
String+split//    0.266000   0.000000   0.266000 (  0.265625)
String#each_byte  0.125000   0.000000   0.125000 (  0.125000)
String#each_char  0.141000   0.000000   0.141000 (  0.140625)
Num#digit         0.141000   0.000000   0.141000 (  0.140625)
=========
Benchmark 1234567890123456789012345678901234567890
      user     system      total        real
Integer%          0.718000   0.000000   0.718000 (  0.718750)
Integer% <<       0.657000   0.000000   0.657000 (  0.656250)
Integer#divmod    0.562000   0.000000   0.562000 (  0.562500)
Integer#divmod<<  0.485000   0.000000   0.485000 (  0.484375)
String+split//    0.500000   0.000000   0.500000 (  0.500000)
String#each_byte  0.218000   0.000000   0.218000 (  0.218750)
String#each_char  0.282000   0.000000   0.282000 (  0.281250)
Num#digit         0.265000   0.000000   0.265000 (  0.265625)

String#each_byte / each_char分割速度越快,对于数字越小,整数版本越快。