Java中的时间测量开销

问题描述

| 在较低级别上测量经过时间时,我可以选择使用以下任一方法
System.currentTimeMillis();
System.nanoTime();
两种方法都实现为“ 1”。在深入研究任何C代码之前,是否有人知道调用一个或另一个是否有大量开销?我的意思是,如果我真的不关心这种额外的精度,那么可以预期哪一种会减少cpu时间消耗? N.B:我使用的是标准Java 1.6 JDK,但该问题可能对任何JRE都有​​效。     

解决方法

在此页面上标记为正确的答案实际上是不正确的。由于JVM死代码消除(DCE),栈上替换(OSR),循环展开等原因,这不是编写基准的有效方法。只有像Oracle的JMH微基准测试框架之类的框架才能衡量正确地。如果您对此类微型基准测试的有效性有任何疑问,请阅读此文章。 这是针对
System.currentTimeMillis()
System.nanoTime()
的JMH基准:
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Benchmark)
public class NanoBench {
   @Benchmark
   public long currentTimeMillis() {
      return System.currentTimeMillis();
   }

   @Benchmark
   public long nanoTime() {
    return System.nanoTime();
   }
}
以下是结果(在I​​ntel Core i5上):
Benchmark                            Mode  Samples      Mean   Mean err    Units
c.z.h.b.NanoBench.currentTimeMillis  avgt       16   122.976      1.748    ns/op
c.z.h.b.NanoBench.nanoTime           avgt       16   117.948      3.075    ns/op
这表明
System.nanoTime()
每次调用时的〜118ns速度要快于〜123ns。但是,很明显,一旦考虑到平均误差,两者之间的差异就很小。结果也可能会因操作系统而异。但是总的来说应该是它们在开销方面基本相同。 2015年8月25日更新:尽管使用JMH进行衡量,这个答案更能纠正大多数问题,但仍然不正确。测量like3ѭ本身是一种特殊的扭曲基准测试。答案和权威文章在这里。     ,我认为您不必担心这两者的开销。它是如此之小,几乎无法测量。这是两者的快速微观基准:
for (int j = 0; j < 5; j++) {
    long time = System.nanoTime();
    for (int i = 0; i < 1000000; i++) {
        long x = System.currentTimeMillis();
    }
    System.out.println((System.nanoTime() - time) + \"ns per million\");

    time = System.nanoTime();
    for (int i = 0; i < 1000000; i++) {
        long x = System.nanoTime();
    }
    System.out.println((System.nanoTime() - time) + \"ns per million\");

    System.out.println();
}
最后的结果是:
14297079ns per million
29206842ns per million
看来
System.currentTimeMillis()
的速度是
System.nanoTime()
的两倍。但是29ns将会比您要测量的任何其他东西都要短得多。我会为精度和准确性争取到3英镑,因为它与时钟无关。     ,您仅应使用ѭ3来测量运行某物所需的时间。这不仅仅是纳秒级精度的问题,
System.currentTimeMillis()
是“挂钟时间”,而
System.nanoTime()
是用于计时的东西,而没有其他的“现实世界时间”怪癖。来自
System.nanoTime()
的Javadoc:   此方法只能用于测量经过时间,并且与系统或挂钟时间的任何其他概念无关。     ,如果有时间,请观看Cliff Click的演讲,他谈到17英镑的价格以及其他事情。     ,
System.currentTimeMillis()
通常非常快(afaik 5-6 cpu周期,但我不知道我在哪里读过这篇文章),但是它的分辨率在不同平台上有所不同。 因此,如果您需要高精度,请选择
nanoTime()
,如果您担心开销,请选择
currentTimeMillis()
。     ,对此问题的公认答案确实是不正确的。 @brettw提供的替代答案是好的,但是细节上还是很轻巧的。 有关此主题的完整处理以及这些电话的实际费用,请参阅https://shipilev.net/blog/2014/nanotrusting-nanotime/ 要回答问的问题:   有谁知道打给对方的费用是否很高? 每次呼叫“ 21”的开销在15到30纳秒之间。 resolution22ѭ报告的值(其分辨率)每30纳秒仅更改一次 这意味着取决于您是否尝试每秒执行百万个请求,调用
nanoTime
意味着您实际上正在丢失第二个调用
nanoTime
的很大一部分。对于此类用例,可以考虑测量来自客户端的请求,从而确保您不会陷入协同疏漏,测量队列深度也是一个很好的指标。 如果您不打算在一秒钟内完成尽可能多的工作,那么
nanoTime
并不重要,但是协调的遗漏仍然是一个因素。 最后,为了完整起见,无论成本多少,都不能使用
currentTimeMillis
。这是因为不能保证在两个呼叫之间前进。尤其是在具有NTP的服务器上,ѭ26正在不断地移动。更不用说计算机衡量的大多数事情不会花费整整毫秒的时间。     ,从理论上讲,对于使用本机线程并位于现代抢先式操作系统上的VM,可以将currentTimeMillis实现为每个时间片仅读取一次。大概,nanoTime实现不会牺牲精度。