Java计时器世界的现状是什么?

时间:2014-07-11 15:13:31

标签: java performance time

我不时会遇到提及System.nanoTime()System.currentTimeMillis()要慢得多(电话费用可能高达微秒),但是证据链接经常过时,或导致一些相当自以为是的博客帖子不能真正信任,或包含与特定平台,或者这个或那个等有关的信息。

我没有运行基准测试,因为我对我在这样一个敏感问题上进行实验的能力是切合实际的,但我的条件确实很明确,所以我期待一个简单的答案。

因此,在平均64位Linux(暗示64位JRE),Java 8和现代硬件上,切换到nanoTime()会花费我微秒才能调用?我应该留在currentTimeMillis()吗?

5 个答案:

答案 0 :(得分:14)

与往常一样,这取决于您使用它的目的。由于其他人正在抨击nanoTime,我会为它插上插件。我专门使用nanoTime来衡量生产代码中的已用时间

我在生产中回避currentTimeMillis,因为我通常需要一个不会像挂钟那样向后和向前跳跃的时钟(并且确实如此)。这在我使用重要的基于计时器的决策的系统中至关重要。 nanoTime应按照您期望的速度单调增加。

事实上,我的一位同事说“currentTimeMillis仅对人类娱乐有用”(例如调试日志中的时间,或显示在网站上)因为无法信任它来衡量已过去时间。

但实际上,我们尝试尽可能多地使用时间,并试图将时间排除在我们的协议之外;然后我们尝试使用逻辑时钟;最后,如果绝对必要,我们会使用基于nanoTime的持续时间。

更新:在连接两台主机时,我们使用currentTimeMillis作为完整性检查的一个位置,但我们正在检查主机的时钟相隔超过5分钟。

答案 1 :(得分:11)

如果您目前正在使用currentTimeMillis()并且对解决方案感到满意,那么您绝对不应该改变。

根据javadoc:

  

此方法提供纳秒精度,但不一定     纳秒分辨率(即值变化的频率)     除了分辨率至少为。之外,不做任何保证     与{@link #currentTimeMillis()}一样好。

因此,根据操作系统的实现,无法保证返回的纳秒时间是正确的!它只有9位数字,与currentTimeMillis()具有相同的毫秒数。

完全有效的实施可能是currentTimeMillis() * 1000000

因此,即使没有性能问题,我也不会认为你真的从nano秒中获益。

答案 2 :(得分:10)

我想强调的是,即使通话非常便宜,也不会获得测量的纳秒分辨率。

让我举个例子(来自http://docs.oracle.com/javase/8/docs/api/java/lang/System.html#nanoTime--的代码):

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

因此,虽然两个长值都将被解析为纳秒,但JVM并不能保证您对nanoTime(),JVM的每次调用都会给您一个新值。

为了说明这一点,我写了一个简单的程序并在Win7x64上运行它(随意运行它并报告结果):

package testNano;
public class Main {
    public static void main(String[] args) {
        long attempts = 10_000_000L;
        long stale = 0;
        long prevTime;
        for (int i = 0; i < attempts; i++) {
            prevTime = System.nanoTime();
            long nanoTime = System.nanoTime();
            if (prevTime == nanoTime) stale++;
        }
        System.out.format("nanoTime() returned stale value in %d out of %d tests%n", stale, attempts);
    }
}

打印出nanoTime() returned stale value in 9117171 out of 10000000 tests

修改

我还建议您阅读有关此问题的Oracle文章:https://blogs.oracle.com/dholmes/entry/inside_the_hotspot_vm_clocks。该文的结论是:

  

如果您对测量绝对时间感兴趣,请始终使用System.currentTimeMillis()。请注意,它的分辨率可能非常粗糙(尽管这绝对不是绝对时间的问题。)

     

如果您对测量/计算已用时间感兴趣,请始终使用System.nanoTime()。在大多数系统上,它将提供微秒级的分辨率。但请注意,此调用也可能需要几微秒才能在某些平台上执行。

此外,您可能会发现此讨论很有趣:Why is System.nanoTime() way slower (in performance) than System.currentTimeMillis()?

答案 3 :(得分:4)

运行这个非常简单的测试:

public static void main(String[] args) {
    // Warmup loops
    long l;

    for (int i=0;i<1000000;i++) {
        l = System.currentTimeMillis();
    }

    for (int i=0;i<1000000;i++) {
        l = System.nanoTime();
    }

    // Full loops
    long start = System.nanoTime();
    for (int i=0;i<10000000;i++) {
        l = System.currentTimeMillis();
    }
    start = System.nanoTime()-start;
    System.err.println("System.currentTimeMillis() "+start/1000);

    start = System.nanoTime();
    for (int i=0;i<10000000;i++) {
        l = System.nanoTime();
    }
    start = System.nanoTime()-start;
    System.err.println("System.nanoTime() "+start/1000);

}

在Windows 7上,这显示millis的速度只有2倍:

System.currentTimeMillis() 138615
System.nanoTime() 299575

在其他平台上,差异并不大,nanoTime()实际上略微(~10%)更快:

在OS X上:

System.currentTimeMillis() 463065
System.nanoTime() 432896

在Linux上使用OpenJDK:

System.currentTimeMillis() 352722
System.nanoTime() 312960

答案 4 :(得分:4)

在这种情况下,最好的办法就是始终对它进行基准测试。由于时间仅取决于您的平台和操作系统,因此我们无法为您做任何事情,特别是因为您无法解释实际使用计时器的

nanoTime和currentTimeMillis一般都不保证单调性(nanoTime仅适用于HotSpot for Solaris,否则依赖于操作系统的现有单调时间源 - 因此对于大多数人而言,即使currentTimeMillis是单调不)。

幸运的是,由于jmh(java测量线束),你用Java编写基准测试相对容易,甚至更幸运AlekseyShipilёv实际调查nanoTime前一段时间:See here - 包括源代码自己做一个有趣的基准测试(它也是jmh本身的一个很好的入门读物,如果你想用相对较少的知识编写准确的基准测试 - 那个可以选择的那个......真是惊人的工程师背后的距离那个项目让普通民众尽可能直接进行基准测试!虽然如果你不小心的话,你当然还可以搞定; - ))

总结现代Linux发行版或Solaris和x86 CPU的结果:

  • 精度:30ns
  • 延迟:30ns最佳案例

视窗:

  • 精度:变化很大,370ns到15 μs
  • 延迟:变化很大,15ns到15 μs

但请注意,在一些罕见的情况下,Windows也可以为currentTimeMillis提供高达 100ms 的精度。选择你的毒药。

Mac OS X:

  • 精度:1μs
  • 延迟:50ns

根据您使用的平台(CPU / MB - 有一些有趣的旧硬件组合,虽然它们幸运地变老)和操作系统,这些结果会有所不同。哎呀,显然只是在800 MHz CPU上运行它,与3.6GHz服务器相比,你的结果会有很大不同。