如何在Ruby中以毫秒为单位计时操作?


78

我想弄清楚一个特定函数使用了多少毫秒。因此,我看上去高低不一,但找不到找到以毫秒为单位精度的Ruby时间的方法。

你怎么做到这一点?在大多数编程语言中,它就像

start = now.milliseconds
myfunction()
end = now.milliseconds
time = end - start

Answers:



60

您还可以使用内置的Benchmark.measure函数:

require "benchmark"
puts(Benchmark.measure { sleep 0.5 })

印刷品:

0.000000   0.000000   0.000000 (  0.501134)

1
我喜欢使用内置库的想法,但是前三个数字(0.000000)是什么?更新:在文档中查找它,它是用户CPU时间,系统CPU时间以及这两者的总和。
lyonsinbeta 2012年

1
是。如果使用,它们会被标记Benchmark.bm。有时会花时间在哪里很有用。在这里,您可以看到该区块正处于空转状态(总计0,真实值0.5)
Simon Perepelitsa 2012年

13
或者有一个Benchmark.realtime返回单个可理解的浮点数的:)
塞尔吉奥·图伦采夫2012年

26

使用Time.now(返回挂钟时间)作为基准线有两个问题,这些问题可能导致意外行为。这是由以下事实时钟时间是受像插入闰秒或变化而引起时间回转来调整本地时间与参考时间。

如果在测量过程中插入了例如a秒,它将be秒。同样,根据本地系统条件,您可能必须处理夏时制,运行时钟的速度更快或更慢,或者时钟甚至跳回到时间上,导致持续时间为负数,以及许多其他问题。

解决此问题的方法是使用不同的时钟时间:单调时钟。这种类型的时钟与挂钟具有不同的属性。它单调递增,即永不回退并以恒定速率增加。这样一来,它就不代表墙上的时钟(即您从墙上的时钟读取的时间),而是可以与以后的时间戳进行比较以获得不同的时间戳。

在Ruby中,您可以使用Process.clock_gettime(Process::CLOCK_MONOTONIC)如下所示的时间戳:

t1 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
# => 63988.576809828

sleep 1.5 # do some work

t2 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
# => 63990.08359163

delta = t2 - t1
# => 1.5067818019961123
delta_in_milliseconds = delta * 1000
# => 1506.7818019961123

Process.clock_gettime方法以小数秒的浮点数形式返回时间戳。返回的实际数字没有定义的含义(您应该依靠)。但是,您可以确保下一个调用将返回更大的数字,并且通过比较这些值,您可以获得实时差。

这些属性使该方法成为测量时差的主要候选方法,而不会在最小的机会内看到程序失败(例如,在除夕夜午夜插入另一个leap秒)。

Process::CLOCK_MONOTONIC此处使用的常数在所有现代Linux,BSD和macOS系统以及Windows的Linux子系统上均可用。但是,它尚不适用于“原始” Windows系统。在此,您可以使用GetTickCount64系统调用,而不是Process.clock_gettime在Windows(> = Windows Vista,Windows Server 2008)上以毫秒为单位返回计时器值。

使用Ruby,您可以这样调用此函数:

require 'fiddle'

# Get a reference to the function once
GetTickCount64 = Fiddle::Function.new(
  Fiddle.dlopen('kernel32.dll')['GetTickCount64'],
  [],
  -Fiddle::TYPE_LONG_LONG # unsigned long long
)

timestamp = GetTickCount64.call / 1000.0
# => 63988.576809828

1
这是唯一可以接受的答案,更不用说被接受了。
Rob Kinyon

也许值得添加一些有关OS支持的详细信息:SUSv3至4,Linux 2.5.63,FreeBSD 3.0,NetBSD 2.0,OpenBSD 3.4,macOS 10.12
Guy C

在此解决方案可用之前,都提供了原始问题和答案。为什么这应该是唯一的“可接受”答案?
CitizenX

13

您应该查看基准测试模块以执行基准测试。但是,作为一种快速而肮脏的计时方法,您可以使用以下方法:

def time
  now = Time.now.to_f
  yield
  endd = Time.now.to_f
  endd - now
end

请注意Time.now.to_f,与to_i不同,不会将其截断为秒。


6
此处不需要将时间投放到浮动。
塞尔吉奥·图伦采夫2012年

另外,“结束”中的错字
nightpool '18

@nightpool之所以这样拼写是因为它end是一个关键字。
sepp2k

1
然后喜欢end_time或做某事,不要只是拼错单词,那简直就是地狱
nightpool '18


1

absolute_time宝石是一个下拉更换为基准,但使用原生指令要准确得多。


1
或Hitimes宝石,其在Windows上的粒度可能比absolute_time ...
rogerdpack 2014年

1

我们还可以创建简单的函数来记录任何代码块:

def log_time
  start_at = Time.now

  yield if block_given?

  execution_time = (Time.now - start_at).round(2)
  puts "Execution time: #{execution_time}s"
end

log_time { sleep(2.545) } # Execution time: 2.55s

0

如果您使用

date = Time.now.to_i

您正在获得以秒为单位的时间,这远非准确的时间,特别是在计时很少的代码块时。


0

使用Time.now.to_ireturn从1970/01/01开始传递的第二个。知道这一点你可以做

date1 = Time.now.to_f
date2 = Time.now.to_f
diff = date2 - date1

这样,您将在第二幅度上有所不同。如果需要毫秒,只需将其添加到代码中

diff = diff * 1000

2
.to_i给出一个整数,并将截断毫秒。如果您将其更改为.to_f
Ryan Taylor

1
是的,正如瑞安所说;对下降票表示歉意,但实际上只有一次上升票,我感到必须予以抵消。将以秒为单位的整数时间差乘以1000得到毫秒?快点来,MARC.RS,您一定是在拖钓我们!:-)
Andrew Hodgkinson 2015年

不,只是尽我所能
MARC.RS 2015年

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.