在Java中计时代码执行的一种干净方法是什么?

时间:2018-10-23 10:23:24

标签: java timing autocloseable

时间执行代码可能很方便,因此您知道需要花费多长时间。但是,我发现这样做的普遍做法很草率,因为它应该具有相同的缩进,这使得读取实际计时内容变得更加困难。

long start = System.nanoTime();

// The code you want to time

long end = System.nanoTime();
System.out.printf("That took: %d ms.%n", TimeUnit.NANOSECONDS.toMillis(end - start));

尝试

我想出了以下几点,它看起来更好,有一些优点和缺点:

优势:

  • 很明显,由于缩进,正在计时什么
  • 它将自动打印代码完成后花费多长时间

缺点:

  • 这不是应该使用AutoClosable的方式(很确定)
  • 它会创建一个不好的TimeCode新实例
  • try块中声明的变量无法在其外部访问

可以这样使用:

 try (TimeCode t = new TimeCode()) {
     // The stuff you want to time
 }

使之成为可能的代码是:

class TimeCode implements AutoCloseable {

    private long startTime;

    public TimeCode() {
        this.startTime = System.nanoTime();
    }

    @Override
    public void close() throws Exception {
        long endTime = System.nanoTime();
        System.out.printf("That took: %d ms%n",
                TimeUnit.NANOSECONDS.toMillis(endTime - this.startTime));
    }

}

问题

我的问题是:

  • 我的方法实际上是否像我认为的那样糟糕
  • 是否有更好的方法来计时Java中的代码执行,您可以清楚地看到正在计时的时间,或者我只需要满足第一个代码块之类的要求。

4 个答案:

答案 0 :(得分:3)

您的解决方案就可以了。

一种不太具表现力的方法是将要计时的代码包装在lambda中。

public void timeCode(Runnable code) {
    ...
    try {
        code.run();
    } catch ...
    }
    ...
}

timeCode(() -> { ...code to time... });

您可能想捕获已检查的异常并将它们传递给某个运行时异常或其他任何东西。

答案 1 :(得分:1)

您的方法原样很棒。我们在专业上使用类似的东西,但是都是用C#编写的。

我可能要添加的一件事是适当的日志记录支持,以便您可以切换这些性能数字,或者将其设置为调试或信息级别。

我要考虑的其他改进是创建一些静态应用程序状态(滥用线程局部变量),以便您可以嵌套这些部分并进行摘要。

请参见https://github.com/aikar/minecraft-timings,了解用于Minecraft改装(用Java编写)的库。

答案 2 :(得分:1)

我认为问题文本中建议的解决方案是太间接和非惯用语以至于不适合生产代码。 (但是,它看起来像是一种精巧的工具,可以在开发过程中快速地进行工作安排。)

Guava projectApache Commons都包含秒表类。如果您使用它们中的任何一个,代码将更易于阅读和理解,并且这些类也具有更多的内置功能。<​​/ p>

即使不使用try-with-resource语句,也可以将要测量的部分封闭在一个块中,以提高清晰度:

// Do things that shouldn't be measured

{
    Stopwatch watch = Stopwatch.createStarted();

    // Do things that should be measured

    System.out.println("Duration: " + watch.elapsed(TimeUnit.SECONDS) + " s");
}

答案 3 :(得分:-1)

写下每个方面,而不是每次都调用性能方法。

@Pointcut("(execution(* com.resources.delegate.*.*(..)) "
        + "|| execution(* com.resources.implementation.*.*(..))) ")
public void resourceLayerPointCut() {
}

@Pointcut("(execution(* com.models.delegate.*.*(..)) "
        + "|| execution(* com.repo.implementation.*.*(..))) ")
public void exclusionsPointcut() {
}

周围是您的方法上写的建议,它将同时打印开始时间和结束时间。您不必每次都调用它。

@Around(value = "resourceLayerPointCut() && exclusionsPointcut()")
public Object aroundResourceLayerMethods(ProceedingJoinPoint code) throws Throwable {
    long startTime = System.currentTimeMillis();
    Object returnValue = null;
  try {

        //This will call your methods

        returnValue = code.proceed();
     } catch (Throwable e) {
        log(joinPoint, startTime, returnValue, Layer.ResourceLayer, true);
        throw (e);
    }

    long endTime = System.currentTimeMillis();
    System.out.printf("That took: %d ms%n", endTime - this.startTime);
    return returnValue;

}