gpt4 book ai didi

dynamic - invokedynamic 什么时候真正有用(除了惰性常量)?

转载 作者:行者123 更新时间:2023-12-01 23:17:55 26 4
gpt4 key购买 nike

TL;DR

请提供一段用一些众所周知的动态语言(例如 JavaScript)编写的代码,以及该代码在使用 invokedynamic 的 Java 字节码中的样子,并解释为什么使用 invokedynamic 是向前迈出的一步。

背景

我已经在 Google 上搜索并阅读了很多关于不再是新的 invokedynamic 指令的信息,互联网上的每个人都同意它有助于加速 JVM 上的动态语言。 Thanks to stackoverflow我设法让我自己的字节码指令与 Sable/Jasmin 一起运行。

我知道 invokedynamic 对惰性常量很有用,我也认为我理解了 the OpenJDK takes advantage of invokedynamic for lambdas .

Oracle 有 a small example ,但据我所知,在这种情况下,invokedynamic 的使用违背了目的,因为“加法器”的示例可能更简单、更快,并且与以下字节码表达的效果大致相同:

aload whereeverAIs
checkcast java/lang/Integer
aload whereeverBIs
checkcast java/lang/Integer
invokestatic IntegerOps/adder(Ljava/lang/Integer;Ljava/lang/Integer;)Ljava/lang/Integer;

因为出于某种原因,Oracle 的引导方法知道两个参数都是整数。他们甚至“承认”:

[..]it assumes that the arguments [..] will be Integer objects. A bootstrap method requires additional code to properly link invokedynamic [..] if the parameters of the bootstrap method (in this example, callerClass, dynMethodName, and dynMethodType) vary.

嗯,是的,如果没有那个有趣的“附加代码”,那么在这里使用 invokedynamic 是没有意义的,是吗?

因此,在那之后以及一些其他的 Javadoc 和博客条目之后,我认为我已经很好地掌握了如何在 invokestatic/invokevirtual/invokevirtual 或 getfield 也能正常工作时使用 invokedynamic 作为糟糕的替代品。

现在我很好奇如何将 invokedynamic 指令实际应用到现实世界的用例中,以便它实际上是对“传统”调用的一些改进(除了惰性常量,我得到了那些......)。

最佳答案

实际上,如果您广义地理解“延迟创建”一词,那么延迟操作就是 invokedynamic 的主要优势。例如,Java 8 的 lambda 创建特性是一种惰性创建,其中包括可能包含最终将由 invokedynamic 指令调用的代码的实际类在执行该指令。

这可以转换到所有类型的脚本语言,以不同于 Java 字节码的形式提供代码(甚至可能在源代码中)。在这里,代码可以在第一次调用方法之前编译并在之后保持链接。但是,如果脚本语言支持重新定义方法,它甚至可能会变得未链接。这使用了 invokedynamic 的第二个重要特性,允许可变的 CallSite 之后可以更改,同时在不重新定义的情况下频繁调用时支持最大性能。

这种事后更改 invokedynamic 目标的可能性允许另一个选项,在第一次调用时链接到解释执行,计算执行次数并仅在超过阈值后编译代码(并重新链接到然后编译代码)。


关于基于运行时实例的动态方法分派(dispatch),很明显invokedynamic不能省略分派(dispatch)算法。但是,如果您在运行时检测到特定调用站点将始终调用相同具体类型的方法,您可以将 CallSite 重新链接到优化代码,该代码将简短检查目标是否符合预期然后键入并执行优化操作,但仅在测试失败时才分支到执行完整动态调度的通用代码。如果它检测到快速路径检查失败了一定次数,该实现甚至可能取消优化这样的调用站点。

这类似于 invokevirtualinvokeinterface 如何在 JVM 内部进行优化.因此,使用 invokedynamic 您可以将相同的技术用于任意查找算法。


但如果您想要一个完全不同的用例,您可以使用 invokedynamic 来实现标准访问修饰符规则不支持的 friend 语义。假设你有一个类 AB 意味着有这样的 friend 关系,因为 A 是允许的调用 Bprivate 方法。然后,所有这些调用都可以编码为具有所需名称和签名的 invokedynamic 指令,并指向 B 中的 public 引导方法,可能看起来像这个:

public static CallSite bootStrap(Lookup l, String name, MethodType type)
throws NoSuchMethodException, IllegalAccessException {
if(l.lookupClass()!=A.class || (l.lookupModes()&0xf)!=0xf)
throw new SecurityException("unprivileged caller");
l=MethodHandles.lookup();
return new ConstantCallSite(l.findStatic(B.class, name, type));
}

它首先验证提供的 Lookup 对象是否具有对 A 的完全访问权限,因为只有 A 能够构造这样的对象。因此,错误来电者的偷偷摸摸的尝试在这个地方被整理出来。然后它使用对 B 具有完全访问权限的 Lookup 对象来完成链接。所以,这些 invokedynamic 指令中的每一个在第一次调用后都永久链接到 B 的匹配 private 方法,以与普通相同的速度运行之后调用。

关于dynamic - invokedynamic 什么时候真正有用(除了惰性常量)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24171950/

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