Ruby 中的亚毫秒时间测量



我在 Rubymine 8.0.3 中运行 Ruby 2.2

我的机器运行的是配备英特尔酷睿 i7-4710MQ 的 Windows 7 专业版

我已经能够在这台机器上使用 C++、Java、Python 和 JS 实现 ~411 ns 的精度,但似乎找不到在 Ruby 中实现这种性能的方法,因为内置的时间库仅适用于毫秒。

我可以对测试进行编程以容忍这种降低的精度,但是是否可以合并 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

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 微秒的时钟周期之间的持续时间

您可以使用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 %dn", 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.11sn", 'Time.now', 'Process.clock_gettime')
array.each do |t, p|
  printf("%.9f %.9fn", 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

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 $/

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

从 Ruby 1.9.2 开始,Time 实现使用有符号的 63 位整数、Bignum 或 Rational。整数是自纪元以来的纳秒数,可以表示 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()

相关内容

  • 没有找到相关文章

最新更新