处理时间措施的最佳方法是什么?
我的目标是编写一个框架,用于测量方法执行或事务时间以及处理测量,即存储,分析等。事务可能包括调用外部系统,同步或异步等待结果。
关于这个话题已经存在一些问题,比如
- “ 我如何计算方法的执行时间 ”
- “ 测量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.compareUnsigned
和Long.toUnsignedString
,你甚至可以处理2⁶⁴纳秒持续时间,换句话说,如果您的计算机没有中断,您可以通过这种方式测量长达584年的经过时间…
我建议使用ThreadMXBean
getThreadCpuTime
(另请参阅https://stackoverflow.com/a/7467299/185031 )。 如果要测量方法的执行时间,那么大多数时候您对挂钟并不那么感兴趣,而是更多地关注CPU执行时间。