【问题标题】:What's a clean way to time code execution in Java?什么是在 Java 中计时代码执行的干净方法?
【发布时间】:2019-03-27 13:15:47
【问题描述】:

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

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 中的代码执行计时,您可以清楚地看到正在计时的内容,还是我只需要满足于我的第一个代码块之类的事情。

【问题讨论】:

  • 您应该使用System.nanoTime() 而不是System.currentTimeMillis() (Link)。
  • @Henry 嗨亨利,我认为这不是重复的,考虑到它与微基准测试无关,因为我不想测试代码运行的快慢。但它更多的是关于记录,记录到文件或控制台,以指示生产中发生的事情有多快。
  • 在应用程序容器中,您可以有一个方法 interceptor 注释来触发性能测量。
  • @RyanTheLeach 再说一次,我认为不是,这个问题是关于 如何 时间码,这个问题是关于时间码的更清洁、更易读的方式,这个问题已经表示知道如何计时码。
  • 对于它的价值,我没有举报。一个紧密重复而不是重复。 (也许近乎重复会更好)

标签: java timing autocloseable


【解决方案1】:

我认为问题文本中建议的解决方案过于间接和非惯用,不适合生产代码。 (但它看起来像是一个在开发过程中快速获取时间的简洁工具。)

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

即使不使用 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");
}

【讨论】:

    【解决方案2】:

    你的解决方案很好。

    表达力较差的方法是将代码包装在 lambda 中以进行计时。

    public void timeCode(Runnable code) {
        ...
        try {
            code.run();
        } catch ...
        }
        ...
    }
    
    timeCode(() -> { ...code to time... });
    

    您可能希望捕获已检查的异常并将它们传递给某个运行时异常或其他什么。

    【讨论】:

    • 我真的很喜欢这个解决方案,您的评论/编辑意味着您添加了类似throw new RuntimeException(e) 的内容,e 是被捕获的异常,对吧?
    • @Mark 是的,已经有类似的例外用于流,但我不记得是哪一个。而不是 RuntimeException 我倾向于 IllegalStateException 之类的。
    【解决方案3】:

    你的方法很好。我们在专业上使用类似的东西,但用 C# 编写。

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

    我将考虑的其他改进是创建一些静态应用程序状态(滥用线程本地),以便您可以嵌套这些部分,并进行汇总细分。

    请参阅 https://github.com/aikar/minecraft-timings 以获取为我的世界模组(用 java 编写)执行此操作的库。

    【讨论】:

    • 是的,如果我要走这条路,那我就是这样,这只是一个最小的例子。所以创建一个新的对象实例只是你做的事情吗?
    • Java 的转义分析通常非常好,并且很少会为似乎需要足够长的时间才能保证被测量的东西创建单个对象,并且输出应该是性能考虑而不是更好的语法.也就是说,您可以只使用一对启动/停止静态方法来减少分配,但这很容易出错。
    猜你喜欢
    • 2014-01-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-16
    相关资源
    最近更新 更多