探查器可以更改递归调用在 Java 中运行的时间吗?

Posted

技术标签:

【中文标题】探查器可以更改递归调用在 Java 中运行的时间吗?【英文标题】:Can a profiler change how long recursive calls take to run in Java? 【发布时间】:2011-01-28 22:28:11 【问题描述】:

我正在用 Java 重构一些代码,所以我正在计时以确保代码不会变慢。但是,新的重构代码似乎比原始代码花费了更多的时间。值得注意的是,当我使用分析器运行代码时,新代码比旧代码快得多。主要区别在于旧代码是递归的,而新代码是迭代的。分析器能否对递归代码产生数十万倍的影响,而对迭代代码的影响仅为 1.5 倍?

我在 Mac OS X 10.6.6、3 GB RAM、2.4 GHz CPU 上运行,使用带有 Java 1.6.0__22 64 位服务器的默认 Netbeans 6.9 分析器。

(这两种方法都有使用 System.currentTimeMillis() 的自计时代码,让我可以在不使用分析器时比较时间,但这不会显着影响事情。)

【问题讨论】:

您使用的是什么分析器?探查器是否使您的代码在与正常情况不同的 JVM 中运行? @Andrew Netbeans 默认,但我不知道它是否使用不同的 JVM(不过我怀疑它确实如此)。 【参考方案1】:

是的。大多数配置文件在方法调用级别进行检测。在递归形式中,分析器必须进行比迭代形式更多的测量。虽然分析器确实试图从报告的数字中提取他们的开销,但这很难做到可靠。不同的分析器在这方面会更好/更差。

【讨论】:

【参考方案2】:

我正在用 Java 重构一些代码,所以我正在计时以确保代码不会变慢。但是,新的重构代码似乎比原始代码花费了更多的时间。

是的。代码通常在分析器下运行较慢。

因此,您应该比较应用程序的旧版本/新版本的时间,要么都在分析器下运行,要么都正常运行。

还要注意,分析器实际上可以distort performance characteristics。并且不同的分析器可能不同意代码热点的位置。因此,在采用您正在试用的优化之前,最好在不进行分析的情况下运行/比较应用程序的版本。


(这两种方法都有使用 System.currentTimeMillis() 的自计时代码,让我可以在不使用分析器时比较时间,但这不会显着影响事情。)

这里也有陷阱:

currentTimeMillis() 的最佳粒度是 1 毫秒,但在某些操作系统上可能是几十毫秒。

如果您不采取措施避免这种情况,您的手动计时可能包括扭曲的开销,例如 JIT 编译时间和 GC 时间。效果可能非常微妙。

【讨论】:

我认为这是分析器扭曲性能特征的一个典型例子。 考虑使用通常至少精确到微秒的 nanoTime() 而不是 currentTimeMillis()。【参考方案3】:

我想说,如果你想衡量速度,就只衡量速度,不要分析。他们不是一回事。检测分析器会在每个函数调用中增加大量开销,如果您想要的只是整体速度差异,那么它是不准确的,因为您部分地测量了检测本身的成本。

如果您想了解什么在花费时间,这与衡量是不同的。报告行级百分比的挂钟时间堆栈采样分析器(不是仪器)是您最好的选择。它是否会减慢程序的速度并不重要,因为它的目的不是测量速度;它的目的是找出时间的去向和原因,以百分比为基础。如果它或其他东西将程序减慢 10% 或 10 倍,那就没问题了,如果它显示您在哪里花费了时间,与速度无关。

我之所以这么说,是因为很多人对这一点感到困惑,而这种困惑被固化到了很多分析器中。

More on that subject.

【讨论】:

以上是关于探查器可以更改递归调用在 Java 中运行的时间吗?的主要内容,如果未能解决你的问题,请参考以下文章

当我不创建任何 char[] 实例时,为啥探查器会显示大量实例?

PHP:在运行时更改 xdebug.profiler_output_name 选项

Java Eclipse RCP 探查器

如何检测探查器正在运行?

如何从 .NET 应用程序收集探查器数据

如何知道 Python 编程是不是在探查器下运行?