Java中的CPU执行时间

12

我想计算Java中我的函数执行所需的CPU时间。目前,我正在按以下方式执行。

   long startTime = System.currentTimeMillis();
    myfunction();
    long endTime = System.currentTimeMillis();
    long searchTime = endTime - startTime;

然而我发现对于相同的输入,根据系统负载的不同,我得到了不同的时间。

那么,如何获取我的函数执行所需的确切CPU时间。

5个回答

38

1
如果我理解JavaDoc正确的话,System.currentTimeMillis()在系统时间改变时也可能会突然改变。因此,我认为测量经过的时间应该始终优先选择System.nanoTime()而不是System.currentTimeMillis() - JojOatXGME

12

随着JVM的热身,所需时间将会有所变化。第二次运行总是比第一次更快。(第一次需要加载类并调用静态块)当您运行该方法10,000次后,它将再次加速(默认阈值为将代码编译为本机机器代码的条件)

为了获得可重复的微基准测试平均计时,建议您忽略前10,000次迭代,并在此之后运行2-10秒。

例如:

long start = 0;
int runs = 10000; // enough to run for 2-10 seconds.
for(int i=-10000;i<runs;i++) {
    if(i == 0) start = System.nanoTime();
    // do test
}
long time = System.nanoTime() - start;
System.out.printf("Each XXXXX took an average of %,d ns%n", time/runs);

非常重要:每个方法只执行其中的一个循环。这是因为它根据使用情况优化整个方法。如果有一个繁忙的循环,那么后面的循环将会显得更慢,因为它们没有运行并且优化效果很差。


这个问题是关于“精确CPU时间”的,而这个答案是关于墙钟时间流逝的,正如Joachim Sauer的回答所详细说明的那样。当然,如果墙钟时间是您关心的问题,这个答案有很好的提示。 - Alexander Wessel
如上所述,此答案将包括线程在暂停期间(例如在GC期间)花费的任何时间,因此不是进行相对性能比较的好方法,也无法回答指定CPU时间的问题。 - Sherms
这是否意味着JIT将编译该方法10,000次?之后它就不再这样做了,所以速度会更快?谢谢。 - Diego Ramos

2
正确进行微基准测试的方法是学习并正确使用Java微基准测试工具(JMH),该工具从OpenJDK 12开始增加了JEP 230微基准测试套件。搜索“java jmh”将会得到一些有用的教程链接。我喜欢Jakob Jenkov的博客文章,当然还有由Aleksey Shipilëv撰写的任何内容,他是JMH的主要开发者和维护者。只需在提供的链接中选择最新版本的JMH讲座即可。
Java基准测试并不是一件简单的事情,而且被测试代码执行的工作越少,就会陷入更深的兔子洞。在尝试解决性能问题时,时间戳可能会非常误导人。时间戳确实适用于测量等待外部事件(例如等待HTTP请求的回复等)的时间,只要您可以确保在等待线程解除阻塞和获取“后”时间戳之间花费的时间可以忽略不计,并且只要线程首先被适当地解除阻塞。如果等待至少为数十毫秒,则通常情况下会出现这种情况。如果您等待某些东西的时间小于,那么也还不错。然而,预热和缓存效应将发生,并且会破坏您的测量结果对真实世界性能的适用性。
关于测量“精确 CPU 时间”,可以采用 Joachim Sauer 的回答中详细介绍的方法。在使用 JMH 时,可以在外部测量 CPU 使用情况,然后对测量的迭代次数进行平均,但是由于这将包括测试工具的开销,因此该方法适用于比较性能测量,但不适合推导出“我的函数 xy 在我使用的 CPU 架构上每次迭代平均需要多少 CPU 秒”。在现代 CPU 和 JVM 上,这样的观察几乎是不可能的。

1

有许多分析器(Jprofile、Jprobe、Yourkit)可用于分析此类数据。而且不仅如此,还有更多的功能...(例如内存利用率、线程详细信息等)


0

是的,但没有一个实际上回答了他如何测量“CPU时间”的问题。 - user458577

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