【问题标题】:How can i benchmark method execution time in java?我如何在java中对方法执行时间进行基准测试?
【发布时间】:2011-02-02 22:41:18
【问题描述】:

我有一个自己用 java 编写的程序,但我想测试方法执行时间并获取特定方法的时间。我想知道这是否可能,通过某种方式的 Eclipse 插件?或者可能插入一些代码?

我明白了,这是一个很小的程序,不超过1500行,最好是专用工具还是System.currentTimeMillis()

【问题讨论】:

  • 您应该使用System.nanoTime() 来测量经过的时间(请参阅stackoverflow.com/questions/238920/…),而不是System.currentTimeMillis()。 @Stephen 在评论中提到的其他点(热身、JIT)仍然有效。
  • 对这个任务使用秒表类

标签: java optimization


【解决方案1】:

除了使用分析器之外,获得所需内容的简单方法如下:

public class SomeClass{
   public void somePublicMethod()
   {
       long startTime = System.currentTimeMillis();
       someMethodWhichYouWantToProfile();
       long endTime = System.currentTimeMillis();
       System.out.println("Total execution time: " + (endTime-startTime) + "ms"); 
   }
 }

【讨论】:

  • @Stephen C - 公平点。以上是我在尝试快速了解方法效率时主要使用的方法。在大多数情况下,我的要求不需要单毫秒精度。
  • 我可能想在此答案中添加您可以考虑热身的内容。通过使用-XX:CompileThreshold=100 标志,您可以强制JVM 在执行100 次时将您的方法编译为本机代码。所以你可以在没有时间的情况下执行 100 次。之后,它已被编译为本机代码,您可以正确测量它。
  • 有没有办法补偿调用方法所花费的时间?例如,我不想计算复制参数和给出返回地址等所需的时间。
  • 这是在 Java 中测量小段代码执行时间的最糟糕的方法之一,因为 (i) 不会预热代码,导致测量值波动很大,以至于无法有效测量任何内容(ii) 可能会受到 DCE、CP 和实际代码中不存在的其他优化的影响 (iii) 毫秒的粒度非常粗糙。检查我的答案以获得更好的方法。
  • @ElMarce 的回答在现代语境中显然更胜一筹。
【解决方案2】:

如果瓶颈大到足以使用探查器观察到,请使用探查器。

如果您需要更高的准确性,测量一小段代码的最佳方法是使用 Java 微基准框架,例如 OpenJDK's JMHGoogle's Caliper。我相信它们与 JUnit 一样易于使用,不仅您将获得更准确的结果,而且您​​还将获得社区的专业知识来正确执行此操作。

按照 JMH 微基准测量 Math.log() 的执行时间:

private double x = Math.PI;

@Benchmark
public void myBenchmark() {
    return Math.log(x)
}

使用currentMillis()nanoTime() 进行测量有很多限制:

  1. 它们存在延迟(它们也需要时间来执行),这会影响您的测量。
  2. 它们的精度非常有限,这意味着您可以在 linux 中测量从 26ns 到 26ns 的东西,在 Windows 中可以测量到 300 左右,已描述here
  3. 未考虑预热阶段,导致您的测量值波动很大。

Java 中的currentMillis()nanoTime() 方法很有用,但必须与极度小心 一起使用,否则您可能会得到错误的测量错误like this,其中测量的sn-ps 的顺序影响测量或like this 作者错误地得出结论,在不到一毫秒的时间内执行了数百万次操作,而实际上 JMV 没有意识到任何操作并提升了代码,根本没有运行任何代码。

这是一个精彩的视频,解释了如何正确地进行微基准测试:https://shipilev.net/#benchmarking

【讨论】:

    【解决方案3】:

    对于快速而肮脏的时间测量测试,不要使用挂钟时间 (System.currentTimeMillis())。更喜欢System.nanoTime()

    public static void main(String... ignored) throws InterruptedException {
        final long then = System.nanoTime();
        TimeUnit.SECONDS.sleep(1);
        final long millis = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - then);
        System.out.println("Slept for (ms): " + millis); // = something around 1000.
    }
    

    【讨论】:

      【解决方案4】:

      你应该使用类似的分析器

      它们将轻松与任何 IDE 集成并显示您需要的任何细节。

      当然,这些工具很复杂,旨在用于分析复杂的程序,如果您只需要一些简单的基准测试,我建议您使用System.currentTimeMillis()System.nanoTime() 并自己计算调用之间的毫秒增量。

      【讨论】:

      • 我明白了,这是一个很小的程序,不超过1500行,最好是专用工具还是System.currentTimeMillis()?
      • 如果您只需要检查某些方法的执行速度,请使用 System.currentTimeMillis(),但请注意它根本不精确!您可以量化十分之一毫秒和类似问题。分析是完成您需要做的事情的最佳方式,但它需要一点学习。
      【解决方案5】:

      使用分析器会更好,因为您可以找出应用中的平均执行时间和瓶颈。

      我使用VisualVM。光滑而简单。

      【讨论】:

        【解决方案6】:

        Google Guava has a stopwatch,让事情变得简单易行。

        Stopwatch stopwatch = Stopwatch.createStarted();
        myFunctionCall();
        LOGGER.debug("Time taken by myFunctionCall: " + stopwatch.stop());
        

        【讨论】:

        • 好的,但这有多准确?
        【解决方案7】:

        Jprofiler 和 yourkit 都不错,但要花钱。

        eclispe 有一个名为 TPTP(测试和性能工具平台)的免费插件,它可以为您提供代码执行时间。这是一个快速谷歌搜索带来的教程。 http://www.eclipse.org/articles/Article-TPTP-Profiling-Tool/tptpProfilingArticle.html

        【讨论】:

          【解决方案8】:

          您可以添加此代码,它会告诉您执行该方法需要多长时间。

          long millisecondsStart = System.currentTimeMillis();
          executeMethod();
          long timeSpentInMilliseconds = System.currentTimeMillis() - millisecondsStart;
          

          【讨论】:

          • 三个问题。 1)毫秒时间可以量化;例如到 20ms 的粒度。 2)你实际上是在测量executeMethod()的时间+System.currentTimeMillis()的时间。 3) 这不考虑 JVM 预热效应;例如JIT 编译。
          【解决方案9】:

          另一个定制解决方案可以基于以下帖子:http://www.mkyong.com/spring/spring-aop-examples-advice/

          您还可以使用有关应用程序监控和 snmp 的实用程序。如果您需要在生产环境中定期“计时”您的方法,您可能应该考虑使用其中一种 SNMP 工具

          【讨论】:

            【解决方案10】:

            通常我将时间存储在 .txt 文件中以分析结果

            StopWatch sWatch = new StopWatch();
            sWatch.start();
            
            //do stuff that you want to measure
            downloadContent();
            
            sWatch.stop();
            
            //make the time pretty
            long timeInMilliseconds = sWatch.getTime();
            long hours = TimeUnit.MILLISECONDS.toHours(timeInMilliseconds);
            long minutes = TimeUnit.MILLISECONDS.toMinutes(timeInMilliseconds - TimeUnit.HOURS.toMillis(hours));
            long seconds = TimeUnit.MILLISECONDS.toSeconds(timeInMilliseconds - TimeUnit.HOURS.toMillis(hours) - TimeUnit.MINUTES.toMillis(minutes));
            long milliseconds = timeInMilliseconds - TimeUnit.HOURS.toMillis(hours) - TimeUnit.MINUTES.toMillis(minutes) - TimeUnit.SECONDS.toMillis(seconds);
            
            String t = String.format("%02d:%02d:%02d:%d", hours, minutes, seconds, milliseconds);
            
            //each line to store in a txt file, new line
            String content = "Ref: " + ref + "  -  " + t + "\r\n";
            
            //you may want wrap this section with a try catch
            File file = new File("C:\\time_log.txt");
            FileWriter fw = new FileWriter(file.getAbsoluteFile(), true); //append content set to true, so it does not overwrite existing data
            BufferedWriter bw = new BufferedWriter(fw);
            bw.write(content);
            bw.close();
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2014-09-20
              • 1970-01-01
              • 1970-01-01
              • 2023-03-13
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多