测量和基准测试Ruby方法的时间

127

如何在Ruby中测量方法所需的时间以及该方法中各个语句的执行时间。 如果您查看下面的方法,我想要测量该方法所需的总时间以及对数据库和Redis进行访问所需的时间。 我不想在每个语句之前编写Benchmark.measure。 Ruby解释器是否提供了用于执行此操作的任何挂钩?

def foo
# code to access database
# code to access redis. 
end

Ruby有类似于JavaScript的new Date()的东西,但我记不清正确的语法了。不过,你可以在谷歌上找到相关的信息。 - reagan
2
@Phani 你能选择一个正确答案吗?8年过去了,我认为这里有一些可靠的答案。谢谢。 - Joshua Pinter
6个回答

153

最简单的方法:

require 'benchmark'

def foo
 time = Benchmark.measure {
  code to test
 }
 puts time.real #or save it to logs
end

样例输出:

2.2.3 :001 > foo
  5.230000   0.020000   5.250000 (  5.274806)

值包括:CPU时间、系统时间、总计和真实经过的时间。

来源:Ruby文档


55
如果您只想获取实时时间,可以使用 Benchmark.realtime { block } - jmccure

137
你可以使用 Time 对象。(时间文档)例如,
start = Time.now
# => 2022-02-07 13:55:06.82975 +0100
# code to time
finish = Time.now
# => 2022-02-07 13:55:09.163182 +0100
diff = finish - start
# => 2.333432

diff 将会作为浮点数以秒为单位显示。


16
只需要进行微小的更正。end是保留字,所以请使用其他变量名。 - Jason Kim
7
Time.now 的值会受到系统时钟调整的影响,因此最好使用 Process.clock_gettime(Process::CLOCK_MONOTONIC)。但是对于粗略计算来说,这并不重要。https://blog.dnsimple.com/2018/03/elapsed-time-with-ruby-the-right-way/ - Patrick Brinich-Langlois

60

使用Benchmark的报告

require 'benchmark' # Might be necessary.

def foo
  Benchmark.bm( 20 ) do |bm|  # The 20 is the width of the first column in the output.
    bm.report( "Access Database:" ) do 
      # Code to access database.
    end
   
    bm.report( "Access Redis:" ) do
      # Code to access redis.
    end
  end
end

这将输出类似以下内容:

                        user     system      total        real
Access Database:    0.020000   0.000000   0.020000 (  0.475375)
Access Redis:       0.000000   0.000000   0.000000 (  0.000037)

<------ 20 -------> # This is where the 20 comes in. NOTE: This is not shown in output.

更多信息可以在这里找到。


4
我刚刚回到自己的答案并对Benchmark处理方式再次印象深刻。喜爱Ruby。 - Joshua Pinter
3
这应该是首选答案:因为从Ruby 2.2开始,Benchmark类使用单调时钟,正如其他答案中所讨论的那样。请参阅以下源代码,并查找第286行的“def measure”:https://github.com/ruby/ruby/blob/ruby_2_2/lib/benchmark.rb - Purplejacket

34

许多答案建议使用Time.now。但值得注意的是,Time.now可能会改变。系统时钟可能会漂移,并可能通过系统管理员或通过NTP进行校正。因此,Time.now可能向前或向后跳跃,从而导致基准测试结果不准确。

更好的解决方案是使用操作系统的单调时钟,它始终向前移动。Ruby 2.1及以上版本通过以下方式提供对其的访问:

start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
# code to time
finish = Process.clock_gettime(Process::CLOCK_MONOTONIC)
diff = finish - start # gets time is seconds as a float

你可以在这里阅读更多详细信息。另外,你可以看到流行的Ruby项目Sidekiq已经转换到使用单调时钟


1
除了秒(毫秒、微秒、纳秒等)之外,还有其他单位可用,请参见核心文档 - bbenno

10
第二个想法是,使用 Ruby 代码块参数定义 measure() 函数可以帮助简化时间测量代码:
def measure(&block)
  start = Time.now
  block.call
  Time.now - start
end

# t1 and t2 is the executing time for the code blocks.
t1 = measure { sleep(1) }

t2 = measure do
  sleep(2)
end

在您的定义中,您称其为“基准测试”。但是,在使用时它应该被称作“测量”,请修正。 - Sandro L

2
根据wquist的回答精神,但更简单一些,你也可以按照以下方式操作:
start = Time.now
# code to time
Time.now - start

这个答案是回答问题的一个(稍微)不同的方式。仅仅因为你可以从@wquist的答案中找到答案并不意味着它无效。 - thesecretmaster

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接