关于java:处理时间措施的最佳方法?

Best approach for dealing with time measures?

本问题已经有最佳答案,请猛点这里访问。

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

关于这个话题已经有了一些问题,比如

  • "如何计时方法的执行时间"
  • "测量Java方法的执行时间"
  • "System.CurrentTimeMillis与System.NanoTime"

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

  • System.currentTimeMillis()
  • 江户十一〔一〕号
  • EDOCX1·2和EDOCX1〉3(自Java 8以来)

我知道,所有这些都有一些含义

System.CurrentTimeMillis()。

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

System.NanoTime()。

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

instant.now()和持续时间

因为Java 8有新的时间API。一个瞬间有一个第二个和一个纳米第二个场,因此它在对象的顶部引用两个长值(对于Duration)。你还可以获得毫秒级的精度,这取决于基础时钟(见"纳秒分辨率的Java 8即时")。.实例化是通过调用Instant.now()来完成的,该Instant.now()映射到正常系统时钟的System.currentTimeMillis()上。

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

  • 即时和持续时间提供了最佳的API支持(计算、比较等),但在标准情况下具有操作系统依赖性和精度,并且内存和创建度量的开销更大(对象构造、更深的调用堆栈)

  • System.NanoTime()和System.CurrentTimeMillis()具有不同的精度级别,但只有基本的"API"支持(对long的数学操作),但获取速度更快,在内存中保存的空间更小。

那么最好的方法是什么呢?有什么我没想到的暗示吗?有什么选择吗?


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

更重要的方面是工具是否为您的任务而设计。System.currentTimeMillis()和所有其他基于挂钟的API,不管它们是否基于currentTimeMillis(),都是为了给你一个与地球自转及其绕太阳运行的路径同步的时钟,它给你加载闰秒和其他校正措施的负担,而不是说你的计算机的不管怎样,锁可能与挂钟不同步,并会得到纠正,例如通过NTP更新,在最坏的情况下,当您试图测量您的已用时间,甚至向后跳时。

相比之下,System.nanoTime()的设计目的是测量经过的时间(确切地说是您想要做的),而不是其他任何事情。因为它的返回值有一个未指定的原点,甚至可能是负数,所以只有此方法返回的两个值之间的差异才有任何意义。您甚至可以在文档中找到:

The values returned by this method become meaningful only when the difference between two such values, obtained within the same instance of a Java virtual machine, is computed.

因此,当您想要度量和处理方法执行或事务所花费的时间时,System.nanoTime()是一条可行的路。当然,它只提供了一个裸体的long值,但还不清楚您想要哪种API支持。由于时间点在这里是不相关的,甚至是分散注意力的,所以您只能有一个持续时间,您可以将其转换为其他时间单位,或者,如果您想使用新的时间API,您可以使用Duration.ofNanos(long)创建一个Duration对象,允许您添加和减去持续时间值并进行比较,但是您不能做太多的事情。你不能把它们和挂钟或基于日历的持续时间混在一起…

最后一点要注意的是,文档对限制有点不精确。如果您正在计算由System.nanoTime()返回的两个值之间的差异,那么数字溢出本身并不坏。由于计数器的来源不明,您的操作的起始值可能接近Long.MAX_VALUE,而结束值可能接近Long.MIN_VALUE,因为jvm的计数器溢出。在这种情况下,计算差异将导致另一个溢出,从而产生正确的差异值。但是,如果您将该差异存储在已签名的long中,它最多可以容纳2个?3纳秒,最大限制为292年,但如果您将其视为无符号长,例如通过Long.compareUnsignedLong.toUnsignedString,您甚至可以处理2??纳秒持续时间,换句话说,如果您的计算机没有在这两者之间中断,您可以用这种方式测量长达584年的时间。


我建议使用来自ThreadMXBeangetThreadCpuTime(另见https://stackoverflow.com/a/7467299/185031)。如果你想测量一个方法的执行时间,你大部分时间对挂钟不太感兴趣,而是对CPU执行时间更感兴趣。