测量Java方法的执行时间

323

我如何计算Java方法执行所需的时间?


1
哈哈,这是很多重复的 :D 可惜当你关闭一个问题时只能命名一个 :( - IAdapter
1
这是一个用于 Java 的秒表类。它将时间输出格式化为 .NET 的 Stopwatch 类。http://carlosqt.blogspot.com/2011/05/stopwatch-class-for-java.html - Carlos Quintanilla
你忘记了明确提到测量的目的,这可能会影响它应该如何进行。无论如何,这听起来像是你想要进行性能优化。在这种情况下,你应该一定要阅读有关“微基准测试”的内容。虽然System.nanoTime()方法非常简单,但是测量程序的性能并没有简单的方法,因为它取决于许多不同的因素(例如硬件、其他在同一系统上运行的软件、即时编译和热点编译、输入数据等)。 - user573215
这是我制作的一个秒表,使用起来非常简单。https://gist.github.com/juanmf/4147a9b7010c7b04c003 - juanmf
8个回答

318
为了更加精确,我会使用nanoTime()方法,而不是currentTimeMillis()方法:
long startTime = System.nanoTime();
myCall(); 
long stopTime = System.nanoTime();
System.out.println(stopTime - startTime);

在Java 8中(输出格式为ISO-8601):

Instant start = Instant.now();
Thread.sleep(63553);
Instant end = Instant.now();
System.out.println(Duration.between(start, end)); // prints PT1M3.553S

Guava 计时器:

Stopwatch stopwatch = Stopwatch.createStarted();
myCall();
stopwatch.stop(); // optional
System.out.println("Time elapsed: "+ stopwatch.elapsed(TimeUnit.MILLISECONDS));

18
你可能需要对结果进行格式化。对于任何有价值的纳秒时间,它将会有很多数字。 - Tom Hawtin - tackline
感谢 @Vitalii Fedorenko。如果该方法涉及大量的数据库事务,那么它是否会非常准确? - Nagappa L M
我不明白为什么数据库事务的数量会影响准确性,唯一需要记住的是数据库查询的时间将包括在总执行时间中。 - Vitalii Fedorenko
3
如果我没错的话,Instant.now()在内部使用System.currentTimeMillis()..但不建议这样使用。 - Kavindu Dodanduwa
1
更好的方法是运行JvisualVM。只需运行您的方法或类并启动jvisualVM。选择您的Java进程(如果在本地运行,它将列出Java进程)。转到采样器,监视CPU,在方法完成后拍摄快照。您可以在调用树中获得每个方法所花费的时间。它已经捆绑在JVM / JDK中。 - arvin_v_s
显示剩余4条评论

254

您可以在之前和之后获取时间戳快照,然后重复多次实验以平均结果。也有一些分析工具可以为您完成这项任务。


来自《Java平台性能:策略与技巧》书籍:

使用System.currentTimeMillis()

class TimeTest1 {
   public static void main(String[] args) {

      long startTime = System.currentTimeMillis();

      long total = 0;
      for (int i = 0; i < 10000000; i++) {
         total += i;
      }

      long stopTime = System.currentTimeMillis();
      long elapsedTime = stopTime - startTime;
      System.out.println(elapsedTime);
   }
}

使用StopWatch类

您可以使用这个StopWatch类,在方法之前调用start(),在方法之后调用stop()

class TimeTest2 {
   public static void main(String[] args) {

      Stopwatch timer = new Stopwatch().start();

      long total = 0;
      for (int i = 0; i < 10000000; i++) {
         total += i;
      }

      timer.stop();
      System.out.println(timer.getElapsedTime());
   }
}

请参见此处(已存档)。


NetBeans Profiler:

应用程序性能应用程序

性能概要文件方法级CPU性能(执行时间)。您可以选择对整个应用程序或应用程序的一部分进行分析。

请参见此处


4
在测试方法中,应该使用 currentTimeMillis 而不是 nanoTime。为了了解预热时间和结果之间的差异,通常会在外部加入循环以重复计时五次。请注意,您可能不希望在被测试的方法中包含计时器代码。 - Tom Hawtin - tackline
6
好的,我会尽力进行翻译,并注意不改变原意。需要翻译的内容是:“+1强调需要重复测试。” - Noel M
2
这不是与多线程平台相悖吗?怎么能确定呢?我认为在这里使用分析器会是更好的方法。 - Skynet
System.nanoTime(); 提供更精确的时间。 - hatranpro
回答于2010年,想要更新什么吗? - VedantK
@bakkal 有没有办法使用分析器来测量执行时间? - Gaurav

53

请检查:System.currentTimeMillis

有了这个,您可以通过以下方式计算方法的时间:

long start = System.currentTimeMillis();
class.method();
long time = System.currentTimeMillis() - start;

我只需要Java程序中特定方法执行所需的时间。 - Renuka
6
最好使用System.nanoTime(),因为它更准确。 - InsertNickHere
2
@Renuka:Functional的回答是否提供了您所需的内容? - BoltClock
《Effective Java 第二版》推荐使用 System.nanoTime()。 - juanmf

31

Android推出了一些实用工具,例如timeLogger和Textutils..感谢指出 :) - Mahendran
@JJD 有没有其他的分析工具,而不是使用这个类? - Gaurav
Android Studio内置了CPU分析器,您可以查看某些进程所需的时间。不确定这是否是您要寻找的内容。 - JJD

15
你可能想考虑面向方面的编程。你不希望在代码中散布时间记录。你希望能够声明式地开启和关闭它们。
如果你使用Spring,请看一下他们的MethodInterceptor类。

11
如果你正在编写应用程序,那么答案是使用System.currentTimeMillisSystem.nanoTime,正如上面的人所指出的那样。
但如果你已经编写了代码,并且不想更改它,最好使用Spring的方法拦截器。例如,您的服务是:
public class MyService { 
    public void doSomething() {
        for (int i = 1; i < 10000; i++) {
            System.out.println("i=" + i);
        }
    }
}
为了避免更改服务,您可以编写自己的方法拦截器:
public class ServiceMethodInterceptor implements MethodInterceptor {
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object result = methodInvocation.proceed();
        long duration = System.currentTimeMillis() - startTime;
        Method method = methodInvocation.getMethod();
        String methodName = method.getDeclaringClass().getName() + "." + method.getName();
        System.out.println("Method '" + methodName + "' took " + duration + " milliseconds to run");
        return null;
    }
}

还有一些针对Java的开源API可供使用,例如BTrace。 或者像@bakkal和@Saikikos建议的那样使用Netbeans分析器。 谢谢。


5

如所提出的nanoTime()在短时间尺度上非常精确。 当需要这种精度时,您需要注意您真正测量的内容。 特别是不要测量nanotime调用本身。

long start1 = System.nanoTime();
// maybe add here a call to a return to remove call up time, too.
// Avoid optimization 
long start2 = System.nanoTime();
myCall(); 
long stop = System.nanoTime();
long diff = stop - 2*start2 + start1;

System.out.println(diff + " ns");

顺便说一下,由于以下原因,您将为相同的调用测量不同的值:

  • 计算机上的其他负载(后台程序、网络、鼠标移动、中断、任务切换、线程)
  • 缓存填充(冷启动、热启动)
  • 即时编译(无优化、运行编译器导致性能下降、编译器带来的性能提升(但有时具有jit的代码比没有jit的代码更慢!))

1

Nanotime实际上甚至不适用于经过的时间,因为它比currentTimeMillis漂移得更多。此外,nanotime倾向于在精度方面提供过度的精确性,而牺牲准确性。因此,它高度不一致,并需要改进。

对于任何时间测量过程,currentTimeMillis(虽然几乎同样糟糕),在平衡准确性和精度方面表现更好。


2
currentTimeMillis不应用于计算经过的时间,因为它不是单调的(你可能会得到负经过的时间!)。请参见例如https://dev59.com/D3A85IYBdhLWcg3wBO3h - Henrik Nordvik

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