【问题标题】:Precision vs. accuracy of System.nanoTime()System.nanoTime() 的精度与精度
【发布时间】:2012-07-12 13:11:43
【问题描述】:

System.nanoTime() 的文档说明如下(强调我的)。

此方法只能用于测量经过的时间,与系统或挂钟时间的任何其他概念无关。返回的值表示自某个固定但任意时间以来的纳秒(可能在将来,因此值可能为负数)。 此方法提供纳秒级精度,但不一定提供纳秒级精度。不保证值的变化频率。

在我看来,这可以用两种不同的方式来解释:

  1. 上面粗体中的句子是指单独的返回值。然后,精度和准确度要从数字的意义上来理解。即精度是指有效数字的个数——截断的位置,而精度是指数字是否正确(如这里的置顶答案中描述的) What is the difference between 'precision' and 'accuracy'?)

  2. 上面粗体中的句子是指方法本身的能力。然后,精度和准确性将被理解为飞镖类比所说明的 (http://en.wikipedia.org/wiki/Precision_vs._accuracy#Accuracy_versus_precision:_the_target_analogy)。 因此,低精度,高精度 => 错误的值被重复击中:假设物理时间静止,连续调用 nanoTime() 返回相同的数值,但它与实际经过的时间不同,因为参考时间由一些常数偏移量。

哪种解释是正确的?我的观点是,解释 2 意味着使用 nanoTime() (通过减去两个返回值)测量时间 差异 将是正确的纳秒(因为测量中的恒定误差/偏移量将是消除),而解释 1 不能保证测量之间的那种一致性,因此不一定意味着时差测量的高精度。


2013 年 4 月 15 日更新:System.nanoTime() 的 Java 7 文档已更新,以解决可能与之前的措辞混淆的问题。

返回正在运行的 Java 虚拟机的高分辨率时间源的当前值,以纳秒为单位。

此方法只能用于测量经过的时间,与系统或挂钟时间的任何其他概念无关。返回的值表示自某个固定但任意的 origin 时间以来的纳秒(可能在将来,因此值可能为负数)。在 Java 虚拟机实例中,此方法的所有调用都使用相同的来源;其他虚拟机实例可能使用不同的来源。

此方法提供纳秒精度,但不一定提供纳秒分辨率(即值更改的频率) - 不保证分辨率至少与 currentTimeMillis() 一样好。

由于数值溢出,超过大约 292 年(263 纳秒)的连续调用的差异将无法正确计算经过的时间。

只有在计算同一 Java 虚拟机实例中获得的两个此类值之间的差异时,此方法返回的值才有意义。

【问题讨论】:

  • 据我所知,该功能取决于操作系统返回纳秒的可能性。所以,假设你得到了x = nanoTime();,这会给你带来一些价值,让我们假设958145。如果您在一纳秒后调用nanoTime(),则不能保证您会得到958146。这意味着它返回纳秒,但不一定是两次调用之间经过的正确纳秒量。
  • 哇——对一个重要的有趣话题提出了一个措辞得体的问题!
  • 292 年很高兴知道我是否为长期太空任务编写了软件。

标签: java nanotime


【解决方案1】:

在 Clojure 命令行中,我得到:

user=> (- (System/nanoTime) (System/nanoTime))
0
user=> (- (System/nanoTime) (System/nanoTime))
0
user=> (- (System/nanoTime) (System/nanoTime))
-641
user=> (- (System/nanoTime) (System/nanoTime))
0
user=> (- (System/nanoTime) (System/nanoTime))
-642
user=> (- (System/nanoTime) (System/nanoTime))
-641
user=> (- (System/nanoTime) (System/nanoTime))
-641

所以本质上,nanoTime 不会每纳秒更新一次,这与人们对其精度 的直觉预期相反。在 Windows 系统中,它在后台使用 QueryPerformanceCounter API(根据 this article),实际上它似乎提供了大约 640 ns 分辨率(在我的系统中!)。

请注意,nanoTime 本身根本没有任何准确性,因为它的绝对值是任意的。只有连续的nanoTime 调用之间的差异 才有意义。这种差异的(不)准确度大约为 1 微秒。

【讨论】:

  • 很好的答案,这是真的。我认为这里的重要部分是 Java 人员意识到操作系统“时钟”时间(来自芯片和操作系统缓存)存在各种问题,从漂移到准确性和延迟(在各种系统上仅每 +/- 30 毫秒更新一次)。所以这个“nanoTime”实现真的很不同——不是使用时钟芯片,而是一个“高性能定时器”实现,它在操作系统级别有所不同,但结果是相同的——定时器的分辨率比旧的 +/- 精确得多30 毫秒缓存/漂移。
【解决方案2】:

第一种解释是正确的。在大多数系统上,三个最低有效数字将始终为零。这实际上提供了微秒级的精度,但以固定的纳秒级精度报告。

事实上,现在我再看一遍,你的第二种解释也是对正在发生的事情的有效描述,也许更是如此。想象冻结时间,报告将始终是相同的错误纳秒数,但如果理解为整数微秒,则正确。

【讨论】:

  • 所以我应该只相信使用 nanoTime() 精确到微秒的时差测量。感谢两位回答者!
  • 正确 - 只有两个值之间的 ABS() 差异才会产生任何有意义的东西,正如高性能计时器的某些操作系统实现中所提供的那样。所以这个函数不是关于“现在几点”而是关于计时器,因此比较两个值就是它的用途(但确实非常有用)。
【解决方案3】:

System.currentTimeMillis()System.nanoTime() 之间区别的一个非常有趣的特点是 System.nanoTime() 不会随挂钟而改变。我在具有大量时间漂移的 Windows 虚拟机上运行代码。 System.currentTimeMillis() 每次可以向后或向前跳跃 1-2 秒,因为 NTP 会纠正这种漂移,从而使准确的时间戳变得毫无意义。 (Windows 2003、2008 VPS 版本)

但是,System.nanoTime() 不受更改挂钟时间的影响,因此您可以通过 NTP 检索时间并根据System.nanoTime() 应用更正,因为上次检查 NTP 并且您的时间比 @ 准确得多987654327@ 在不利的挂钟条件下

这当然是违反直觉的,但了解一下很有用

【讨论】:

  • 这或许可以解释为什么在 Windows 7 下运行 java 测试时,Java 使用 System.getNanoTime() 报告时差为 350777 ns(等于 0.35 毫秒),但时差为 @987654330 @在同一时间段内使用System.currentTimeMillis()。看起来使用 System.currentTimeMillis() 对于基准测试可能完全不准确。
【解决方案4】:

如果像我这样的人来一次又一次地阅读这个问题 一次又一次 仍然有点理解它,这里有一个更简单(我希望)的解释。

Precision 是关于您保留多少位数字。每个:

long start = System.nanoTime();
long end   = System.nanoTime();

将是一个精确的数字(很多位数)。

由于accuracy与某物相比进行测量,因此单独调用System.nanoTime 毫无意义,因为它的值非常随意,不依赖于我们可以测量的东西。区分其准确性的唯一方法是对其进行两次不同的调用,因此:

 long howMuch = end - start;

不会达到纳秒级的精度。事实上,在我的机器上,差异是 0.2 - 0.3 微秒。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-08-04
    • 2022-12-18
    • 1970-01-01
    • 2020-09-29
    相关资源
    最近更新 更多