为什么System.nanoTime()的性能比System.currentTimeMillis()慢得多?

67

今天我进行了一个小小的基准测试,以测试System.nanoTime()System.currentTimeMillis()的速度性能:

long startTime = System.nanoTime();

for(int i = 0; i < 1000000; i++) {
  long test = System.nanoTime();
}

long endTime = System.nanoTime();

System.out.println("Total time: "+(endTime-startTime));

这是结果:

System.currentTimeMillis(): average of 12.7836022 / function call
System.nanoTime():          average of 34.6395674 / function call

为什么奔跑速度的差距如此之大?

基准系统:

Java 1.7.0_25
Windows 8 64-bit
CPU: AMD FX-6100

可能是为什么System.nanoTime()和System.currentTimeMillis()会如此迅速地漂移?的重复问题。 - chrylis -cautiouslyoptimistic-
这篇帖子可能会回答你的问题:https://dev59.com/WW025IYBdhLWcg3wtobX#5839267 - matts
nanoTimecurrentTimeMillis更精确,这可能是原因。 - Thomas
10
不同意这是一个重复的问题。这个问题在询问执行速度,而另一个问题在询问漂移。也许答案相似,但问题不同。 - Erick Robertson
虽然这并没有回答你的问题,但即使是“慢”的nanoTime也只需要34 纳秒来执行。我看不出有很多使用情况会变得如此缓慢以至于成为一个问题。 - yannick1976
5个回答

71
根据这篇Oracle博客

System.currentTimeMillis()使用 GetSystemTimeAsFileTime 方法实现,它基本上只读取Windows维护的低分辨率时间值。根据报告的信息,读取全局变量自然非常快-大约6个周期。

System.nanoTime()是使用QueryPerformanceCounter / QueryPerformanceFrequency API实现的(如果可用,则返回currentTimeMillis*10^6)。   QueryPerformanceCounter (QPC)在运行它的硬件上以不同的方式实现。通常它将使用可编程间隔定时器(PIT)或ACPI电源管理计时器(PMT)或CPU级时间戳计数器(TSC)。访问PIT/PMT需要执行缓慢的I/O端口指令,因此QPC的执行时间为微秒级别。相比之下,从芯片读取TSC并根据操作频率将其转换为时间值需要大约100个时钟周期。

也许这回答了问题。这两种方法使用不同数量的时钟周期,因此后者的速度较慢。

在该博客的结论部分中:

 

如果您有兴趣测量/计算经过的时间,请始终使用System.nanoTime()。在大多数系统上,它将提供微秒级分辨率。但请注意,在某些平台上,这个调用也可能需要耗费几微秒的时间


12
不错的回答。此外,这个网站:http://stas-blogspot.blogspot.nl/2012/02/what-is-behind-systemnanotime.html 有人做了一些研究,并展示了不同操作系统上System.nanoTime的源代码。 - MystyxMac

24

大多数操作系统(您没有提到使用哪个)都有一个内存计数器/时钟,提供毫秒级精度(或接近此精度)。为了达到纳秒级别的精度,大多数要读取硬件计数器。与硬件通信比读取内存中的某些值慢。


5
这可能只适用于Windows系统。请参见类似问题的此答案
基本上,System.currentTimeMillis() 只读取由Windows维护的全局变量(这就是其粒度低的原因),而System.nanoTime() 实际上必须执行IO操作。

1
你正在使用Windows进行测量,对吗?我在2008年经历过这个过程。nanoTime在Windows上比currentTimeMillis慢。据我记得,在Linux上,nanotime比currentTimeMillis快,而且肯定比在Windows上快。
需要注意的重要事项是,如果您试图测量多个亚毫秒操作的总和,必须使用nanotime,因为如果操作在不到1/1000秒内完成,使用currentTimeMillis将显示该操作为瞬间完成,因此1,000次这样的操作仍将被视为瞬间完成。您可能希望使用nanotime然后四舍五入到最近的毫秒,因此,如果一个操作花费了8000纳秒,它将被计算为1毫秒,而不是0。

0
你可能想要做的是使用纳秒时间,然后四舍五入到最近的毫秒,这样如果一个操作花费了8000纳秒,它将被计算为1毫秒,而不是0。
算术注意事项:
8000纳秒等于8微秒,即0.008毫秒。四舍五入将其取为0毫秒。

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