gpt4 book ai didi

java - 拥有大量小方法是否有助于 JIT 编译器优化?

转载 作者:IT老高 更新时间:2023-10-28 20:44:21 25 4
gpt4 key购买 nike

在最近关于如何优化某些代码的讨论中,有人告诉我,将代码分解成许多小方法可以显着提高性能,因为 JIT 编译器不喜欢优化大方法。

我对此不确定,因为 JIT 编译器本身似乎应该能够识别自包含的代码段,无论它们是否在自己的方法中。

谁能证实或反驳这一说法?

最佳答案

Hotspot JIT 仅内联小于某个(可配置)大小的方法。所以使用更小的方法允许更多的内联,这很好。

查看 this page 上的各种内联选项.


编辑

稍微详细一点:

  • 如果一个方法很小,它将被内联,因此很少有机会因将代码拆分为小方法而受到惩罚。
  • 在某些情况下,拆分方法可能会导致更多的内联。

示例(如果您尝试,完整的代码将具有相同的行号)

package javaapplication27;

public class TestInline {
private int count = 0;

public static void main(String[] args) throws Exception {
TestInline t = new TestInline();
int sum = 0;
for (int i = 0; i < 1000000; i++) {
sum += t.m();
}
System.out.println(sum);
}

public int m() {
int i = count;
if (i % 10 == 0) {
i += 1;
} else if (i % 10 == 1) {
i += 2;
} else if (i % 10 == 2) {
i += 3;
}
i += count;
i *= count;
i++;
return i;
}
}

使用以下 JVM 标志运行此代码时:-XX:+UnlockDiagnosticVMOptions -XX:+PrintCompilation -XX:FreqInlineSize=50 -XX:MaxInlineSize=50 -XX:+PrintInlining(是的,我已使用证明我的情况的值: m 太大,但重构的 mm2 均低于阈值 - 您可能使用其他值得到不同的输出)。

你会看到 m()main() 被编译,但 m() 没有被内联:

 56    1             javaapplication27.TestInline::m (62 bytes)
57 1 % javaapplication27.TestInline::main @ 12 (53 bytes)
@ 20 javaapplication27.TestInline::m (62 bytes) too big

您还可以检查生成的程序集以确认 m 没有内联(我使用了这些 JVM 标志:-XX:+PrintAssembly -XX:PrintAssemblyOptions=intel) - 它看起来像这样:

0x0000000002780624: int3   ;*invokevirtual m
; - javaapplication27.TestInline::main@20 (line 10)

如果你像这样重构代码(我已经在单独的方法中提取了 if/else):

public int m() {
int i = count;
i = m2(i);
i += count;
i *= count;
i++;
return i;
}

public int m2(int i) {
if (i % 10 == 0) {
i += 1;
} else if (i % 10 == 1) {
i += 2;
} else if (i % 10 == 2) {
i += 3;
}
return i;
}

您将看到以下编译操作:

 60    1             javaapplication27.TestInline::m (30 bytes)
60 2 javaapplication27.TestInline::m2 (40 bytes)
@ 7 javaapplication27.TestInline::m2 (40 bytes) inline (hot)
63 1 % javaapplication27.TestInline::main @ 12 (53 bytes)
@ 20 javaapplication27.TestInline::m (30 bytes) inline (hot)
@ 7 javaapplication27.TestInline::m2 (40 bytes) inline (hot)

所以 m2 被内联到 m 中,这是您所期望的,所以我们回到原来的场景。但是当 main 被编译时,它实际上内联了整个东西。在汇编级别,这意味着您不会再找到任何 invokevirtual 指令。你会发现这样的行:

 0x00000000026d0121: add    ecx,edi   ;*iinc
; - javaapplication27.TestInline::m2@7 (line 33)
; - javaapplication27.TestInline::m@7 (line 24)
; - javaapplication27.TestInline::main@20 (line 10)

基本上常见的指令是“相互化的”。

结论

我并不是说这个例子具有代表性,但它似乎证明了几点:

  • 使用更小的方法可以提高代码的可读性
  • 较小的方法通常会被内联,因此您很可能不会支付额外方法调用的成本(这不会影响性能)
  • 使用较小的方法可能在某些情况下全局改进内联,如上面的示例所示

最后:如果您的代码的一部分对性能非常关键,而这些考虑因素很重要,您应该检查 JIT 输出以微调您的代码并在前后进行重要的配置文件。

关于java - 拥有大量小方法是否有助于 JIT 编译器优化?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15756075/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com