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

Posted

技术标签:

【中文标题】如何在 Java 中计时方法的执行?【英文标题】:How do I time a method's execution in Java? 【发布时间】:2010-09-15 20:40:14 【问题描述】:
    如何获取方法的执行时间? 是否有一个Timer 实用程序类用于计时任务需要多长时间等?

Google 上的大多数搜索都返回了用于安排线程和任务的计时器的结果,这不是我想要的。

【问题讨论】:

JAMon API 是一个免费、简单、高性能、线程安全的 Java API,它允许开发人员轻松监控生产应用程序的性能和可扩展性。 JAMon 跟踪命中、执行时间(总、平均、最小值、最大值、标准差)等。 http://jamonapi.sourceforge.net/ 下载:http://sourceforge.net/project/showfiles.php?group_id=96550 您可能还想查看Apache Commons Lang StopWatch 类。一个简单但有用的实用程序类。 后期类似问题:How do I write a correct micro-benchmark in Java? 是的,秒表非常适合。 Java 8 使用 Instant 类:***.com/a/30975902/1216775 【参考方案1】:

如果只是想知道时间,您可以尝试这种方式。

long startTime = System.currentTimeMillis();
//@ Method call
System.out.println("Total time [ms]: " + (System.currentTimeMillis() - startTime));    

【讨论】:

【参考方案2】:

在 Java 8 中引入了一个名为 Instant 的新类。根据文档:

Instant 表示时间线上一纳秒的开始。这 类对于生成表示机器时间的时间戳很有用。 瞬间的范围需要存储一个大于a的数字 长。为了实现这一点,该类存储了一个长表示 epoch-seconds 和一个表示 nanosecond-of-second 的 int,这将 始终介于 0 和 999,999,999 之间。测量纪元秒数 从 1970-01-01T00:00:00Z 的标准 Java 时代开始 在 epoch 之后有正值,并且更早的时刻有 负值。对于纪元秒和纳秒部分,a 在时间线上,较大的值总是比较小的值晚。

这可以用作:

Instant start = Instant.now();
try 
    Thread.sleep(7000);
 catch (InterruptedException e) 
    e.printStackTrace();

Instant end = Instant.now();
System.out.println(Duration.between(start, end));

它打印PT7.001S

【讨论】:

【参考方案3】:

如果java有更好的功能支持就好了,这样需要度量的动作可以被包裹到一个块中:

measure 
   // your operation here

在java中这可以通过匿名函数来完成,看起来太冗长了

public interface Timer 
    void wrap();



public class Logger 

    public static void logTime(Timer timer) 
        long start = System.currentTimeMillis();
        timer.wrap();
        System.out.println("" + (System.currentTimeMillis() - start) + "ms");
    

    public static void main(String a[]) 
        Logger.logTime(new Timer() 
            public void wrap() 
                // Your method here
                timeConsumingOperation();
            
        );

    

    public static void timeConsumingOperation() 
        for (int i = 0; i<=10000; i++) 
           System.out.println("i=" +i);
        
    

【讨论】:

可以通过使用 Java 8 的 lambda 表达式来清理。 drdobbs.com/jvm/lambda-expressions-in-java-8/240166764?pgno=2 确实,由于 Java 8,java.lang.Runnable@FunctionalInterface,这意味着您可以将 lambda 表达式传递给任何以 Runnable 作为参数的方法。您的timeThisCode(Runnable r) 可以简单地返回毫秒/纳米或更精细的经过时间的表示。【参考方案4】:

这里是漂亮的打印字符串准备好格式化的秒数,类似于谷歌搜索时间:

        long startTime = System.nanoTime();
        //  ... methodToTime();
        long endTime = System.nanoTime();
        long duration = (endTime - startTime);
        long seconds = (duration / 1000) % 60;
        // formatedSeconds = (0.xy seconds)
        String formatedSeconds = String.format("(0.%d seconds)", seconds);
        System.out.println("formatedSeconds = "+ formatedSeconds);
        // i.e actual formatedSeconds = (0.52 seconds)

【讨论】:

nonoTime 不是 /1000 秒。你的数学假设 getTime 是毫秒。最好做 /1e6 以获得毫秒。【参考方案5】:

我实现了一个简单的计时器,我认为它真的很有用:

public class Timer
    private static long start_time;

    public static double tic()
        return start_time = System.nanoTime();
    

    public static double toc()
        return (System.nanoTime()-start_time)/1000000000.0;
    


这样你就可以为一个或多个动作计时:

Timer.tic();
// Code 1
System.out.println("Code 1 runtime: "+Timer.toc()+" seconds.");
// Code 2
System.out.println("(Code 1 + Code 2) runtime: "+Timer.toc()+"seconds");
Timer.tic();
// Code 3
System.out.println("Code 3 runtime: "+Timer.toc()+" seconds.");

【讨论】:

【参考方案6】:

您可以使用javaagent来修改java类字节,动态添加监控代码。github上有一些开源工具可以为您做到这一点。 如果你想自己做,只需要实现javaagent,使用javassist修改你要监控的方法,在你的方法返回之前的监控代码。很干净并且您可以监控甚至没有源代码的系统。

【讨论】:

【参考方案7】:

System.nanoTime() 是一个非常精确的系统实用程序,用于测量执行时间。但请注意,如果您在抢占式调度程序模式(默认)下运行,此实用程序实际上测量的是挂钟时间而不是 CPU 时间。因此,您可能会注意到每次运行的执行时间值不同,具体取决于系统负载。如果您寻找 CPU 时间,我认为以实时模式运行您的程序就可以了。你必须使用 RT linux。链接:Real-time programming with Linux

【讨论】:

【参考方案8】:

在 java ee 中对我有用的策略是:

    @AroundInvoke注解的方法创建一个类;

    @Singleton
    public class TimedInterceptor implements Serializable 
    
        @AroundInvoke
        public Object logMethod(InvocationContext ic) throws Exception 
            Date start = new Date();
            Object result = ic.proceed();
            Date end = new Date();
            System.out.println("time: " + (end.getTime - start.getTime()));
            return result;
        
    
    

    注释你要监控的方法:

    @Interceptors(TimedInterceptor.class)
    public void onMessage(final Message message)  ... 
    

我希望这会有所帮助。

【讨论】:

【参考方案9】:

对于 java 8+,另一种可能的解决方案(更通用,func 样式且没有方面)可能是创建一些实用方法,接受代码作为参数

public static <T> T timed (String description, Consumer<String> out, Supplier<T> code) 
    final LocalDateTime start = LocalDateTime.now ();
    T res = code.get ();
    final long execTime = Duration.between (start, LocalDateTime.now ()).toMillis ();
    out.accept (String.format ("%s: %d ms", description, execTime));
    return res;

调用代码可能是这样的:

public static void main (String[] args) throws InterruptedException 
    timed ("Simple example", System.out::println, Timing::myCode);


public static Object myCode () 
    try 
        Thread.sleep (1500);
     catch (InterruptedException e) 
        e.printStackTrace ();
    
    return null;

【讨论】:

【参考方案10】:

也可以实现 Timer 接口并在你的类的任何方法上执行

import java.util.function.*;

public interface Timer 

    default void timeIt(Runnable r) 
        timeIt(() ->  r.run(); return 0;);
    

    default <S,T> T timeIt(Function<S,T> fun, S arg) 
        long start = System.nanoTime();
        T result = fun.apply(arg);
        long stop = System.nanoTime();
        System.out.println("Time: " + (stop-start)/1000000.0 + " msec");
        return result;
    

    default <T> T timeIt(Supplier<T> s) 
        return timeIt(obj -> s.get(), null);
    

用法:

class MyClass implements Timer ..

timeIt(this::myFunction); 

【讨论】:

【参考方案11】:

这里有很多有效的答案,所有这些都是在方法中实现的。为了制作一个通用的计时方法,我通常有一个 Timing 类,包含以下内容。

public record TimedResult<T>(T result, Duration duration) 

public static Duration time(Runnable r) 
    var s = Instant.now();
    r.run();
    var dur = Duration.between(s, Instant.now());
    return dur;


public static <T> TimedResult<T> time(Callable<T> r) throws Exception 
    var s = Instant.now();
    T res = r.call();
    var dur = Duration.between(s, Instant.now());
    return new TimedResult<>(res, dur);

这足以传递Runnable 或Callable。

Duration result = Timing.time(() -> 
    // do some work.
);

TimedResult<String> result = Timing.time(() -> 
    // do some work.
    return "answer";
);

Duration timeTaken = result.duration();
String answer = result.result();

【讨论】:

【参考方案12】:

纯Java SE代码,无需添加依赖,使用TimeTracedExecuter

public static void main(String[] args) 

    Integer square = new TimeTracedExecutor<>(Main::calculateSquare)
                .executeWithInput("calculate square of num",5,logger);


public static int calculateSquare(int num)
    return num*num;

会产生这样的结果:

INFO: It took 3 milliseconds to calculate square of num

自定义可重用类:TimeTracedExecutor

import java.text.NumberFormat;
import java.time.Duration;
import java.time.Instant;
import java.util.function.Function;
import java.util.logging.Logger;

public class TimeTracedExecutor<T,R> 
    Function<T,R> methodToExecute;

    public TimeTracedExecutor(Function<T, R> methodToExecute) 
        this.methodToExecute = methodToExecute;
    

    public R executeWithInput(String taskDescription, T t, Logger logger)
        Instant start = Instant.now();
        R r= methodToExecute.apply(t);
        Instant finish = Instant.now();
        String format = "It took %s milliseconds to "+taskDescription;
        String elapsedTime = NumberFormat.getNumberInstance().format(Duration.between(start, finish).toMillis());
        logger.info(String.format(format, elapsedTime));
        return r;
    

【讨论】:

以上是关于如何在 Java 中计时方法的执行?的主要内容,如果未能解决你的问题,请参考以下文章

如何等到计时器在java中停止?

java启用一个倒计时任务

如何在java中的单行代码上设置计时器

如何计算Java中操作的耗时?

微信小程序长时间计时方法

如何使用 Timer 类调用方法,做某事,重置计时器,重复?