How to time an operation in milliseconds in Ruby?

2020-05-15 07:57发布

I'm wishing to figure out how many milliseconds a particular function uses. So I looked high and low, but could not find a way to get the time in Ruby with millisecond precision.

How do you do this? In most programming languages its just something like

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

标签: ruby timer
10条回答
▲ chillily
2楼-- · 2020-05-15 08:37

I've a gem which can profile your ruby method (instance or class) - https://github.com/igorkasyanchuk/benchmark_methods.

No more code like this:

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

Now you can do:

benchmark :calculate_report # in class

And just call your method

user.calculate_report
查看更多
老娘就宠你
3楼-- · 2020-05-15 08:39

You should take a look at the benchmark module to perform benchmarks. However, as a quick and dirty timing method you can use something like this:

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

Note the use of Time.now.to_f, which unlike to_i, won't truncate to seconds.

查看更多
我欲成王,谁敢阻挡
4楼-- · 2020-05-15 08:40

The use of Time.now.to_i return the second passed from 1970/01/01. Knowing this you can do

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

With this you will have difference in second magnitude. If you want it in milliseconds, just add to the code

diff = diff * 1000
查看更多
劫难
5楼-- · 2020-05-15 08:43

Using Time.now (which returns the wall-clock time) as base-lines has a couple of issues which can result in unexpected behavior. This is caused by the fact that the wallclock time is subject to changes like inserted leap-seconds or time slewing to adjust the local time to a reference time.

If there is e.g. a leap second inserted during measurement, it will be off by a second. Similarly, depending on local system conditions, you might have to deal with daylight-saving-times, quicker or slower running clocks, or the clock even jumping back in time, resulting in a negative duration, and many other issues.

A solution to this issue is to use a different time of clock: a monotonic clock. This type of clock has different properties than the wall clock. It increments monitonically, i.e. never goes back and increases at a constant rate. With that, it does not represent the wall-clock (i.e. the time you read from a clock on your wall) but a timestamp you can compare with a later timestamp to get a difference.

In Ruby, you can use such a timestamp with Process.clock_gettime(Process::CLOCK_MONOTONIC) like follows:

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

The Process.clock_gettime method returns a timestamp as a float with fractional seconds. The actual number returned has no defined meaning (that you should rely on). However, you can be sure that the next call will return a larger number and by comparing the values, you can get the real time difference.

These attributes make the method a prime candidate for measuring time differences without seeing your program fail in the least opportune times (e.g. at midnight at New Year's Eve when there is another leap-second inserted).

The Process::CLOCK_MONOTONIC constant used here is available on all modern Linux, BSD, and macOS systems as well as the Linux Subsystem for Windows. It is however not yet available for "raw" Windows systems. There, you can use the GetTickCount64 system call instead of Process.clock_gettime which also returns a timer value in millisecond granularity on Windows (>= Windows Vista, Windows Server 2008).

With Ruby, you can call this function like this:

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
查看更多
登录 后发表回答