在 Ruby 中测量时间的惯用方法是什么?

What is an idiomatic way to measure time in Ruby?

这很丑陋:

t = Time.now
result = do_something
elapsed = Time.now - t

我试过这个:

elapsed = time do
  result = do_something
end

def time
  t = Time.now
  yield
  Time.now - t
end

这样更好。但问题是 result 在块结束后超出范围。

那么,有没有更好的计时方法?或者使用result的好方法?

你的想法是正确的,但为了避免范围问题,请这样做:

result = nil
elapsed = time do
  result = do_something
end

一个真正惯用的方法是使用标准库。 :)

require 'benchmark'

result = nil
elapsed = Benchmark.realtime do 
  result = do_something
end

我喜欢你构建 time 方法的方式。我没有改进的建议,但我会就一个相关问题说几句话。假设您希望测量执行方法所花费的时间。有时您可能可以写一些简单的东西,例如:

require 'time'

t = Time.now
rv = my_method(*args)
et = t.Time.now - t

其他时候不方便。例如,假设您正在构造一个数组,其元素是 my_method 的 return 值或 my_method return 枚举器,以便它可以链接到其他方法。

例如,假设您想要对数组的值求和,直到遇到零为止。一种方法是构造一个枚举器 stop_at_zero,它从其接收器生成值,直到遇到零,然后停止(即引发 StopIteration 异常)。然后我们可以写:

arr.stop_at_zero.reduce(:+)

如果我们想知道执行 stop_at_zero 花费了多少时间,我们可以按如下方式构造它。

class Array
  def stop_at_zero
    extime = Time.now
    Enumerator.new do |y|
      begin
        each do |n|
          sleep(0.5)
          return y if n.zero?
          y << n
        end
      ensure
        $timings << [__method__, Time.now - extime]
      end
    end
  end
end

我使用了 beginensureend 块来确保 $timings << [__method__, Time.now - extime] 在方法 return 过早执行时执行。 sleep(0.5)当然只是为了说明目的。

我们来试试吧。

$timings = []
arr = [1,7,0,3,4]
arr.stop_at_zero.reduce(:+)
  #=> 8 
$timings
  #=> [[:stop_at_zero, 1.505672]] 

$timings 将包含包含计时代码的所有方法的执行时间的历史记录。