【问题标题】:How can I measure time with microsecond precision in Java?如何在 Java 中以微秒精度测量时间?
【发布时间】:2010-10-04 23:16:21
【问题描述】:

我在 Internet 上看到我应该使用 System.nanoTime() 但这对我不起作用 - 它给了我毫秒精度的时间。我只需要我的函数执行前后的微秒,这样我就知道需要多长时间。我使用的是 Windows XP。

基本上,我有这段代码,例如,在 java 链表中执行 100 万到 1000 万次插入。问题是我无法正确测量精度;有时在较小的列表中插入所有内容所需的时间更少。

这是一个例子:

class test
{
    public static void main(String args[])
    {
        for(int k=1000000; k<=10000000; k+=1000000)
        {
            System.out.println(k);
            LinkedList<Integer> aux = new LinkedList<Integer>();
            //need something here to see the start time
            for(int i=0; i<k; i++)
                aux.addFirst(10000);
            //need something here to see the end time
            //print here the difference between both times
        }
    }
}

我这样做了很多次 - 每个 k 都有一个外部循环执行 20 次 - 但结果并不好。有时进行 1000 万次插入所需的时间比 100 万次要少,因为我现在使用的没有得到正确的测量时间(System.nanoTime())

编辑 2:是的,我使用的是 Sun JVM。

编辑 3:我可能在代码中做错了什么,我会看看更改它是否符合我的要求。

编辑 4:我的错误,似乎 System.nanoTime() 有效。唷。

【问题讨论】:

  • 什么版本的Java?在我的带有 JDK1.6.0_06 的 XP 机器上,nanoTime() 精确到最后一个小数位。我假设您的版本返回的值总是以三个零结尾。
  • Sun JVM 不够用,哪个版本的呢? (你的回答是正确的,我没有问正确的问题。我假设每个人都使用 Sun JVM)
  • cmd 中的 Java -version 给了我: java version "1.7.0-ea" 和另外 2 行。无论哪种方式,这都是我的代码中的错误。抱歉,我搜索了一段时间也找不到错误。
  • 勇敢的灵魂,1.7 还为时过早
  • 需要微秒精度的基准测试不会非常准确。如果您网络上的另一台机器碰巧 ping 您的盒子,如果您移动鼠标等,您可以在微秒时间测量中添加噪声,只需一点额外的 CPU 负载。

标签: java time


【解决方案1】:

如果您想要一个可靠的结果,请使用分析器。我建议VisualVM,它易于安装,并且从版本 1.6.0_07 开始与 JDK 捆绑在一起。

它是一个易于使用的可视化工具,集成了多个命令行 JDK 工具和轻量级分析功能。

【讨论】:

    【解决方案2】:

    对于我们最近的分析,我发现 ThreadMXBean.getCurrentThreadCpuTime() 和选项 -XX:+UseLinuxPosixThreadCPUClocks 可以满足我们的需要。

    详情请见http://bugs.java.com/view_bug.do?bug_id=6888526

    【讨论】:

      【解决方案3】:

      System.nanoTime() 在 CPU 中使用计数器,在 Windows XP 和 Linux 上通常精确到大约 1 微秒。

      注意:Windows XP 在多 CPU 机器上通常不太准确,因为它不能补偿具有不同计数器的不同 CPU。 Linux 可以。 注意 2:它会相对于 System.currentTimeMillis() 漂移,因为它基于您的 CPU 时钟的准确性(在一段时间内不需要如此准确),而不是您拥有的时钟用于获取时间。(每天漂移较少,但粒度较小)

      在您的基准测试中,您基本上是在测试创建新对象的速度。毫不奇怪,您的结果会根据您的 GC 设置和最近执行一次 GC 的时间而有很大差异。

      尝试使用以下选项运行测试,您应该会看到截然不同的结果。

      -verbosegc -XX:NewSize=128m -mx256m
      

      【讨论】:

        【解决方案4】:

        您必须重复测试数千次。有很多事情会影响您的测量,例如垃圾收集、I/O、换入/换出、就绪队列线程的大小等。

        【讨论】:

          【解决方案5】:

          我不清楚您到底在进行什么基准测试,但总的来说,任何需要这样短时间运行的测试,低于 50 毫秒的准确度都是相关的非常容易受到其他干扰。

          我通常会尝试让基准测试运行至少 10 秒。我现在正在编写的框架会猜测要运行多少次迭代,这样就需要 30 秒。这意味着您不会仅仅因为某些其他进程占用 CPU 几毫秒而得到根本不同的结果。

          运行更长时间几乎总是一种更好的方法,而不是尝试以更细粒度的精度进行测量。

          【讨论】:

            【解决方案6】:

            使用 java.time

            仅供参考,Java 9 及更高版本具有Clock 的全新实现,可以以高达纳秒的分辨率捕获当前时刻。

            Instant 类表示UTC 中时间轴上的时刻,分辨率为nanoseconds(最多九 (9) 位小数)。

            致电Instant.now 捕捉当前时刻。

            • 在 Java 9 及更高版本中,您可以在当前时刻获得高达 nanoseconds 的分辨率。
            • 在 Java 8 中,当前时刻只能以milliseconds 分辨率捕获(您确实可以保持 纳秒级的值,但只能捕获 以毫秒为单位的当前时刻) .

              Instant Instant = Instant.now() ;

            使用Duration 类表示未附加到时间线的时间跨度。保存以秒和纳秒为单位的时间量。

            Duration d = Duration.between( instantThen , Instant.now() );
            

            需要明确的是,问题中提出的microseconds 分辨率介于毫秒和纳秒之间。小数位数:毫秒为 3 (0.123),微秒为 6 (0.123456),纳秒为 9 (0.123456789)。

            警告

            Java 依赖于计算机的硬件时钟。正如其他人所警告的那样,硬件几乎肯定会以远低于精度远低于纳秒的分辨率捕获时间。

            如此细粒度的基准测试充满了问题,通常不可取

            注意premature optimization

            JEP 230: Microbenchmark Suite 中提出了向 Java 平台添加微基准测试工具的建议。基于Java Microbenchmark Harness (JMH)


            关于java.time

            java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.DateCalendarSimpleDateFormat

            Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。

            要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310

            从哪里获得 java.time 类?

            ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore

            【讨论】:

              【解决方案7】:

              我最终采用的“快速而肮脏”的解决方案:

              TimeUnit.NANOSECONDS.toMicros(System.nanoTime());
              

              更新:

              我最初使用 System.nanoTime,但后来我发现它只能用于经过的时间,我最终更改了我的代码以使用毫秒或在某些地方使用:

              TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis());
              

              但这只会在值的末尾添加零 (micros = millis * 1000)

              将此答案留在这里作为“警告标志”,以防其他人想到 nanoTime :)

              【讨论】:

                【解决方案8】:

                是的,System.nanoTime 的准确性和精度通常比 System.currentTimeMillis 好得多,但不能保证:在最坏的情况下它可能会变得同样糟糕。

                ThreadMXBean.getCurrentThreadCpuTime 往往会产生较小的时间,但其分辨率尚不清楚,并且还有其他缺点(您真的想要 CPU 时间吗?平台相关语义,您的平台支持?)。

                使用所有三种技术测量时间也有一定的成本,即需要时间本身,这可能会扭曲测量结果。成本高度依赖于平台,但通常是 cost(System.currentTimeMillis)

                关于一般的微基准测试,请参阅

                【讨论】:

                  【解决方案9】:

                  这种依赖于短时间间隔的基准测试会给您带来不可靠的结果。由于 I/O、交换、进程切换、缓存、垃圾收集等外部因素,您总是会得到不同的结果。此外,JVM 会优化您的调用,因此第一次测量的事情可能比后来的调用慢。 JVM 开始越来越多地优化你执行的命令。

                  另外,像 System.nanoTime() 这样的方法依赖于底层系统的定时器。他们可能(并且很可能)没有粒度来衡量该准确性。引用API

                  此方法提供纳秒 精度,但不一定 纳秒精度。没有任何保证 关于值的频率 改变。

                  要真正进行高精度测量,您需要访问具有保证精度的外部计时硬件。

                  要使您的基准测试更加稳定,您需要多次执行它并测量比毫秒更大的时间间隔。

                  【讨论】:

                    【解决方案10】:

                    可能是底层操作系统不提供纳秒精度的计时器。

                    还有一个older post

                    【讨论】:

                      【解决方案11】:

                      我的猜测是,由于System.nanoTime() 使用“最精确的可用系统计时器”,它显然在您的系统上只有毫秒精度,因此您无法得到更好的结果。

                      【讨论】:

                        【解决方案12】:

                        这很奇怪。 System.nanoTime() 应该可以工作。您使用的是 Sun JVM 吗?

                        你能重复你的操作 1000 次,然后将时间除以 1000 来找出你需要知道的内容吗?

                        【讨论】:

                          猜你喜欢
                          • 2013-04-06
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 2016-07-14
                          • 1970-01-01
                          • 2015-07-20
                          • 1970-01-01
                          相关资源
                          最近更新 更多