什么时候invokedynamic真正有用(除了惰性常量)? [英] When is invokedynamic actually useful (besides lazy constants)?

查看:117
本文介绍了什么时候invokedynamic真正有用(除了惰性常量)?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

请提供一段用某种众所周知的动态语言(例如JavaScript)编写的代码,以及使用invokedynamic在Java字节码中的代码外观,并在这里解释为什么使用invokedynamic是向前迈进的一步.

Please provide a piece of code written in some well known dynamic language (e.g. JavaScript) and how that code would look like in Java bytecode using invokedynamic and explain why the usage of invokedynamic is a step forward here.

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

I have googled and read quite a lot about the not-that-new-anymore invokedynamic instruction which everyone on the internet agrees on that it will help speed dynamic languages on the JVM. Thanks to stackoverflow I managed to get my own bytecode instructions with Sable/Jasmin to run.

我知道invokedynamic对于惰性常量很有用,而且我还认为我理解 OpenJDK利用lambda的invokedynamic .

I have understood that invokedynamic is useful for lazy constants and I also think that I understood how the OpenJDK takes advantage of invokedynamic for lambdas.

Oracle具有 a小例子,但据我所知,这种情况下对invokedynamic的使用无法达到目的,因为加法器"的例子可以更简单,更快,并且具有与以下字节码大致相同的效果:

Oracle has a small example, but as far as I can tell the usage of invokedynamic in this case defeats the purpose as the example for "adder" could much simpler, faster and with roughly the same effect expressed with the following bytecode:

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的bootstrap方法知道两个参数都是整数.他们甚至承认":

because for some reason Oracle's bootstrap method knows that both arguments are integers anyway. They even "admit" that:

[..]假定参数[..]将是Integer对象.如果引导程序方法的参数(在此示例中为callerClass,dynMethodName和dynMethodType)不同,则引导程序方法需要附加代码才能正确链接invokedynamic [..].

[..]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是没有意义的吗?

Well yes, and without that interesing "additional code" there is no point in using invokedynamic here, is there?

因此,在此之后,再加上其他两个Javadoc和Blog条目,我认为我可以很好地掌握如何在invokestatic/invokevirtual/invokevirtual或getfield也可以正常工作时使用invokedynamic作为不良替代品.

So after that and a couple of further Javadoc and Blog entries I think that I have a pretty good grasp on how to use invokedynamic as a poor replacement when invokestatic/invokevirtual/invokevirtual or getfield would work just as well.

现在,我很好奇如何将invokedynamic指令实际应用于现实世界的用例,以使它实际上比传统"调用(除了惰性常量,我得到的那些...)所能做的有所改进. >

Now I am curious how to actually apply the invokedynamic instruction to a real world usecase so that it actually is some improvements over what we could with "traditional" invocations (except lazy constants, I got those...).

推荐答案

实际上,如果您广泛地使用懒惰创建"一词,则懒惰操作是invokedynamic的主要优点.例如,Java 8的lambda创建功能是一种懒惰的创建,其中包括这样的可能性,即包含最终将由invokedynamic指令调用的代码的实际类甚至在该指令执行之前都不存在.

Actually, lazy operations are the main advantage of invokedynamic if you take the term "lazy creation" broadly. E.g., the lambda creation feature of Java 8 is a kind of lazy creation that includes the possibility that the actual class containing the code that will be finally invoked by the invokedynamic instruction doesn’t even exist prior to the execution of that instruction.

可以将其投影到各种脚本语言中,以与Java字节码不同的形式提供代码(甚至在源代码中也可以).在这里,代码可以在第一次调用方法之前进行编译,并在之后保持链接状态.但是,如果脚本语言支持方法的重新定义,它甚至可能变得未链接.这利用了invokedynamic的第二个重要功能,允许可变的CallSite可以在以后进行更改,同时在频繁调用而无需重新定义时支持最高性能.

This can be projected to all kind of scripting languages delivering code in a different form than Java bytecode (might be even in source code). Here, the code may be compiled right before the first invocation of a method and remains linked afterwards. But it may even become unlinked if the scripting language supports redefinition of methods. This uses the second important feature of invokedynamic, to allow mutable CallSites which may be changed afterwards while supporting maximal performance when being invoked frequently without redefinition.

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

This possibility to change an invokedynamic target afterwards allows another option, linking to an interpreted execution on the first invocation, counting the number of executions and compiling the code only after exceeding a threshold (and relinking to the compiled code then).

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

Regarding dynamic method dispatch based on a runtime instance, it’s clear that invokedynamic can’t elide the dispatch algorithm. But if you detect at runtime that a particular call-site will always call the method of the same concrete type you may relink the CallSite to an optimized code which will do a short check if the target is that expected type and performs the optimized action then but branches to the generic code performing the full dynamic dispatch only if that test fails. The implementation may even de-optimize such a call-site if it detects that the fast path check failed a certain number of times.

这与在JVM中对invokevirtualinvokeinterface进行内部优化的方式非常接近,因为大多数指令都是在相同的具体类型上调用的.因此,使用invokedynamic,您可以对任意查找算法使用相同的技术.

This is close to how invokevirtual and invokeinterface are optimized internally in the JVM as for these it’s also the case that most of these instructions are called on the same concrete type. So with invokedynamic you can use the same technique for arbitrary lookup algorithms.

但是,如果您希望使用一个完全不同的用例,则可以使用invokedynamic来实现标准访问修饰符规则不支持的friend语义.假设您有一个类AB,它们具有这样的friend关系,因为允许A调用Bprivate方法.然后,所有这些调用都可以被编码为invokedynamic指令,并带有所需的名称和签名,并指向B中的public引导程序方法,如下所示:

But if you want an entirely different use case, you can use invokedynamic to implement friend semantics which are not supported by the standard access modifier rules. Suppose you have a class A and B which are meant to have such a friend relationship in that A is allowed to invoke private methods of B. Then all these invocations may be encoded as invokedynamic instructions with the desired name and signature and pointing to a public bootstrap method in B which may look like this:

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方法,然后以与之后的普通调用相同的速度运行.

It first verifies that the provided Lookup object has full access to A as only A is capable of constructing such an object. So sneaky attempts of wrong callers are sorted out at this place. Then it uses a Lookup object having full access to B to complete the linkage. So, each of these invokedynamic instructions is permanently linked to the matching private method of B after the first invocation, running at the same speed as ordinary invocations afterwards.

这篇关于什么时候invokedynamic真正有用(除了惰性常量)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆