プログラミング
JEP 544: Ahead-of-Time (AOT) コードコンパイル
JEP 544: Ahead-of-Time Code Compilation (openjdk.org)
要約
JEP 544は、Javaアプリケーションの起動時間とウォームアップ時間を改善するため、HotSpot JVMでアプリケーションコードを事前にネイティブコードにコンパイルし、AOTキャッシュに保存して、その後の実行で即座に利用可能にする提案です。これにより、ピークパフォーマンスへの到達を早め、ワークロードの変化にも動的に対応することで、AOTとJITコンパイルの利点を組み合わせることを目指します。
全文翻訳
JEP 544: Ahead-of-Time コードコンパイル
オーナー
John Rose
タイプ
機能
スコープ
実装
ステータス
候補
コンポーネント
hotspot / compiler
ディスカッション
leyden dash dev at openjdk dot org
関連
JEP 483: Ahead-of-Time クラスロードとリンク
JEP 515: Ahead-of-Time メソッドプロファイリング
レビュー担当者
Alex Buckley, Dan Heidinga, Mark Reinhold, Vladimir Kozlov
承認者
Vladimir Kozlov
作成日
2024/06/30 04:47
更新日
2026/09/10 15:28
課題
8335368
概要
HotSpot Java Virtual Machineが起動したときに、最適化されたネイティブコードをアプリケーションですぐに利用可能にすることで、起動時間とウォームアップ時間を改善します。これを実現するために、トレーニング実行でアプリケーションコードをネイティブコードにコンパイルし、そのネイティブコードをAOTキャッシュに保存して、後続のプロダクション実行で使用します。プロダクションでワークロードが変化した場合、ピークパフォーマンスを維持するためにネイティブコードを動的に再生成し、ahead-of-time (AOT) と just-in-time (JIT) コンパイルの両方の利点を提供します。
目標
アプリケーションがより早くピークパフォーマンスを達成できるようにする。
ワークロードが変化しても、アプリケーションがピークパフォーマンスを維持できるようにする。
アプリケーション、ライブラリ、またはフレームワークのコードに変更を要求しない。
AOTキャッシュの使用を要求する以外は、HotSpotの設定に変更を要求しない。
Serial、Parallel、G1、およびZGCガベージコレクタを引き続きサポートする。
新しいAOTワークフローを導入するのではなく、既存のAOTキャッシュ作成ワークフローを拡張する。
AOTコンパイル済みコードからJITコンパイル済みコードへの切り替えがアプリケーションから見えないようにする。
AArch64およびx64プロセッサアーキテクチャをサポートする。
目標外のこと
AOT専用モードを提供することは目標ではありません。
アプリケーションは、必要に応じて自動的に切り替えながら、同じ実行でAOTコンパイル済みコードとJITコンパイル済みコードの両方を使用します。
クロスコンパイルをサポートすることは目標ではありません。
トレーニング実行でコンパイルされたコードは、後続のプロダクション実行で、同じCPUアーキテクチャ、同じ機能セットで実行する必要があります。
HotSpotが現在サポートしているすべてのCPUアーキテクチャをサポートすることは目標ではありません。
通常のポート作業により、最終的にすべての主要アーキテクチャのサポートが追加されると予想されます。
動機
HotSpot JVMでJavaアプリケーションを実行すると、アプリケーションは3つのフェーズを経ます。起動、ウォームアップ、そしてピークパフォーマンスに到達します。
起動中、HotSpotはアプリケーションのメインメソッドを呼び出し、クラスをオンデマンドでロード、リンク、初期化します。
最初は、バイトコードインタプリタを介してアプリケーションコードとJDKライブラリコードの両方を実行しますが、これは低速です。
インタプリタ内で、HotSpotはメソッド呼び出し数やループ反復回数などのイベントをカウントすることで、アプリケーションの動作をプロファイルします。
このプロファイルデータを使用して、頻繁に呼び出されるメソッド、つまりホットスポットを選択し、基本的なC1コンパイラを介してネイティブコードにコンパイルします。
このネイティブコードはわずかに最適化されているだけです。
ウォームアップ中、アプリケーションはワークロードに落ち着き、クラスのロード、リンク、初期化が減少します。
HotSpotは、バイトコードインタプリタとC1によって挿入されたインストルメンテーションコードの両方で、アプリケーションのプロファイリングを続行します。
メソッド呼び出し数やループ反復回数だけでなく、遭遇したオブジェクトのタイプなど、よりリッチなプロファイル情報を収集します。
プロファイルデータが蓄積されるにつれて、統計的に有用性が高まります。
最終的に、HotSpotはそのデータを使用して最もホットなメソッドを選択し、高度なC2コンパイラを介してネイティブコードにコンパイルします。
このネイティブコードにはインストルメンテーションが含まれておらず、高度に最適化されています。
アプリケーションのプロファイリングとネイティブコードの生成にはコストがかかります。
バイトコードインタプリタが低速であるだけでなく、インストルメントされたネイティブコードはインストルメントされていないネイティブコードよりも低速です。
メソッドをネイティブコードにコンパイルするには、CPU時間とメモリが必要ですが、これらはアプリケーションが使用できるはずです。ただし、HotSpotはプロファイルデータがその労力に見合うことを示唆する場合にのみメソッドをネイティブコードにコンパイルします。
しかし、徐々にJITコンパイルはアプリケーションの出現するホットスポットに追いつき、アプリケーションはより速く実行されます。
最終的に、すべてのホットメソッドが完全に最適化されたネイティブコードにコンパイルされ、コンパイラはアイドル状態になります。
アプリケーションのホットスポットが変わらない限り、このピークパフォーマンスの状態を維持します。
しかし、アプリケーションのホットスポットは、ワークロードの変化に応じて変化する可能性があります。
その場合、HotSpotは必要に応じて以前に生成されたネイティブコードを破棄して動的にデオプティマイズし、新しくホットになったメソッドのために新しいネイティブコードを生成して再オプティマイズすることができます。
例えば、アプリケーションが最初に2種類の要求を受け取った場合、HotSpotはその2つの要求タイプのコードを動的に最適化します。
アプリケーションが3番目のタイプの要求を受け取り始めた場合、HotSpotは動的にデオプティマイズしてから、3つのタイプの要求すべてに対してコードを再オプティマイズすることができます。
アプリケーションは、実質的に、アプリケーションのワークロードが変化するにつれてパフォーマンスを維持しながら、別のウォームアップフェーズを通過することができます。
静的コンパイルについてはどうでしょうか?
静的コンパイルは、Javaコードの動的なコンパイルの代替として提案されることがあります。
静的コンパイラは、実行時間前に、アプリケーション全体をネイティブコードに変換します。
静的コンパイルには、動的コンパイルと比較していくつかの利点があります。
静的にコンパイルされたアプリケーションは、ウォームアップフェーズなしで、すぐに起動し、ピークパフォーマンスに到達します。
実行時には、バイトコードインタプリタ、プロファイリング、またはコンパイルの必要がありません。
静的コンパイラの最適化作業が、以前の実行中に収集された正確なプロファイルによってガイドされている場合、ピークパフォーマンスはHotSpotと同等になることさえあります。
しかし、動的コンパイルには、静的コンパイルと比較して3つの重要な利点があります。
第一に、動的コンパイルはアプリケーションのホットスポットの変化に対応するため、アプリケーションを機敏にします。
必要に応じてデオプティマイズおよび再オプティマイズし、アプリケーションのワークロードが変化するにつれてパフォーマンスを維持します。
静的にコンパイルされたアプリケーションは、このように応答することはできません。その性質上、1つのホットスポットのセットに対してのみ最適化できます。
第二に、動的コンパイルは、実行時に実行環境固有のネイティブコードを生成するため、さまざまなハードウェアおよびソフトウェアにわたってアプリケーションをポータブルにします。
アプリケーションが異なるプロセッサアーキテクチャ、異なる機能セットを持つプロセッサ、異なるオペレーティングシステム、または異なるバージョンのJDKに再デプロイされた場合、HotSpotはアプリケーションの変更なしにその環境でピークパフォーマンスを達成します。
静的にコンパイルされたアプリケーションは、このような変更に直面した場合に再コンパイルする必要があります。
最後に、動的コンパイルはJavaプラットフォームの動的な性質と互換性があります。
動的クラスロード、動的リンク、動的ディスパッチ、動的リフレクションなどの機能は、広範な表現力を提供し、プラットフォームの成功の基盤となってきました。
HotSpotはこれらの機能を自然に処理しますが、静的コンパイラはこれらの機能に苦労します。
静的解析の多大な努力をもってしても、これらの機能では実行時に多くの決定を行う必要があるという事実を補うことはできません。
Javaコードの静的コンパイラの実装者は、したがって、クローズドワールドの仮定などの互換性のない制約や、開発者にリフレクションの対象となるクラスを事前に特定する必要があるなどの大きな負担をかけることに頼ってきました。
コンパイル作業をトレーニング実行にシフトする
起動およびウォームアップフェーズ全体で、HotSpotは複数のボールを継続的にジャグリングします。アプリケーションコードとJDKライブラリコードを実行し、クラスをオンデマンドでロード、リンク、初期化し、アプリケーションの実行をプロファイルし、プロファイルデータによってガイドされるさまざまなレベルの最適化でホットメソッドをネイティブコードにコンパイルします。
Project Leydenのテーゼは、起動時間とウォームアップ時間を改善する鍵は、この作業の一部を、実行時ではなく、事前に、つまりahead-of-timeで行うことであるということです。
トレーニング実行で作業を行い、その結果をAOTキャッシュに保存して、後続のプロダクション実行で即座に使用できるようにすることで、作業を時間的に前倒しします。
私たちは作業を前倒ししました