398

准确性对比 精确

我想知道的是在我的游戏中更新对象的位置时是否应该使用System.currentTimeMillis()System.nanoTime() ?他们的运动变化与自上次通话以来经过的时间成正比,我希望尽可能精确。

我读过不同操作系统之间存在一些严重的时间分辨率问题(即 Mac / Linux 的分辨率几乎为 1 毫秒,而 Windows 的分辨率为 50 毫秒??)。我主要在 Windows 上运行我的应用程序,50 毫秒的分辨率似乎非常不准确。

有比我列出的两个更好的选择吗?

有什么建议/意见吗?

4

10 回答 10

336

如果您只是在寻找经过时间的极其精确的测量值,请使用System.nanoTime(). System.currentTimeMillis()将为您提供自纪元以来最准确的可能经过的时间(以毫秒为单位),但System.nanoTime()相对于某个任意点为您提供纳秒精度的时间。

来自 Java 文档:

public static long nanoTime()

返回最精确的可用系统计时器的当前值,以纳秒为单位。

此方法只能用于测量经过的时间,与系统或挂钟时间的任何其他概念无关。返回的值表示自某个固定但任意的原始时间以来的纳秒(可能在将来,因此值可能为负数)。此方法提供纳秒精度,但不一定提供纳秒精度。不保证值的变化频率。 由于数值溢出,超过大约 292 年(2 63纳秒)的连续调用的差异将无法准确计算经过的时间。

例如,要测量一些代码需要多长时间执行:

long startTime = System.nanoTime();    
// ... the code being measured ...    
long estimatedTime = System.nanoTime() - startTime;

另请参阅:JavaDoc System.nanoTime()JavaDoc System.currentTimeMillis()了解更多信息。

于 2008-12-09T02:06:01.230 回答
110

由于没有其他人提到这一点……</p>

System.nanoTime()比较不同 JVM 之间的调用结果是不安全的,每个 JVM 可能有一个独立的“起源”时间。

System.currentTimeMillis()将在 JVM 之间返回(近似)相同的值,因为它与系统挂钟时间相关联。

如果要计算两个事件之间经过的时间量,例如秒表,请使用nanoTime(); 系统挂钟的更改currentTimeMillis()不适合此用例。

于 2012-02-16T16:03:33.330 回答
59

Arkadiy更新:我System.currentTimeMillis()在 Oracle Java 8 中观察到 Windows 7 上更正确的行为。时间以 1 毫秒的精度返回。OpenJDK 中的源代码没有改变,所以我不知道是什么导致了更好的行为。


Sun 的 David Holmes 几年前发表了一篇博客文章,其中非常详细地介绍了 Java 计时 API(特别是System.currentTimeMillis()System.nanoTime()),您何时想要使用它们,以及它们如何在内部工作。

Hotspot VM 内部:时钟、计时器和调度事件 - 第 I 部分 - Windows

Java 在 Windows 上为具有定时等待参数的 API 使用的计时器的一个非常有趣的方面是,计时器的分辨率可以根据可能进行的其他 API 调用而改变 - 系统范围内(不仅仅是在特定进程中) . 他展示了一个示例,其中使用Thread.sleep()将导致此分辨率更改。

于 2008-12-09T06:06:04.880 回答
13

正如其他人所说,currentTimeMillis 是时钟时间,它会因夏令时而改变(不是:夏令时和时区与 currentTimeMillis 无关,其余的都是真的),用户更改时间设置,闰秒和互联网时间同步。如果您的应用程序依赖于单调递增的经过时间值,您可能更喜欢 nanoTime。

您可能认为玩家在玩游戏时不会摆弄时间设置,也许您是对的。但不要低估由于互联网时间同步或远程桌面用户造成的中断。nanoTime API 不受这种破坏的影响。

如果您想使用时钟时间,但要避免由于 Internet 时间同步而导致的不连续性,您可以考虑使用诸如 Meinberg 之类的 NTP 客户端,它可以“调整”时钟频率以将其“调零”,而不仅仅是定期重置时钟。

我从个人经历说。在我开发的一个天气应用程序中,我得到了随机出现的风速峰值。我花了一段时间才意识到我的时基被典型 PC 上时钟时间的行为所打乱。当我开始使用 nanoTime 时,我所有的问题都消失了。一致性(单调性)对我的应用程序来说比原始精度或绝对精度更重要。

于 2013-09-25T00:28:56.627 回答
11

System.nanoTime()较旧的 JVM 不支持。如果这是一个问题,请坚持currentTimeMillis

关于准确性,您几乎是正确的。在某些 Windows 机器currentTimeMillis()上,分辨率约为 10 毫秒(不是 50 毫秒)。我不知道为什么,但有些 Windows 机器和 Linux 机器一样准确。

我过去使用过GAGETimer并取得了一定的成功。

于 2008-12-09T02:06:21.557 回答
5

是的,如果需要这种精度,请使用System.nanoTime(),但请注意,您需要 Java 5+ JVM。

在我的 XP 系统上,我看到使用以下代码报告的系统时间至少为100 微秒278纳秒:

private void test() {
    System.out.println("currentTimeMillis: "+System.currentTimeMillis());
    System.out.println("nanoTime         : "+System.nanoTime());
    System.out.println();

    testNano(false);                                                            // to sync with currentTimeMillis() timer tick
    for(int xa=0; xa<10; xa++) {
        testNano(true);
        }
    }

private void testNano(boolean shw) {
    long strMS=System.currentTimeMillis();
    long strNS=System.nanoTime();
    long curMS;
    while((curMS=System.currentTimeMillis()) == strMS) {
        if(shw) { System.out.println("Nano: "+(System.nanoTime()-strNS)); }
        }
    if(shw) { System.out.println("Nano: "+(System.nanoTime()-strNS)+", Milli: "+(curMS-strMS)); }
    }
于 2008-12-09T02:24:09.150 回答
4

对于游戏图形和平滑位置更新,请使用System.nanoTime()而不是System.currentTimeMillis(). 我在游戏中从 currentTimeMillis() 切换到 nanoTime(),并在运动平滑度方面获得了重大的视觉改进。

虽然一毫秒似乎应该已经很精确,但从视觉上看却并非如此。nanoTime()可以改善的因素包括:

  • 低于挂钟分辨率的准确像素定位
  • 如果需要,可以在像素之间消除锯齿
  • Windows 挂钟不准确
  • 时钟抖动(挂钟实际向前滴答的时间不一致)

正如其他答案所暗示的那样,如果重复调用 nanoTime 确实会产生性能成本 - 最好每帧只调用一次,并使用相同的值来计算整个帧。

于 2017-08-17T02:29:35.987 回答
2

System.currentTimeMillis()对于经过的时间是不安全的,因为这种方法对系统的系统实时时钟变化很敏感。你应该使用System.nanoTime. 请参考 Java 系统帮助:

关于 nanoTime 方法:

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

如果您使用System.currentTimeMillis()经过的时间可以是负数(返回 <-- 到未来)

于 2017-11-28T15:48:15.560 回答
1

我对nanotime有很好的体验。它使用 JNI 库提供两个 long 的挂钟时间(自纪元以来的秒数和该秒内的纳秒)。它适用于为 Windows 和 Linux 预编译的 JNI 部分。

于 2011-04-21T11:01:25.817 回答
-3

这里的一件事是 nanoTime 方法的不一致。它没有为相同的输入提供非常一致的值。currentTimeMillis 在性能和一致性方面做得更好,而且,虽然不如 nanoTime 精确,但误差范围较小,因此其值更准确。因此,我建议您使用 currentTimeMillis

于 2012-04-24T04:52:10.777 回答