哪些数组初始化在Ruby中更好?

时间:2010-06-15 06:12:33

标签: ruby performance arrays memory initialization

这两种形式的数组初始化中哪一种在Ruby中更好?

方法1:

DAYS_IN_A_WEEK = (0..6).to_a
HOURS_IN_A_DAY = (0..23).to_a

@data = Array.new(DAYS_IN_A_WEEK.size).map!{ Array.new(HOURS_IN_A_DAY.size) }

DAYS_IN_A_WEEK.each do |day|
  HOURS_IN_A_DAY.each do |hour|
    @data[day][hour] = 'something'
  end
end

方法2:

DAYS_IN_A_WEEK = (0..6).to_a
HOURS_IN_A_DAY = (0..23).to_a

@data = {}

DAYS_IN_A_WEEK.each do |day|
  HOURS_IN_A_DAY.each do |hour|
    @data[day] ||= {}
    @data[day][hour] = 'something'
  end
end

第一种方法和第二种方法的区别在于第二种方法最初不分配内存。我觉得第二个在性能方面有点逊色,因为必须发生大量的阵列拷贝。

然而,在Ruby中找不到正在发生的事情并不是直截了当的。所以,如果有人能解释我哪个更好,那真的很棒!

由于

3 个答案:

答案 0 :(得分:3)

在我回答你提出的问题之前,我将回答你应该问的问题但是没有:

  

问:我应该首先专注于使我的代码可读,还是应该首先关注性能?

A:首先使您的代码可以正确,然后 如果出现性能问题,请开始担心性能通过衡量,性能问题优先,只有然后对代码进行更改。

现在回答你提出的问题,但不应该:

method1.rb:

DAYS_IN_A_WEEK = (0..6).to_a
HOURS_IN_A_DAY = (0..23).to_a

10000.times do

  @data = Array.new(DAYS_IN_A_WEEK.size).map!{ Array.new(HOURS_IN_A_DAY.size) }

  DAYS_IN_A_WEEK.each do |day|
    HOURS_IN_A_DAY.each do |hour|
      @data[day][hour] = 'something'
    end
  end

end

method2.rb:

DAYS_IN_A_WEEK = (0..6).to_a
HOURS_IN_A_DAY = (0..23).to_a

10000.times do

  @data = {}

  DAYS_IN_A_WEEK.each do |day|
    HOURS_IN_A_DAY.each do |hour|
      @data[day] ||= {}
      @data[day][hour] = 'something'
    end
  end

end

脑死亡基准的结果:

$ time ruby method1.rb

real    0m1.189s
user    0m1.140s
sys 0m0.000s

$ time ruby method2.rb

real    0m1.879s
user    0m1.780s
sys 0m0.020s

在我看来,用户时间使用(重要因素)使method1.rb快得多。当然,您不应该信任此基准测试,并且应该自己反映您的实际代码使用情况。但是,这是你应该只做确定哪些代码是你现实中的性能瓶颈。 (提示:99.44%的计算机程序员100%错误当他们猜测他们的瓶颈在哪里而没有测量!)

答案 1 :(得分:3)

只是

有什么问题
@data = Array.new(7) { Array.new(24) { 'something' }}

或者,如果您满足于相同的对象:

@data = Array.new(7) { Array.new(24, 'something') }

速度快得多,而不是重要。它还更多可读,这是最重要的事情。毕竟,代码的目的是向其他利益相关者传达意图,与计算机进行通信。

             user   system     total       real
method1  8.969000 0.000000  8.969000 ( 9.059570)
method2 16.547000 0.000000 16.547000 (16.799805)
method3  6.468000 0.000000  6.468000 ( 6.616211)
method4  0.969000 0.015000  0.984000 ( 1.021484)
最后一行也显示了另一个有趣的事情:运行时占主导地位创建7 * 24 * 100000 = 1680万'something'字符串所需的时间。

当然还有另一个重要的观点:你正在比较的method1method2会做两件完全不同的事情!将它们相互比较甚至没有意义。 method1创建Arraymethod2创建Hash

您的method1等同于我上面的第一个示例:

@data = Array.new(7) { Array.new(24) { 'something' }}

虽然method2非常大致)相当于:

@data = Hash.new {|h, k| h[k] = Hash.new {|h, k| h[k] = 'something' }}

好吧,除了你的 method2急切地初始化整个Hash,而我的方法只是在读取未初始化的密钥时懒惰地执行初始化代码。

换句话说,在运行上述初始化代码后,Hash仍为空:

@data # => {}

但无论何时尝试访问密钥,它都会神奇地出现:

@data[5][17] # => 'something'

它会留在那里:

@data # => {5 => {17 => 'something'}}

由于此代码实际上并未初始化Hash,因此显然会更快:

             user   system     total       real
method5  0.266000 0.000000  0.266000 ( 0.296875)

答案 2 :(得分:2)

我将两个代码片段包装成单独的方法并进行了一些基准测试。结果如下:

Benchmark.bm(7) do |x|
  x.report ("method1") { 100000.times { method1 } }
  x.report ("method2") { 100000.times { method2 } }
end

             user     system      total        real
method1 11.370000   0.010000  11.380000 ( 11.392233)
method2 17.920000   0.010000  17.930000 ( 18.328318)