【问题标题】:Precise time measurement in JavaJava中的精确时间测量
【发布时间】:2009-09-30 22:06:43
【问题描述】:

Java 提供了两种获取当前时间的方法:System.nanoTime()System.currentTimeMillis()。第一个以纳秒为单位给出结果,但实际精度比这差得多(很多微秒)。

JVM 是否已经为每台特定机器提供了最佳价值? 否则,是否有一些 Java 库可以提供更精细的测量,可能通过绑定到特定系统?

【问题讨论】:

  • 您在什么操作系统中证明 System.nanoTime() 产生的输出偏离了“许多微秒”?
  • 它是在双核机器上的 linux 系统上,但它是一个相当旧的安装(从 2007 年初开始......)。也许这就是原因。我会检查一下最近的事情。同样据我所知,我有连续的调用返回相同的值,然后跳跃了几微秒。

标签: java performance measurement


【解决方案1】:

获得超精确时间测量的问题是某些处理器不能/不提供如此微小的增量。

据我所知,System.currentTimeMillis()System.nanoTime() 是您能找到的最佳测量值。

请注意,两者都返回 long 值。

【讨论】:

  • 现代处理器 (>1ghz) 的周期快于 1 纳秒,因此它们在技术上相当强大。
  • 他们可以记录时间,但这并不意味着他们报告的时间那么准确。
  • 不要忘记有开销:涉及到一个系统调用,它本身通常是微秒级的(只是跳入内核并退出,但这是读取时钟的昂贵部分)。然后,您可能有一个启用了抢占的加载系统,这意味着可能会安排其他一些进程。即使不是这种情况,这意味着您仍然必须跳入 JVM 并且即使使用 JITed 代码也会有一点开销。在原生 gode 中,您可以使用 clock_gettime & friends API 来探索高分辨率计时器的准确性。
  • 确实,我刚刚试过,在我的家用机器上,看起来纳米时间确实需要超过一微秒(平均值是 1.2,通过调用它 100000 次来测量)。
  • linux时间刻度精度默认为10ms,所以要求纳秒是没有用的,除非你调整内核来支持它(如何调整的url在我的答案中)
【解决方案2】:

在 Java 中将时间测量到纳秒级有点毫无意义;偶尔的 GC 命中将很容易消除这可能提供的任何类型的准确性。无论如何,文档指出,虽然它提供纳秒精度,但它与纳秒精度不同。并且有些操作系统在任何情况下都不报告纳秒(这就是为什么您在访问它们时会发现量化为 1000 的答案;这不是运气,而是限制)。

不仅如此,根据操作系统实际实现该功能的方式,您可能会发现量化的结果(例如,总是以 64 或 128 而不是中间值结尾的答案)。

还值得注意的是,该方法的目的是找到某个(附近)开始时间与现在之间的两个时间差;如果您在长时间运行的应用程序开始时使用 System.nanoTime(),然后在很长一段时间后使用 System.nanoTime(),它可能与实时相差甚远。所以你应该只在不到 1 秒的时间内真正使用它;如果您需要更长的运行时间,那么毫秒就足够了。 (如果不是,则补上最后几个数字;您可能会给客户留下深刻印象,结果也同样有效。)

【讨论】:

  • “所以你应该只在少于 1 秒的时间内真正使用它”。这是针对小的重复现象。 “如果不是,那就补上最后几个数字”。不,他们可能想尝试重现这个:)
【解决方案3】:

不幸的是,我认为目前 java RTS 还不够成熟。

Java 时间确实试图提供最佳价值(它们实际上委托本机代码调用获取内核时间)。但是,JVM 规范主要针对 GC 活动和对底层系统的支持等方面做出这种粗略的时间测量免责声明。

  • 即使您正在运行并发 GC,某些 GC 活动也会阻塞所有线程。
  • 默认 linux 时钟滴答精度仅为 10ms。如果 linux 内核不支持,Java 无法让它变得更好。

除非您的应用不需要执行 GC,否则我还没有弄清楚如何解决 #1。一个体面的中等大小的应用程序可能偶尔会在 GC 暂停上花费数十毫秒。如果您的精度要求低于 10 毫秒,那么您可能不走运。

至于#2,您可以tune the linux kernal 提供更高的精度。但是,由于现在内核上下文切换更加频繁,因此您的开箱即用也越来越少。

或许,我们应该换个角度看。 OPS需要10ms以下的精度有什么原因吗?是否可以告诉 Ops 精度为 10ms 并且当时还查看 GC 日志,所以他们知道在没有 GC 活动的情况下时间是 +-10ms 准确?

【讨论】:

  • "即使您正在运行并发 GC,某些 GC 活动也会阻塞所有线程。"您是对的,但另一方面,通过对 JVM 参数进行一些调整,可以部分缓解这种情况。正如提议的那样,是的,可以考虑并删除在 GC 中经过的时间。
  • 我的意思不是我们不能调整它。我的观点是,即使您对其进行调整,您也无法将 GC 降低到您似乎喜欢的纳秒级。那是我对“体面”应用程序的定义,应该已经调整了:)
【解决方案4】:

如果您希望以纳秒级记录某种类型的现象,您真正需要的是real-time operating system。计时器的准确性很大程度上取决于操作系统对其high resolution timer 和底层硬件的实现。

但是,您仍然可以使用 Java,因为有可用的 RTOS 版本。

【讨论】:

    【解决方案5】:

    JNI: 创建一个简单的函数来访问 Intel RDTSC 指令或 ARM 中协处理器 p15 的 PMCCNTR 寄存器。

    纯Java: 如果您愿意延迟到时钟滴答声,您可能会获得更好的价值。您可以旋转检查 System.nanoTime() 直到值更改。例如,如果您知道 System.nanoTime() 的值在您的平台上每 10000 次循环迭代就会发生变化量 DELTA,那么实际事件时间是 finalNanoTime-DELTA*ITERATIONS/10000。在进行实际测量之前,您需要“预热”代码。

    Hack(仅用于分析等): 如果垃圾收集让您失望,您总是可以使用在不创建对象的第二个 jvm 中运行的高优先级线程来测量时间。让它在你用作时钟的共享内存中自旋递增一个 long。

    【讨论】:

    • 我想知道 JNI 调用开销是多少。如果它接近于零,RDTSC 确实是要走的路。
    猜你喜欢
    • 2012-12-29
    • 1970-01-01
    • 1970-01-01
    • 2010-11-01
    • 2019-03-28
    • 2018-03-17
    • 2023-01-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多