如何在Java中以微秒精度测量时间?

23

我在互联网上看到应该使用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
        }
    }
}

我已经多次这样做了 - 外部循环执行了20次每次为k做一次操作,但结果并不好。有时候进行1000万次插入比1百万次还要快,因为我现在使用的不是正确的测量时间方法(System.nanoTime())。

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

编辑3:我可能在代码中做错了什么,我会看看更改是否能实现我想要的效果。

编辑4:我的错误,似乎System.nanoTime()可以工作。太好了。


Java的哪个版本?在我的XP机器上,使用JDK1.6.0_06,nanoTime()函数精度可以达到小数点后的最后一位。我猜测你的版本返回的值总是以三个零结尾。 - basszero
Sun JVM 不够,需要哪个版本?(你的答案是正确的,我没有问清楚问题。我假设每个人都使用 Sun JVM) - basszero
在cmd中输入Java -version,输出如下:java version "1.7.0-ea"还有另外两行。无论如何,这是我的代码错误。很抱歉,即使搜索了一段时间也找不到错误。 - jao
8
需要微秒级精度的基准测试不会非常准确。如果网络上的另一台机器恰好向您的计算机发送 ping,或者您移动鼠标等,微秒级时间测量就会受到最轻微的 CPU 负载干扰而产生噪音。 - Kip
这并不回答你的具体问题,但使用这个可能会使它变得不必要:http://code.google.com/p/caliper/ - TJR
显示剩余2条评论
12个回答

35

我的猜测是,由于 System.nanoTime() 使用的是“最精确的系统计时器”,而你的系统上显然只有毫秒级别的精度,因此你无法获得更高精度的时间。


25

我不清楚您具体测试的是什么,但总的来说,任何测试时间如此之短,以至于低于50毫秒的精度变化是相关的,都会非常容易受到其他干扰。

通常我会尝试让基准测试运行至少10秒钟。我目前正在编写的框架将猜测要运行多少次迭代,以便它将花费30秒钟的时间。这意味着,即使有些其他过程占用了CPU的几毫秒,您也不会得到完全不同的结果。

长时间运行几乎总是比尝试使用更精细的精度进行测量更好


8

使用java.time

顺便提一下,Java 9及更高版本有一个全新的Clock实现,可以以纳秒分辨率捕获当前时刻。

Instant类表示时间线上的一个时刻,在UTC时区中具有纳秒(最多九(9)位小数)的分辨率。

调用Instant.now来捕获当前时刻。

  • 在Java 9及以上版本中,您可以获得当前时刻高达纳秒的分辨率。
  • 在Java 8中,当前时刻仅捕获毫秒的分辨率(您确实可以使用纳秒来保存值,但只能在毫秒级别上捕获当前时刻)。

代码:

Instant instant = Instant.now() ;

使用Duration类来表示与时间线无关的一段时间。该类以秒和纳秒为单位保存时间量。
Duration d = Duration.between( instantThen , Instant.now() );

为了明确,问题中要求的微秒分辨率位于毫秒和纳秒之间。小数部分的位数:毫秒为3(0.123),微秒为6(0.123456),纳秒为9(0.123456789)。

注意

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

以如此精细的粒度进行基准测试充满问题,通常不建议这样做。

并且要谨防过早的优化

有一个提案,将微基准测试功能添加到JEP 230: 微基准套件中。基于Java Microbenchmark Harness (JMH)


关于java.time

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

Joda-Time项目现在处于维护模式,建议迁移到java.time类。

要了解更多,请参见Oracle教程。并在Stack Overflow上搜索许多示例和解释。规范是JSR 310
在哪里获取java.time类? ThreeTen-Extra项目扩展了java.time的额外类。该项目是java.time可能未来添加的试验场。您可能会在此处找到一些有用的类,例如Interval, YearWeek, YearQuarter更多

4

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

注意:对于多CPU机器,Windows XP通常不太准确,因为它没有补偿不同CPU具有不同计数器。Linux有。

注意2:相对于System.currentTimeMillis(),它会漂移,因为它基于您的CPU时钟的准确性(长时间内不需要那么准确),而不是用于获取时间的时钟。(每天漂移较少,但粒度较低)

在基准测试中,您基本上正在测试创建新对象的速度。毫不奇怪,您的结果将根据GC设置以及最近进行GC的时间而大幅变化。

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

-verbosegc -XX:NewSize=128m -mx256m

3

很奇怪,System.nanoTime()应该是有效的。你正在使用Sun JVM吗?

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


3

你需要重复进行成千上万次的测试。有很多事情会影响你的测量结果,例如垃圾回收、I/O、交换进/出以及就绪队列线程的大小等。


3
如果您想要可靠的结果,使用性能分析工具。我建议使用易于安装且自JDK 1.6.0_07版本开始捆绑的VisualVM

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


1

可能底层操作系统不支持纳秒级别的计时器。

还有一篇旧文章


1

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

ThreadMXBean.getCurrentThreadCpuTime倾向于产生较小的时间,但其分辨率不明确,并且具有进一步的缺点(您真的想要CPU时间吗?平台相关语义,在您的平台上受支持?)。

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

关于微基准测试,请参见


0
我最终采用的是一种“快速而简单”的解决方案:
TimeUnit.NANOSECONDS.toMicros(System.nanoTime());

更新:

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

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

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

我将这个答案留在这里作为一个“警示”,以防其他人想到nanoTime :)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接