如何在Java中计算方法的执行时间?

1018
  1. 如何获取方法的执行时间?
  2. 是否有一个Timer实用程序类来计算任务所需的时间等?

大多数在Google上搜索的结果都是关于调度线程和任务的定时器,这不是我想要的。


JAMon API是一个免费、简单、高性能、线程安全的Java API,允许开发人员轻松监控生产应用程序的性能和可扩展性。JAMon跟踪点击次数、执行时间(总计、平均值、最小值、最大值、标准偏差)等等。http://jamonapi.sourceforge.net/下载: http://sourceforge.net/project/showfiles.php?group_id=96550 - Mike Pone
2
你可能还想看看Apache Commons Lang StopWatch类。一个简单但实用的工具类。 - user101561
类似的问题:如何在Java中编写正确的微基准测试? - Basil Bourque
是的,StopWatch非常适合这个。 - Shubham Pandey
Java 8使用Instant类:https://dev59.com/InVC5IYBdhLWcg3wz0h9#30975902 - akhil_mittal
42个回答

19

JEP 230: 微基准测试套件

值得注意的是,JEP 230:微基准测试套件是一个OpenJDK项目,旨在:

向 JDK 源代码添加一组基本的微基准测试,并使开发人员能够轻松运行现有的微基准测试和创建新的测试。

这个功能已经在Java 12中实现。

Java 微基准测试工具(JMH)

对于早期版本的 Java,请查看Java 微基准测试工具(JMH)项目,JEP 230 是基于它实现的。


15

如果您不使用工具并想计时执行时间较短的方法:多次执行,每次将执行次数加倍,直到达到一秒左右。因此,System.nanoTime的调用时间等以及System.nanoTime的准确性并不会对结果产生太大影响。

    int runs = 0, runsPerRound = 10;
    long begin = System.nanoTime(), end;
    do {
        for (int i=0; i<runsPerRound; ++i) timedMethod();
        end = System.nanoTime();
        runs += runsPerRound;
        runsPerRound *= 2;
    } while (runs < Integer.MAX_VALUE / 2 && 1000000000L > end - begin);
    System.out.println("Time for timedMethod() is " + 
        0.000000001 * (end-begin) / runs + " seconds");

当然,关于使用墙钟的注意事项也适用:JIT编译、多个线程/进程的影响等。因此,您首先需要执行该方法“很多次”,以便JIT编译器完成其工作,然后多次重复此测试并记录最短的执行时间。


15
我们正在使用AspectJ和Java注解来实现这个目的。如果我们需要知道一个方法的执行时间,我们只需对其进行注释。更高级的版本可以使用自己的日志级别,在运行时可以启用和禁用它。
public @interface Trace {
  boolean showParameters();
}

@Aspect
public class TraceAspect {
  [...]
  @Around("tracePointcut() && @annotation(trace) && !within(TraceAspect)")
  public Object traceAdvice ( ProceedingJintPoint jP, Trace trace ) {

    Object result;
    // initilize timer

    try { 
      result = jp.procced();
    } finally { 
      // calculate execution time 
    }

    return result;
  }
  [...]
}

12

非常优秀的代码。

http://www.rgagnon.com/javadetails/java-0585.html

import java.util.concurrent.TimeUnit;

long startTime = System.currentTimeMillis();
........
........
........
long finishTime = System.currentTimeMillis();

String diff = millisToShortDHMS(finishTime - startTime);


  /**
   * converts time (in milliseconds) to human-readable format
   *  "<dd:>hh:mm:ss"
   */
  public static String millisToShortDHMS(long duration) {
    String res = "";
    long days  = TimeUnit.MILLISECONDS.toDays(duration);
    long hours = TimeUnit.MILLISECONDS.toHours(duration)
                   - TimeUnit.DAYS.toHours(TimeUnit.MILLISECONDS.toDays(duration));
    long minutes = TimeUnit.MILLISECONDS.toMinutes(duration)
                     - TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(duration));
    long seconds = TimeUnit.MILLISECONDS.toSeconds(duration)
                   - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(duration));
    if (days == 0) {
      res = String.format("%02d:%02d:%02d", hours, minutes, seconds);
    }
    else {
      res = String.format("%dd%02d:%02d:%02d", days, hours, minutes, seconds);
    }
    return res;
  }

3
实际上问题是如何计算一个方法所需的时间,而不是如何格式化它。不过这个问题相当古老了(将近四年了!)。请尽量避免唤起旧的帖子,除非回复可以在现有回复基础上添加新的和重要的信息。 - Leigh
1
并将剩余的毫秒数添加到结尾,进行以下更改:long millis = TimeUnit.MILLISECONDS.toMillis(duration) - TimeUnit.SECONDS.toMillis(TimeUnit.MILLISECONDS.toSeconds(duration)); if (days == 0) { res = String.format("%02d:%02d:%02d.%02d", hours, minutes, seconds, millis); } else { res = String.format("%dd%02d:%02d:%02d.%02d", days, hours, minutes, seconds, millis); } - Rick Barkhouse

12

Spring提供了一个实用类org.springframework.util.StopWatch,根据JavaDoc:

Simple stop watch, allowing for timing of a number of tasks, exposing total running time and running time for each named task.

用法:

StopWatch stopWatch = new StopWatch("Performance Test Result");

stopWatch.start("Method 1");
doSomething1();//method to test
stopWatch.stop();

stopWatch.start("Method 2");
doSomething2();//method to test
stopWatch.stop();

System.out.println(stopWatch.prettyPrint());

输出:

StopWatch 'Performance Test Result': running time (millis) = 12829
-----------------------------------------
ms     %     Task name
-----------------------------------------
11907  036%  Method 1
00922  064%  Method 2

有关方面:

@Around("execution(* my.package..*.*(..))")
public Object logTime(ProceedingJoinPoint joinPoint) throws Throwable {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    Object retVal = joinPoint.proceed();
    stopWatch.stop();
    log.info(" execution time: " + stopWatch.getTotalTimeMillis() + " ms");
    return retVal;
}

这个能和AspectJ一起使用吗? - zygimantus

10

我编写了一种方法,可以以更易读的形式打印方法的执行时间。例如,计算100万的阶乘大约需要9分钟。因此,执行时间将被打印为:

Execution Time: 9 Minutes, 36 Seconds, 237 MicroSeconds, 806193 NanoSeconds

代码在这里:

public class series
{
    public static void main(String[] args)
    {
        long startTime = System.nanoTime();

        long n = 10_00_000;
        printFactorial(n);

        long endTime = System.nanoTime();
        printExecutionTime(startTime, endTime);

    }

    public static void printExecutionTime(long startTime, long endTime)
    {
        long time_ns = endTime - startTime;
        long time_ms = TimeUnit.NANOSECONDS.toMillis(time_ns);
        long time_sec = TimeUnit.NANOSECONDS.toSeconds(time_ns);
        long time_min = TimeUnit.NANOSECONDS.toMinutes(time_ns);
        long time_hour = TimeUnit.NANOSECONDS.toHours(time_ns);

        System.out.print("\nExecution Time: ");
        if(time_hour > 0)
            System.out.print(time_hour + " Hours, ");
        if(time_min > 0)
            System.out.print(time_min % 60 + " Minutes, ");
        if(time_sec > 0)
            System.out.print(time_sec % 60 + " Seconds, ");
        if(time_ms > 0)
            System.out.print(time_ms % 1E+3 + " MicroSeconds, ");
        if(time_ns > 0)
            System.out.print(time_ns % 1E+6 + " NanoSeconds");
    }
}

我认为你只是错过了一个时间单位。从秒开始的下一个单位是毫秒而不是微秒。 - Wilson Barbosa

10

你可以使用Perf4j,这是一个非常酷的工具。使用起来很简单。

String watchTag = "target.SomeMethod";
StopWatch stopWatch = new LoggingStopWatch(watchTag);
Result result = null; // Result is a type of a return value of a method
try {
    result = target.SomeMethod();
    stopWatch.stop(watchTag + ".success");
} catch (Exception e) {
    stopWatch.stop(watchTag + ".fail", "Exception was " + e);
    throw e; 
}

详细信息可在开发者指南中找到。

编辑:项目似乎已经停滞不前


1
Perf4j还可以生成漂亮的统计数据 - Paaske

10

在Spring框架中,我们有一个称为StopWatch(org.springframework.util.StopWatch)的调用。

//measuring elapsed time using Spring StopWatch
        StopWatch watch = new StopWatch();
        watch.start();
        for(int i=0; i< 1000; i++){
            Object obj = new Object();
        }
        watch.stop();
        System.out.println("Total execution time to create 1000 objects in Java using StopWatch in millis: "
                + watch.getTotalTimeMillis());

从文档中得知:这个类通常用于验证概念证明和开发过程中的性能,而不是作为生产应用程序的一部分。 - q99
@q99 对的,这种逻辑我们不会放到生产环境中,在将其移入生产环境之前我们需要进行测试。 - Bhaskara Arani
它在底层使用了System.nanoTime(),这不是一个好的选择(System.currentTimeMillis()也一样),请参考@TondaCZE的答案。 - Eboubaker

8
您可以使用 Metrics库,该库提供各种测量工具。添加依赖项:
<dependencies>
    <dependency>
        <groupId>io.dropwizard.metrics</groupId>
        <artifactId>metrics-core</artifactId>
        <version>${metrics.version}</version>
    </dependency>
</dependencies>

请配置您的环境。
方法可以使用@Timed进行注释:
@Timed
public void exampleMethod(){
    // some code
}

或者包含 Timer 的代码片段:

final Timer timer = metricsRegistry.timer("some_name");
final Timer.Context context = timer.time();
// timed code
context.stop();

聚合指标可以导出到控制台、JMX、CSV或其他文件格式。
@Timed指标输出示例:
com.example.ExampleService.exampleMethod
             count = 2
         mean rate = 3.11 calls/minute
     1-minute rate = 0.96 calls/minute
     5-minute rate = 0.20 calls/minute
    15-minute rate = 0.07 calls/minute
               min = 17.01 milliseconds
               max = 1006.68 milliseconds
              mean = 511.84 milliseconds
            stddev = 699.80 milliseconds
            median = 511.84 milliseconds
              75% <= 1006.68 milliseconds
              95% <= 1006.68 milliseconds
              98% <= 1006.68 milliseconds
              99% <= 1006.68 milliseconds
            99.9% <= 1006.68 milliseconds

8

通过使用AOP/AspectJ和@Loggable注解,以及jcabi-aspects库,您可以轻松简洁地完成它:

@Loggable(Loggable.DEBUG)
public String getSomeResult() {
  // return some value
}

每次调用此方法都将以DEBUG日志级别发送到SLF4J日志记录设施。并且每个日志消息都将包含执行时间。

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