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

Posted

技术标签:

【中文标题】如何在 Ruby 中以毫秒为单位计时?【英文标题】:How to time an operation in milliseconds in Ruby? 【发布时间】:2011-01-18 08:44:11 【问题描述】:

我想知道一个特定函数使用了多少毫秒。所以我看高和低,但找不到以毫秒精度在 Ruby 中获取时间的方法。

你是怎么做到的?在大多数编程语言中,它就像

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

【问题讨论】:

【参考方案1】:

使用 Time.now.to_f

【讨论】:

【参考方案2】:

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

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

注意Time.now.to_f 的使用,它与 to_i 不同,不会截断为秒。

【讨论】:

这里没有必要将时间转换为浮点数。 另外,“endd”中的错字 @nightpool 之所以这样拼写是因为end 是关键字。 然后像end_time 之类的,不要只是拼错单词,那太马虎了【参考方案3】:

您可以使用 ruby​​ 的 Time 类。例如:

t1 = Time.now
# processing...
t2 = Time.now
delta = t2 - t1 # in seconds

现在,delta 是一个 float 对象,您可以获得类提供的最细粒度的结果。

【讨论】:

这是一个简单的解决方案,但使用 Process.clock_gettime 的答案更好。见blog.dnsimple.com/2018/03/elapsed-time-with-ruby-the-right-way 更好的是 Benchmark.measure 的答案。它的实现使用单调时钟。【参考方案4】:

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

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

打印:

0.000000   0.000000   0.000000 (  0.501134)

【讨论】:

我喜欢使用内置库的想法,但前三个数字是什么(0.000000)? 更新:查找in the docs,它是用户 CPU 时间、系统 CPU 时间以及这两者的总和。 是的。如果您使用Benchmark.bm,它们会被标记。有时查看时间花在哪里很有用。在这里您可以看到该块只是空闲(总共 0,实际 0.5) 或者有一个 Benchmark.realtime 返回一个可以理解的浮点数:)【参考方案5】:

absolute_time gem 是 Benchmark 的直接替代品,但使用原生指令更加准确。

【讨论】:

或hitimes gem,它在windows上的粒度可能比absolute_time ...【参考方案6】:

Time.now.to_i 的使用返回从 1970/01/01 传递的第二个。知道这一点你就可以做到

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

有了这个,你会有数量级的差异。如果您希望在 毫秒 内完成,只需添加到代码中

diff = diff * 1000

【讨论】:

.to_i 给出一个整数并截断毫秒。如果您将其更改为.to_f,这将起作用 是的,正如瑞恩所说;为反对票道歉,但这实际上有一个赞成票,我觉得有必要抵消它。以秒为单位的 integer 时间差乘以 1000 得到毫秒?现在来吧,MARC.RS,你肯定只是在捉弄我们! :-) 不,只是尽力而为【参考方案7】:

如果你使用

date = Time.now.to_i

您以秒为单位获得时间,这远非准确,特别是在您为一小段代码计时时。

【讨论】:

【参考方案8】:

我有一个可以分析您的 ruby​​ 方法(实例或类)的 gem - https://github.com/igorkasyanchuk/benchmark_methods。

不再有这样的代码:

t = Time.now
user.calculate_report
puts Time.now - t

现在你可以这样做了:

benchmark :calculate_report # in class

然后调用你的方法

user.calculate_report

【讨论】:

【参考方案9】:

使用Time.now(返回挂钟时间)作为基线有几个问题,可能会导致意外行为。这是因为挂钟时间会发生变化,例如插入闰秒或time slewing 以将本地时间调整为参考时间。

如果有例如在测量过程中插入闰秒,它将关闭一秒。同样,根据本地系统条件,您可能必须处理夏令时、运行速度更快或更慢的时钟,甚至时钟甚至会及时跳回,从而导致负持续时间以及许多其他问题。

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

在 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 方法将时间戳作为带有小数秒的浮点数返回。返回的实际数字没有定义的含义(您应该依赖它)。但是,您可以确定下一次调用将返回一个更大的数字,并且通过比较这些值,您可以获得实时差。

这些属性使该方法成为测量时差的主要候选者,而不会在最不合适的时间(例如,在新年前夜的午夜插入另一个闰秒时)看到您的程序失败。

此处使用的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

【讨论】:

这是唯一应该被接受的答案,更不用说被接受了。 也许值得添加一些关于操作系统支持的细节:SUSv3 到 4、Linux 2.5.63、FreeBSD 3.0、NetBSD 2.0、OpenBSD 3.4、macOS 10.12 原始问题和答案均在此解决方案可用之前提供。为什么这应该是唯一“可接受”的答案?【参考方案10】:

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

def log_time
  start_at = Time.now

  yield if block_given?

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

log_time  sleep(2.545)  # Execution time: 2.55s

【讨论】:

以上是关于如何在 Ruby 中以毫秒为单位计时?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 PHP 中以毫秒为单位获取当前时间?

如何在 Mysql 中以 unix 毫秒为单位存储日期?

如何以毫秒为单位显示 ssms 的执行时间?

为啥 setDate() 在 nodejs 中以毫秒为单位给出最终日期? [复制]

在 Windows 和 Linux 中以毫秒为单位的随机种子

Python中的时间(以毫秒为单位)