3

TL;DR

よく知られている動的言語 (JavaScript など) で記述されたコードの一部と、invokedynamic を使用した Java バイトコードでそのコードがどのように見えるかを提供し、invokedynamic の使用がここで一歩前進した理由を説明してください。

バックグラウンド

インターネット上の誰もがJVM上の動的言語の高速化に役立つことに同意する、それほど新しくはないinvokedynamic命令について、私はググってかなりの量を読みました。stackoverflow のおかげで、Sable/Jasmin を使用して独自のバイトコード命令を実行することができました。

私は、invokedynamic が遅延定数に役立つことを理解しました。また、OpenJDK が lambdas に対してどのように invokedynamic を利用するかを理解したと思います。

Oracleには小さな例がありますが、この場合の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 のブートストラップ メソッドは両方の引数が整数であることを認識しているためです。彼らは次のことを「認めます」。

[..]引数が[..]整数オブジェクトになると仮定します。ブートストラップ メソッドのパラメーター (この例では、callerClass、dynMethodName、および dynMethodType) が異なる場合、ブートストラップ メソッドには、invokedynamic [..] を適切にリンクする追加のコードが必要です。

そうです、その興味深い「追加コード」がなければ、invokedynamic をここで使用しても意味がありませんね。

そのため、Javadoc とブログのエントリをいくつか追加した後、invokestatic/invokevirtual/invokevirtual または getfield が同様に機能する場合に、invokedynamic を適切な代替手段として使用する方法について、かなりよく理解していると思います。

今、実際にinvokedynamic命令を実際のユースケースに適用して、「従来の」呼び出しでできることを実際に改善する方法に興味があります(遅延定数を除く、私はそれらを手に入れました...)。

4

1 に答える 1

8

invokedynamic実際、 「遅延作成」という用語を広く使用する場合、遅延操作は主な利点です。たとえば、Java 8 のラムダ作成機能は一種の遅延作成であり、命令によって最終的に呼び出されるコードを含む実際のクラスが、invokedynamicその命令の実行前に存在しない可能性さえあります。

これは、Java バイトコードとは異なる形式でコードを提供するあらゆる種類のスクリプト言語に投影できます (ソース コードの場合もあります)。ここで、コードはメソッドの最初の呼び出しの直前にコンパイルされ、その後リンクされたままになります。ただし、スクリプト言語がメソッドの再定義をサポートしている場合は、リンクが解除されることさえあります。これは、 の 2 番目の重要な機能を使用して、再定義せずに頻繁に呼び出される場合に最大のパフォーマンスをサポートしながら、後で変更invokedynamicできるミュータブル s を許可します。CallSite

後でターゲットを変更できるこの可能性invokedynamicにより、最初の呼び出しで解釈された実行にリンクし、実行回数をカウントし、しきい値を超えた後にのみコードをコンパイルする (そしてコンパイルされたコードに再リンクする) 別のオプションが可能になります。


ランタイム インスタンスに基づく動的メソッド ディスパッチに関してinvokedynamicは、ディスパッチ アルゴリズムを省略できないことは明らかです。ただし、特定の呼び出しサイトが常に同じ具象型のメソッドを呼び出すことを実行時に検出した場合はCallSite、最適化されたコードに再リンクして、ターゲットが期待される型であるかどうかを簡単にチェックし、最適化されたアクションを実行することができますが、そのテストが失敗した場合にのみ、完全な動的ディスパッチを実行する汎用コードに分岐します。高速パス チェックが一定回数失敗したことが検出された場合、実装はそのような呼び出しサイトの最適化を解除することさえあります。

これは、JVM で内部的に最適化される方法に近くinvokevirtualinvokeinterfaceこれらの命令のほとんどが同じ具象型で呼び出される場合でもあります。したがってinvokedynamic、同じ手法を任意のルックアップ アルゴリズムに使用できます。


しかし、まったく別のユース ケースが必要な場合は、標準のアクセス修飾子規則ではサポートされていないセマンティクスをinvokedynamic実装するために を使用できます。のメソッドを呼び出すことができるような関係を持つfriendクラスAとがあるとします。次に、これらすべての呼び出しは、目的の名前と署名を持ち、次のようなブートストラップ メソッドを指す命令としてエンコードできます。BfriendAprivateBinvokedynamicpublicB

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オブジェクトに完全なアクセス権があることを最初に確認します。したがって、間違った発信者の卑劣な試みは、この場所で整理されます。次に、完全なアクセス権を持つオブジェクトを使用してリンクを完了します。したがって、これらの各命令は、最初の呼び出しの後のマッチング メソッドに永続的にリンクされ、その後は通常の呼び出しと同じ速度で実行されます。AALookupBinvokedynamicprivateB

于 2014-12-12T19:48:38.157 に答える