Truffle AOTの概要

Truffleでは、AOTの事前初期化、コンパイル、およびキャッシュのいくつかの異なるフレーバーがサポートされています。このドキュメントは、これらの機能の概要を提供することを目的としています。

ここで言及されている機能の一部は、Oracle GraalVMでのみサポートされていることに注意してください。

最初のコンテキストの事前初期化 #

ネイティブイメージでは、イメージビルド時に静的イニシャライザでJavaコードを実行できます。静的初期化が実行された後、静的フィールドから参照される値がスナップショットされ、イメージに永続化されます。コンテキストの事前初期化は、イメージビルド時に言語コンテキストを作成および初期化し、ランタイムに分離またはプロセスで作成される最初のコンテキストで使用することで、この機能を活用します。これにより、通常、最初のコンテキストの初期化時間が大幅に向上します。

コンテキストの事前初期化は、イメージビルド時にシステムプロパティ-Dpolyglot.image-build-time.PreinitializeContexts=ruby,llvmを設定することで有効にできます。言語は、コンテキストの事前初期化をサポートするためにTruffleLanguage.patchContextを実装し、trueを返す必要があります。さらに、言語は、ホスト固有のデータをバインドしたり、ネイティブイメージに保存できないオブジェクト(java.lang.Threadインスタンスなど)を作成したりしないように注意する必要があります。

詳細については、TruffleLanguage.patchContextのJavadocを参照してください。

同じ分離/プロセス内でのコード共有 #

ポリグロットエンジンを使用して、コンテキスト間のコード共有の範囲を決定できます。その方法の例は、リファレンスマニュアルにあります。言語がポリグロットコンテキスト用に初期化されると、エンジンから新しい言語インスタンスが要求されます。言語がContextPolicy.SHAREDをサポートしている場合、言語インスタンスはエンジンインスタンスに再利用されます。ソース解析キャッシュは言語インスタンスに関連付けられているため、解析は言語インスタンスごとに1回行われます。言語は、TruffleLanguage.areOptionsCompatibleを実装することで、新しい追加のコンテキストに対して言語インスタンスの再利用を禁止することを選択できます。これにより、言語は、言語によって作成されたすべてのルートノードに対して、特定のコンテキストオプションがコンパイル時に最終になることを前提とできます。このルールの例外はInteropLibraryであり、ノードは言語インスタンス間で無条件に共有できます。

コンテキストに依存しないコードのサポート #

コード共有では、すべてのコードデータ構造がコンテキストに依存しない必要があります。たとえば、あるコンテキストで実行し、別の新しいコンテキストで再度実行しても、コードが最適化されない場合、コードはコンテキストに依存しません。言語実装のコンテキスト独立性を検証するための適切なテストは、明示的なエンジンでコンテキストを作成し、テストアプリケーションを実行し、同じ決定性アプリケーションを実行した場合に、2番目のコンテキストが最適化解除を引き起こさないことを確認することです。

Truffleフレームワークは、TruffleLanguage.initializeMultipleContextsを呼び出すことにより、通常、最初のコンテキストが作成される前でも、複数のコンテキストで言語インスタンスを使用する可能性を通知します。フレームワークは、明示的なエンジンが使用されている場合、または--engine.CacheStoretrueに設定されている場合に、最初のコンテキストが作成される前に複数のコンテキストを初期化できます。

コンテキストに依存しないコードをサポートする場合は、次の基準を満たす必要があります。

  • 複数のコンテキストが初期化された場合、ランタイム値の同一性に関するすべての推測を無効にする必要があります。これらは、2番目のコンテキストで使用した場合に、保証された最適化解除につながるためです。
  • 関数のインラインキャッシュは、2レベルのインラインキャッシュとして変更および実装する必要があります。最初のレベルは関数インスタンスの同一性を推測し、2番目のレベルは基になるCallTargetインスタンスを推測します。最初のレベルのキャッシュは、複数のコンテキストが初期化されている場合は無効にする必要があります。これは、不必要に最適化解除を引き起こすためです。
  • DynamicObjectルートのShapeインスタンスは、言語コンテキストではなく、言語インスタンスに格納する必要があります。そうしないと、シェイプに対するすべてのインラインキャッシュが安定せず、最終的にジェネリック状態になります。
  • すべてのNode実装は、コンテキストに依存するデータ構造またはコンテキストに依存するランタイム値を格納してはなりません。
  • 言語内部の組み込み関数でも、ソースのロードと解析は、言語インスタンスごとにソース解析をキャッシュするために、TruffleLanguage.Env.parseを使用して実行する必要があります。
  • すべての仮定インスタンスは、コンテキストではなく、言語インスタンスに格納する必要があります。複数のコンテキストが初期化されている場合、コンテキスト参照を使用して読み取られるコンテキストインスタンスは、もはや定数ではない可能性があります。この場合、コンテキストから読み取られた仮定は折りたたまれず、ランタイムパフォーマンスのオーバーヘッドが大幅に増加します。言語からの仮定は、単一コンテキストモードと複数コンテキストモードの両方でコンパイラーによって折りたたむことができます。

複数のコンテキスト用に作成されたASTは、ランタイム値の同一性に関する推測を許可しないため、効率の低いマシンコードにコンパイルされることが予想されます。たとえば、インラインキャッシュ内の関数インスタンスを推測する代わりに、含まれているCallTargetを推測する必要があります。これは、関数に格納されているCallTargetにアクセスするために追加の読み取りが必要になるため、より遅くなります。コンテキストに依存しないコードを作成するのはコストがかかる可能性があるため、複数のコンテキストが初期化されていない場合は、ランタイム値に関する推測を引き続き実行する必要があります。

SimpleLanguageおよびJavaScriptは、すでにコンテキストに依存しないコードをサポートしている2つの言語であり、具体的な問題に関するガイダンスとして役立つ場合があります。

補助エンジンキャッシュを使用した永続的なコンテキストに依存しないコード(Oracle GraalVM) #

Oracle GraalVMは、コードデータ構造をディスクに永続化することをサポートしています。これにより、分離/プロセスでのアプリケーションの初回実行のウォームアップ時間をほぼ排除できます。SVM補助イメージ機能は、必要なデータ構造を永続化してディスクにロードするために使用されます。コンパイルを実行する必要があるため、イメージの永続化にはかなりの時間がかかる場合があります。ただし、ロードは可能な限り高速になるように設計されており、通常はほぼ瞬時です。

エンジンのキャッシュは、オプションを使用して有効になり、コンテキストが明示的なエンジンなしで作成された場合でも機能します。

エンジンキャッシュの詳細については、エンジンキャッシュのチュートリアルを参照してください。

プロファイリングなしのコンパイル #

デフォルトでは、言語関数が作成されても実行されない場合、補助エンジンキャッシュイメージに保存されるときにコンパイルされません。補助エンジンキャッシュは、ロードされたが実行されなかったルートノードのコンパイルをトリガーすることをサポートしています。このような場合、フレームワークはRootNode.prepareForAOTメソッドを呼び出します。

事前の実行なしでコンパイルの準備ができた言語実装の詳細については、AOTチュートリアルを参照してください。すべての言語を事前の実行なしにコンパイルし、効率的なマシンコードを生成できるわけではないことに注意してください。静的型付け言語は、通常、これに適しています。

アプリケーションのスナップショット #

ポリグロットコンテキストインスタンスのランタイム値をディスクに永続化することも計画されています。この機能が実装され次第、詳細がここに表示されます。

お問い合わせ