Java - 调用静态方法与手动内联 - 性能开销

2022-09-04 08:06:08

我感兴趣的是,我是否应该手动内联小方法,这些方法在某些性能敏感的算法中称为100k - 100万次。

首先,我认为,如果不内联,我会产生一些开销,因为JVM必须找到是否内联此方法(甚至无法这样做)的确定。

但是,前几天,我用静态方法的调用替换了这个手动内联的代码,并看到了性能的提升。这怎么可能?这是否表明实际上没有开销,并且通过让JVM以“其意愿”内联实际上提高了性能?或者这在很大程度上取决于平台/架构?

(发生性能提升的示例是将数组交换 () 替换为静态方法调用 。另一个没有性能差异的例子是,当我内联了一个10行方法,它被调用了1000000次。int t = a[i]; a[i] = a[j]; a[j] = t;swap(int[] a, int i, int j)


答案 1

我也见过类似的东西。“手动内联”不一定更快,结果程序可能太复杂,优化程序无法分析。

在您的示例中,让我们进行一些疯狂的猜测。当您使用 swap() 方法时,JVM 可能能够分析方法体,并得出结论,由于 i 和 j 不会更改,尽管有 4 个数组访问,因此只需要 2 个范围检查而不是 4 个。此外,局部变量不是必需的,JVM可以使用2个寄存器来完成这项工作,而不涉及堆栈上的r / w。tt

稍后,swap() 的主体内联到调用方方法中。这是在之前的优化之后,因此保存仍然存在。甚至可能调用方方法体已经证明 i 和 j 始终在范围内,因此剩余的 2 个范围检查也被删除了。

现在在手动内联版本中,优化器必须一次分析整个程序,有太多的变量和太多的操作,它可能无法证明保存范围检查或消除局部变量是安全的。在最坏的情况下,此版本可能会花费6个以上的内存访问来执行交换,这是一个巨大的开销。即使只有1个额外的内存读取,它仍然非常明显。t

当然,我们没有根据相信手动“大纲”总是更好,即提取小方法,一厢情愿地认为它会帮助优化器。

--

我学到的是,忘记手动微优化。这并不是说我不关心微观性能改进,也不是我总是相信JVM的优化。我完全不知道该怎么做,好事大于坏事。所以我放弃了。


答案 2

JVM可以非常有效地内联小方法。内联自己的唯一好处是,如果您可以删除代码,即通过内联来简化它的作用。

JVM寻找某些结构,并在识别这些结构时进行一些“手工编码”优化。通过使用交换方法,JVM可以识别结构,并通过特定的优化对其进行不同的优化。

您可能有兴趣尝试OpenJDK 7调试版本,该版本具有打印出它生成的本机代码的选项。


推荐