处理时间措施的最佳方法是什么?

我的目标是编写一个框架,用于测量方法执行或事务时间以及处理测量,即存储,分析等。事务可能包括调用外部系统,同步或异步等待结果。

关于这个话题已经存在一些问题,比如

  • “ 我如何计算方法的执行时间 ”
  • “ 测量Java方法的执行时间 ”
  • “ System.currentTimeMillis vs System.nanoTime ”

所有的答案归结为三种方法来花时间

  • System.currentTimeMillis()
  • System.nanoTime()
  • Instant.now()Duration (自Java 8起)

我知道,所有这些都有一些影响

System.currentTimeMillis的()

此方法的结果取决于平台。 在Linux上你获得1ms的分辨率,在Windows中你可以获得10ms(单核)~15ms(多核)。 因此,可以测量大型运行操作或短期运行操作的多次执行。

System.nanoTime()

你获得了一个高分辨率的时间测量,具有纳秒精度(但不一定是纳秒精度),并且在292年后你会得到溢出(我可以忍受)。

Instant.now()和持续时间

从Java 8开始,有新的时间API。 瞬间有第二个和第二个纳米字段,因此它在对象引用的顶部使用两个长值( Duration相同)。 您还可以获得纳秒级精度,具体取决于基础时钟(请参阅“ 具有纳秒分辨率的Java 8 Instant.now()? ”)。 实例化是通过调用Instant.now()完成的, Instant.now()映射到System.currentTimeMillis()以获得正常的系统时钟。

鉴于事实,很明显,最佳精确度只能通过System.nanoTime()实现,但我的问题更多地针对处理一般措施的最佳实践 ,其中不仅包括采取措施,还包括措施处理。

  • Instant和Duration提供最佳API支持(计算,比较等),但在标准情况下具有os-dependend精度,内存和创建度量的更多开销(对象构造,更深的callstack)

  • System.nanoTime()和System.currentTimeMillis()具有不同的精度级别,但只有基本的“api”支持(长时间的数学运算),但更快和更小以保持在内存中。

那么最好的方法是什么? 有没有我没想过的含义? 还有其他选择吗?

你过分关注精度的不重要细节。 如果要测量/分析某些操作的执行,则必须确保这些操作运行的时间足够长,以使测量不受一次性工件,线程调度时间,垃圾收集或HotSpot优化的微小差异的影响。 在大多数情况下,如果差异小于毫秒级,则从它们得出结论是没有用的。

更重要的方面是这些工具是否专为您的任务而设计。 System.currentTimeMillis()和所有其他基于wall-clock的API,无论它们是否基于currentTimeMillis() ,都旨在为您提供一个时钟,该时钟旨在与地球的旋转及其绕太阳的路径同步,它有闰秒的负担和其他纠正措施,更不用说计算机的时钟可能与挂钟不同并且得到纠正,例如通过NTP更新,在最糟糕的情况下,当你是试图测量你的经过时间,甚至可能倒退。

相比之下, System.nanoTime()旨在测量经过的时间 (正是您想要做的),而不是其他任何内容。 由于它的返回值有一个未指定的原点,甚至可能是负数,因此只有这个方法返回的两个值之间的差异才有意义。 您甚至可以在文档中找到它:

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

因此,当您想要测量和处理方法执行或事务的已用时间时, System.nanoTime()System.nanoTime()的方法。 当然,它只提供一个裸的long值,但不清楚你想要什么样的API支持。 由于时间点不相关甚至分散注意力,因此您只有一个持续时间,您可以转换为其他时间单位,或者,如果您想使用新时间API,您可以使用Duration.ofNanos(long)创建一个Duration对象Duration.ofNanos(long) ,允许您添加和减去持续时间值并进行比较,但您可以做的更多。 你不能将它们与壁钟或基于日历的持续时间混合在一起……

最后一点,文档对这个限制有点不清楚。 如果计算System.nanoTime()返回的两个值之间的差异,则数值溢出本身并不错。 由于计数器具有未指定的原点,因此操作的Long.MAX_VALUE可能接近Long.MAX_VALUE而结束值接近Long.MIN_VALUE因为JVM的计数器有溢出。 在这种情况下,计算差异将导致另一次溢出,从而产生差异的正确值。 但是如果你将这个差异存储在有符号long ,它最多可以保持2⁶3纳秒,将差异限制为最大292年,但如果你把它视为无符号长 ,例如通过Long.compareUnsignedLong.toUnsignedString ,你甚至可以处理2⁶⁴纳秒持续时间,换句话说,如果您的计算机没有中断,您可以通过这种方式测量长达584年的经过时间…

我建议使用ThreadMXBean getThreadCpuTime (另请参阅https://stackoverflow.com/a/7467299/185031 )。 如果要测量方法的执行时间,那么大多数时候您对挂钟并不那么感兴趣,而是更多地关注CPU执行时间。