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

我在互联网上看到我应该使用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 aux = new LinkedList(); //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()工作。 唷。

我的猜测是,因为System.nanoTime()使用“最精确的可用系统计时器”,它显然只有毫秒级的精度,所以你无法获得更好的结果。

我不清楚你究竟是什么基准测试,但一般来说,任何需要花费这么短时间才能运行的测试,相关的精度低于50毫秒,将非常容易受到其他干扰。

我通常会尝试使基准测试运行至少10秒。 我现在正在编写的框架将猜测要运行多少次迭代,以便花费30秒。 这意味着你不会因为其他一些进程偷走CPU几毫秒而得到截然不同的结果。

运行更长时间几乎总是比尝试以更精细的精度测量更好的方法。

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

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

在您的基准测试中,您基本上测试了创建新对象的速度。 毫不奇怪,根据您的GC设置以及最近GC的执行情况,您的结果会有很大差异。

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

-verbosegc -XX:NewSize = 128m -mx256m

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

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

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

使用java.time

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

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

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

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

    即时瞬间= Instant.now();

使用Duration类表示未附加到时间轴的时间跨度。 以秒和纳秒为单位保持一定的时间。

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

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

警告

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

如此细粒度的基准测试充满了问题,而且一般不可取

并提防过早优化 。

有人建议在JEP 230:Microbenchmark Suite中向Java平台添加微基准测试工具。 基于Java Microbenchmark Harness(JMH) 。


关于java.time

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

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

要了解更多信息,请参阅Oracle教程 。 并搜索Stack Overflow以获取许多示例和解释。 规范是JSR 310 。

从哪里获取java.time类?

  • Java SE 8SE 9及更高版本
    • 内置。
    • 带有捆绑实现的标准Java API的一部分。
    • Java 9增加了一些小function和修复。
  • Java SE 6SE 7
    • 许多java.timefunction都被反向移植到ThreeTen-Backport中的 Java 6和7。
  • Android的
    • ThreeTenABP项目特别适用于Android的ThreeTen-Backport (如上所述)。
    • 请参见如何使用ThreeTenABP ….

ThreeTen-Extra项目使用其他类扩展了java.time。 该项目是未来可能添加到java.time的试验场。 您可以在这里找到一些有用的课程,如IntervalYearWeekYearQuarter等。

如果您想获得可靠的结果,请使用分析器。 我建议使用VisualVM ,它易于安装,并且从版本1.6.0_07开始与JDK捆绑在一起。 它是一个易于使用的可视化工具,集成了多个命令行JDK工具和轻量级分析function。

可能的情况是底层OS不提供具有纳秒精度的定时器。

还有一个较旧的post 。

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

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

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

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

  • 如何在Java中编写正确的微基准测试?
  • 用java创建快速/可靠的基准测试?

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

此外,System.nanoTime()等方法依赖于底层系统的计时器。 他们可能(并且很可能)不具有以该准确度来衡量的粒度。 引用API :

该方法提供纳秒精度,但不一定是纳秒精度。 不保证值的变化频率。

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

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

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

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

更新:

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

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

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

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

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

有关更多详细信息,请参见http://bugs.java.com/view_bug.do?bug_id=6888526