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

时间:2011-02-11 14:17:29

标签: java performance inline static-methods

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

首先,我认为,由于没有内联,我会产生一些开销,因为JVM必须确定是否要内联此方法(甚至不能这样做)。

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

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

5 个答案:

答案 0 :(得分:12)

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

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

稍后,swap()的主体内联到调用方法中。这是在上一次优化之后,因此保存仍然存在。调用者方法体甚至可能证明i和j总是在范围内,因此剩下的2个范围检查也被丢弃。

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

当然,我们没有理由认为手动“概述”总是更好,即提取小方法,如愿以为它会帮助优化者。

-

我所学到的是,忘记了手动微观优化。并不是我不关心微观性能改进,而是我始终信任JVM的优化。这是我完全不知道该做什么比做坏事更好。所以我放弃了。

答案 1 :(得分:9)

JVM可以非常有效地内联小方法。唯一能够自我介绍的好处是,如果你可以删除代码,也可以通过内联来简化代码。

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

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

答案 2 :(得分:2)

很抱歉我迟到的回复,但我刚刚发现了这个话题,引起了我的注意。

在Java中开发时,尝试编写“简单而愚蠢”的代码。原因:

  1. 优化是在运行时进行的(因为编译本身是在运行时进行的)。编译器无论如何都要弄清楚要做什么优化,因为它不是编译你编写的源代码,而是编译它使用的内部表示(几个AST - > VM代码 - > VM代码...... - >本机二进制代码转换由JVM编译器和JVM解释器在运行时创建)
  2. 优化编译器时使用一些常用的编程模式来决定优化内容;所以帮助他帮助你!编写一个私有静态(也许是最终的)方法,它会立即发现它可以:
    • 内联方法
    • 将其编译为本机代码
  3. 如果方法是手动内联的,那么它只是编译器首先尝试理解的另一种方法的一部分,并且看是否有时间将其转换为二进制代码,或者是否必须稍等一下才能理解程序流程。此外,根据方法的作用,在运行时期间可以进行多次重新JIT:> JVM只在“预热”之后生成最佳二进制代码......并且可能在JVM自行升温之前程序结束(因为我希望最终性能应该非常相似)。

    结论:优化C / C ++中的代码是有意义的(因为静态转换为二进制文件),但相同的优化通常不会对Java产生影响,其中编译器JIT是字节代码,而不是源代码码。顺便说一句,从我看到的javac甚至都没有做出优化:)

答案 3 :(得分:1)

  

然而,前几天,我用静态方法的调用替换了这个手动内联代码,并看到了性能提升。怎么可能?

如果它位于一个地方(静态方法),那么JVM分析器可能更容易看到瓶颈,而不是单独实施多次。

答案 4 :(得分:0)

Hotspot JIT编译器能够内联很多东西,特别是在-server模式下,虽然我不知道你是如何获得实际的性能提升的。 (我的猜测是内联是通过方法调用计数完成的,交换这两个值的方法不会经常调用。)

顺便说一句,如果它的性能非常重要,您可以尝试将其交换为两个int值。 (我不是说它会更快,但它可能值得一试。)

a[i] = a[i] ^ a[j];
a[j] = a[i] ^ a[j];
a[i] = a[i] ^ a[j];