【问题标题】:Best approach for dealing with time measures? [duplicate]处理时间度量的最佳方法? [复制]
【发布时间】:2016-09-01 06:20:46
【问题描述】:

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

已经有一些关于该主题的问题,例如

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

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

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

System.currentTimeMillis()

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

System.nanoTime()

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

Instant.now() 和持续时间

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

鉴于事实,很明显,只有使用 System.nanoTime() 才能实现最佳精度,但我的问题更多地针对处理一般措施的最佳实践,这不仅包括采取措施,也包括措施处理。

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

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

那么最好的方法是什么?有什么我没有想到的影响吗?有其他选择吗?

【问题讨论】:

标签: java performance time java-8 timing


【解决方案1】:

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

更重要的方面是这些工具是否专为您的任务而设计。 System.currentTimeMillis() 和所有其他基于挂钟的 API,无论它们是否基于 currentTimeMillis(),旨在为您提供一个时钟,该时钟旨在与地球的自转及其绕太阳的路径同步,它会加载它Leap Seconds 的负担和其他纠正措施,更不用说您的计算机时钟可能与挂钟不同步并得到纠正,例如通过 NTP 更新,在最坏的情况下,当您尝试测量经过的时间时会立即跳转,甚至可能向后跳转。

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

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

因此,当您想要测量和处理方法执行或事务的经过时间时,System.nanoTime() 是您的最佳选择。诚然,它只提供了一个赤裸裸的 long 值,但不清楚你想要什么样的 API 支持。由于时间点在这里无关紧要,甚至会分散注意力,因此您将只有一个持续时间,您可以将其转换为其他 time units,或者,如果您想使用新的时间 API,您可以使用 @ 创建一个 Duration 对象987654324@,允许您添加和减去持续时间值并比较它们,但您无能为力。您不得将它们与挂钟或基于日历的持续时间混淆......

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

【讨论】:

    【解决方案2】:

    我建议使用ThreadMXBean 中的getThreadCpuTime(另请参阅https://stackoverflow.com/a/7467299/185031)。如果你想衡量一个方法的执行时间,你大部分时间不是对挂钟感兴趣,而是对 CPU 执行时间感兴趣。

    【讨论】:

    • 我想测量偶尔调用外部系统(即调用 web 服务)的方法,等待时间是否也会被 CPU 时间覆盖?
    • 如果您很忙,请等待。如果是异步号。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-21
    • 1970-01-01
    相关资源
    最近更新 更多