JIT(ジャストインタイム)とAOT(アヘッドオブタイム)がしばしば相反する形で提示されることがある理由については、よくわかりません。
移植性について気にしない場合、プログラムはAOTコンパイルが非常にうまくでき、実行時にJITを使用してホットパーツを再最適化できると私は感じています。
このスキームを使用した既知の実装は何ですか。ない場合は、なぜですか?
あなたは プロファイルに基づく最適化 について話している。 Scala私が見たネイティブAOTコンパイラは、PGOを使用して2段階で実行します。基本的に、最初のパスは、実行時にコードをインストルメント化してプロファイルファイルを生成します。次に、そのバージョンをテストで実行します製品の一般的な使用例を実行するスイート。次に、そのプロファイルファイルは、最適化を実際に適用してネイティブ実行可能ファイルを生成する2番目のビルドパスへの入力として使用されます。
ビルド時にランタイムプロファイルを事前に生成することにより、コードの異常な使用パターンに基づく最適化の潜在的なメリットを失うだけでなく、ランタイムプロファイリングのオーバーヘッドも失われます。私はこれを会議でデモを見たことがありますが、正確な数は覚えていませんが、実行時間は同等であり、本質的にはHotSpot JVMと比較して、印象的なRAMおよび起動時間の短縮がありました。 JIT方式で同じPGO。
JITの重要な側面を見逃しているようです:プラットフォームの独立性マシンコードAOTにコンパイルするとすぐに、プログラムはプラットフォームではありません-もう独立。
とはいえ、.NETエコシステムはこの機能をかなり長い間提供してきました。あなたはそれについて読むことができます この記事で 。
JITを使用してホットスポットを再最適化することの意味がわかりません。定義上、AOTをコンパイルすると、プログラムは既にコンパイル済みです。
はい。プログラムを3回再コンパイルすることは可能であり、非常に一般的です。
開発者のマシンで、コードは中間言語コードにコンパイルされます。これは、ソースコードよりも解析が速いコードの形式ですが、一般的な仮想マシンまたは一般的なバージョンのターゲットアーキテクチャを対象としています。ここでのコンパイルの目的は、最適化ではなく主に移植性のためですが、デッドコードの除去、定数の折りたたみ、メソッドのインライン化など、このステップで発生する可能性のある単純な最適化が数多くあります。
プログラムを初めてインストールまたはロードするときに、中間言語コードをマシンのアーキテクチャネイティブコードにコンパイルできます。このコンパイル手順により、プログラムは、実行されている特定の命令セットの知識を利用できます。 「ほとんどのx86 CPU」のような一般的なアーキテクチャー用にコンパイルするのではなく、インストール時にコンパイルすることで、コンパイラーは「AMD Ryzen 3rd Gen Threadripper 3960X」で実行されているという知識を利用できます。その特定のマシン用の最新のCPU命令セット。コードを他のx86マシンで不適切にすることを心配する必要はありません。
実行時に、JITはコードの実行パスを追跡して、ユーザーが作業しているファイルが機能Fooを使用していないことを確認できます。つまり、メソッドAを呼び出すホットループの後には常に呼び出しが続きます。メソッドC、D、およびHに追加します。これらのメソッドには、Fooを使用するファイルにのみ必要であるため、trueであることが確認されていないif条件が多数あるため、C、D、Hを再コンパイルしてみましょう。これらのifブロックを削除し、メソッドAにガードを設定して、ユーザーが機能Fooを使用する別のファイルを突然開いた場合に古いコードに戻る新しいメソッドC_D_H。 JITコンパイルは、最速のコードを生成するのに最も有利ですが、コードがどこにあるかを追跡するためにコードを計測する必要があり、リアルタイムでこれらの分析と再コンパイルを行う必要があるため、危険な場所でもあります。最適化されていないコードを実行するだけの場合と比較して、実際に実行が遅くなる可能性があります。
3つのコンパイルすべてを利用することが多い言語は、JavaScript、Webアセンブリ、およびAndroidです。