【问题标题】:Megamorphic virtual call - trying to optimize it超态虚拟调用 - 尝试优化它
【发布时间】:2022-02-05 21:46:31
【问题描述】:

我们知道有一些技术可以让虚拟调用在 JVM 中变得不那么昂贵,例如 Inline Cache 或 Polymorphic Inline Cache。

让我们考虑以下情况:

Base 是一个接口。

public void f(Base[] b) {
    for(int i = 0; i < b.length; i++) {
          b[i].m();   
    }
}

我从我的分析器中看到调用虚拟(接口)方法m 相对昂贵。 f 在热路径上,它被编译为机器代码 (C2),但我看到对 m 的调用是一个真正的虚拟调用。这意味着它没有被JVM优化。

问题是,如何处理这种情况?显然,我不能在这里使 m 方法不是虚拟的,因为它需要进行认真的重新设计。

我可以做任何事还是必须接受?我在考虑如何“强制”或“说服”JVM

  1. 在这里使用多态内联缓存 - b` 中不同类型的数量非常少 - 在 4-5 种类型之间。
  2. 展开此循环 - b 的长度也相对较小。展开后,内联缓存可能会在这里有所帮助。

提前感谢您的任何建议。 问候,

【问题讨论】:

    标签: optimization jvm


    【解决方案1】:

    HotSpot JVM 最多可以内联虚拟调用的两个不同目标,对于更多接收者,将通过 vtable/itable [1] 进行调用。

    要强制内联更多接收者,您可以尝试手动去虚拟化调用,例如

    if (b.getClass() == X.class) {
        ((X) b).m();
    } else if (b.getClass() == Y.class) {
        ((Y) b).m();
    } ...
    

    在分析代码执行期间(在解释器或 C1 中),JVM 收集每个调用站点的接收器类型统计信息。然后在优化编译器 (C2) 中使用此统计信息。您的示例中只有一个调用站点,因此统计信息将在整个执行过程中汇总。

    但是,例如,如果 b[0] 总是只有两个接收器 XY,而 b[1] 总是有另外两个接收器 ZW,则 JIT 编译器可能会从拆分代码中受益进入多个呼叫站点,即手动展开:

    int len = b.length;
    if (len > 0) b[0].m();
    if (len > 1) b[1].m();
    if (len > 2) b[2].m();
    ...
    

    这将拆分类型配置文件,以便 b[0].m()b[1].m() 可以单独优化。

    这些是依赖于特定 JVM 实现的低级技巧。一般来说,我不建议将它们用于生产代码,因为这些优化很脆弱,但它们肯定会使源代码更难阅读。毕竟,多态调用并没有那么糟糕 [2]。

    [1]https://shipilev.net/blog/2015/black-magic-method-dispatch/
    [2]https://shipilev.net/jvm/anatomy-quarks/16-megamorphic-virtual-calls/

    【讨论】:

    • 谢谢! :) “但是​​,例如,如果 b[0] 总是只有两个接收器 X 或 Y,而 b[1] 总是有另外两个接收器 Z 或 W,则 JIT 编译器可能会受益于将代码拆分为多个调用站点,即手动展开”。这就是我想要做的,但我想强制 JVM 自动展开这个循环。有可能吗?
    • @Gilgamesz HotSpot 确实自动展开,但想法不是循环展开,而是拆分类型配置文件(甚至在 C2 开始编译之前就收集)。
    • 是的,我明白了。但是,当 JVM 展开我的循环时,将会有许多调用站点,因此会有许多类型配置文件。这就是为什么我想 JVM 展开我的循环。但是 - 据我了解? - 在特定情况下无法强制 JVM 展开。
    • @Gilgamesz 再次:C2 会自动展开您的循环(是的,在这种特殊情况下确实会发生这种情况,您无需为此做任何事情)。但是,这对 w.r.t 没有帮助。去虚拟化。类型配置文件是更早收集的,甚至在 C2 开始编译此方法之前; C2 不收集类型配置文件。
    • 我明白了。感谢@apangin 的帮助。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-03
    • 2016-01-11
    • 2015-10-06
    • 2018-01-19
    • 2020-10-31
    • 2018-09-07
    相关资源
    最近更新 更多