Ruby中的亚毫秒时间测量

时间:2016-03-04 08:06:56

标签: ruby time benchmarking timing metrics

我在Rubymine 8.0.3中运行Ruby 2.2

我的机器运行的是带有Intel内核i7-4710MQ的Windows 7 Pro

我已经能够在这台机器上用C ++,Java,Python和JS达到~411 ns的精度,但似乎无法找到在Ruby中获得这种性能的方法,因为内置的时间库是好的仅适用于ms。

我可以对我的测试进行编程以容忍这种降低的精度,但是是否可以结合使用Windows QPC API来改进执行时间的评估?

我确定时钟滴答精度的测试代码如下:

numTimes = 10000
times = Array.new(numTimes)

(0...(numTimes)).each do |i|
  times[i] = Time.new
end

durations = []
(0...(numTimes - 1)).each do |i|
  durations[i] = times[i+1] - times[i]
end

# Output duration only if the clock ticked over    
durations.each do |duration|
  if duration != 0
    p duration.to_s + ','
  end
end

以下代码将QPC合并为here

require "Win32API"

QueryPerformanceCounter = Win32API.new("kernel32",
                                       "QueryPerformanceCounter", 'P', 'I')
QueryPerformanceFrequency = Win32API.new("kernel32",
                                         "QueryPerformanceFrequency", 'P', 'I')

def get_ticks
  tick = ' ' * 8
  get_ticks = QueryPerformanceCounter.call(tick)
  tick.unpack('q')[0]
end

def get_freq
  freq = ' ' * 8
  get_freq =  QueryPerformanceFrequency.call(freq)
  freq.unpack('q')[0]
end

def get_time_diff(a, b)
  # This function takes two QPC ticks
  (b - a).abs.to_f / (get_freq)
end

numTimes = 10000
times = Array.new(numTimes)

(0...(numTimes)).each do |i|
  times[i] = get_ticks
end

durations = []
(0...(numTimes - 1)).each do |i|
  durations[i] = get_time_diff(times[i+1], times[i])
end

durations.each do |duration|
    p (duration * 1000000000).to_s + ','
end

此代码返回机器上约22-75微秒的刻度之间的持续时间

3 个答案:

答案 0 :(得分:2)

使用Process::clock_gettime

可以获得更高的精确度
  

返回POSIX clock_gettime()函数返回的时间。

以下是Time.now

的示例
times = Array.new(1000) { Time.now }
durations = times.each_cons(2).map { |a, b| b - a }

durations.sort.group_by(&:itself).each do |time, elements|
  printf("%5d ns x %d\n", time * 1_000_000_000, elements.count)
end

输出:

    0 ns x 686
 1000 ns x 296
 2000 ns x 12
 3000 ns x 2
12000 ns x 2
18000 ns x 1

这是与Process.clock_gettime相同的例子:

times = Array.new(1000) { Process.clock_gettime(Process::CLOCK_MONOTONIC) }

输出:

  163 ns x 1
  164 ns x 1
  164 ns x 9
  165 ns x 6
  165 ns x 22
  166 ns x 39
  166 ns x 174
  167 ns x 13
  167 ns x 129
  168 ns x 95
  168 ns x 32
  169 ns x 203
  169 ns x 141
  170 ns x 23
  170 ns x 37
  171 ns x 30
  171 ns x 3
  172 ns x 24
  172 ns x 10
  174 ns x 1
  175 ns x 2
  180 ns x 1
  194 ns x 1
  273 ns x 1
 2565 ns x 1

这是一个快速的并排比较:

array = Array.new(12) { [Time.now, Process.clock_gettime(Process::CLOCK_MONOTONIC)] }

array.shift(2)                # first elements are always inaccuate
base_t, base_p = array.first  # baseline

printf("%-11.11s %-11.11s\n", 'Time.now', 'Process.clock_gettime')
array.each do |t, p|
  printf("%.9f %.9f\n", t - base_t, p - base_p)
end

输出:

Time.now    Process.clo
0.000000000 0.000000000
0.000000000 0.000000495
0.000001000 0.000000985
0.000001000 0.000001472
0.000002000 0.000001960
0.000002000 0.000002448
0.000003000 0.000002937
0.000003000 0.000003425
0.000004000 0.000003914
0.000004000 0.000004403

这是运行在Intel Core i7上的OS X上的Ruby 2.3,不确定Windows。

为避免浮点转换导致的精度损失,您可以指定其他单位,例如:

Process.clock_gettime(Process::CLOCK_MONOTONIC, :nanosecond)
#=> 191519383463873

答案 1 :(得分:1)

Time#nsec

numTimes = 10000
times = Array.new(numTimes)

(0...(numTimes)).each do |i|
  # nsec              ⇓⇓⇓⇓
  times[i] = Time.new.nsec
end

durations = (0...(numTimes - 1)).inject([]) do |memo, i|
  memo << times[i+1] - times[i]
end

puts durations.reject(&:zero?).join $/

答案 2 :(得分:1)

Ruby Time objects存储自纪元以来的纳秒数。

  

从Ruby 1.9.2开始,Time实现使用带符号的63位整数,Bignum或Rational。整数是自Epoch以来的纳秒数,可以代表1823-11-12到2116-02-20。

您可以使用Time#nsec最准确地访问纳秒部分。

$ ruby -e 't1 = Time.now; puts t1.to_f; puts t1.nsec'
1457079791.351686
351686000

正如您所看到的,在我的OS X机器上,它只能精确到微秒。这可能是因为OS X缺少clock_gettime()