在Java中对本地方法进行分析 - 奇怪的结果

5
我一直在使用Yourkit 8.0对在Mac OS X(10.5.7,Apple JDK 1.6.0_06-b06-57)下运行的数学密集型应用程序进行剖析,并注意到CPU剖析结果中出现了一些奇怪的行为。
例如-我使用采样进行了一次剖析,报告说该应用程序10分钟的运行时间中有40%花费在StrictMath.atan方法上。我觉得这很令人困惑,但我相信它的话并花了一些时间用一个极其简单的多项式拟合替换了atan。
当我再次运行应用程序时,它的运行时间几乎与之前完全相同(10分钟)-但是我的atan替换在剖析结果中根本没有出现。相反,其他主要热点的运行时间百分比增加以弥补它的缺失。
总结一下:
使用StrictMath.atan(本地方法)的结果 总运行时间:10分钟 方法1:20% 方法2:20% 方法3:20% StrictMath.atan:40%
使用简化的纯Java atan的结果 总运行时间:10分钟 方法1:33% 方法2:33% 方法3:33%
(方法1,2,3不执行任何atan调用)
你知道这种行为背后的原因吗?我使用EJ-Technologies的JProfiler得到了相同的结果。似乎JDK剖析API在本地方法下报告不准确的结果,至少在OS X下是这样。

如果 atan 是内置的,而不是调用方法,我也不会感到惊讶 - 相应的机器代码将被嵌入行内。 - Tom Hawtin - tackline
我也在Mac OS X 10.7(以及之前的版本)上使用StrictMath的各种方法时遇到过这个问题。 - Seth Tisue
那么,这个问题有什么解决方案吗? - ziggystar
6个回答

3
这可能是由于采样时间不一致造成的。例如,如果一个方法使用了很长时间,但执行起来并不需要很长时间,那么采样可能会错过它。此外,我认为垃圾回收永远不会在采样时发生,但如果某些代码导致大量垃圾回收,则可能会显著降低速度,而不会出现在采样中。
在类似的情况下,我发现运行两次非常有帮助,一次进行跟踪,一次进行采样。如果一个方法同时出现在两个结果中,那么它可能正在大量使用CPU,否则它可能只是采样过程的产物。

0
我发现YourKit极度夸大了调用子方法的成本(由于其日志记录方法,我认为)。如果你只遵循配置文件给出的建议,你最终会合并函数而没有真正获得任何好处,因为HotSpot通常在这方面表现出色。
因此,我强烈建议完全在分析工具之外测试批处理,以更好地了解更改是否真正有益(这似乎很明显,但这让我浪费了一些开发时间)。

0

由于您使用的是Mac,您可以尝试Apple's Shark profiler(ADC免费下载),它支持Java,并且苹果的性能组已经花了相当多的时间来开发这个工具。

正如Nick所指出的那样,如果采样间隔接近函数的执行时间并且分析器很少检查函数实际执行的时间,则采样可能会误导。我不知道YourKit是否支持此功能,但在Shark中,您可以将采样间隔更改为默认值10ms以外的其他值,并查看结果是否有显著差异。 还有一个单独的调用跟踪模式,它将记录每个函数的进入/返回-这完全避免了别名错误的可能性,但收集了大量数据和更高的开销,如果您的应用程序正在进行任何实时处理,则可能会有影响。


0
您可能需要查看传递到三种方法中的参数。有可能耗费时间生成返回值或在创建大量临时对象的方法中。

0
值得注意的是,如果Java方法足够小,则可以将其内联,但本地方法则根据不同规则进行内联。如果方法被内联,则不会出现在分析器中(无论是YourKit还是其他)。

-1

想解释一下为什么这会导致与自动化此过程的采样分析器产生不同的结果? - ziggystar
@ziggystar:一些采样分析器自动化了一半的过程,即对整个堆栈进行采样,以墙钟(而不是CPU)时间为基础。他们没有自动化的另一半过程是发现可以修复的性能问题。如果语句或函数具有较小的包含百分比,则问题在其他地方,但这并不能很好地缩小问题所在的范围。仅仅知道一行代码、函数或“路径”是“热点”并不能告诉你太多。如果您可以完整地检查特定的代表性样本,那么它将更具启示性。... - Mike Dunlavey
@ziggystar:...这导致的不同结果是,即使使用非常好的分析器,您也会发现加速效果。当您修复这些问题并重复该过程时,以前很小的问题现在占程序时间的比例更大了。因此,您逐个解决这些问题,复合加速效果可能会令人惊讶。您曾经通过分析器获得多少加速效果?如果答案超过40%,那将令我感到惊讶。 - Mike Dunlavey
使用jvisualvm,优化了10倍以上的性能。不是一次,而是持续地(通常我都在优化高级语言Scala)。就在今天,我将我的一个程序从比优化过的C++库慢10倍以上的速度优化到了不到4倍的速度。我的代码已经相当优化了,但C++的性能也可以达到。至于其他方面:我遇到了与此处提到的采样分析器(jvisualvm)相同的问题。我也进行了手动堆栈转储。它们也支持本机方法。我想这是JVM堆栈转储的问题。 - ziggystar
@ziggystar:10倍已经很不错了。这里是43倍,以及这里是730倍。区别不在于获取样本,而在于获取样本后对其进行的处理。然后发现需要大量样本的需求并没有任何依据。统计学上的理由在这里讨论过。 - Mike Dunlavey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接