事前コンパイラ(Ahead-Of-Timeコンパイラ、AOTコンパイラ、)とは、アプリケーション実行前に(事前に)ソースコードや中間表現(中間言語)を機械語へ変換(コンパイル)するコンパイラのこと。対義語は実行時コンパイラ(Just-In-Timeコンパイラ、JITコンパイラ)。
概要 JITコンパイラはアプリケーションの実行直前または実行中に、中間言語コードを実行環境のプロセッサに応じた機械語コードにコンパイルするため、インタプリタによる逐次実行方式よりは高速であるものの、必要なコードがすべて機械語に変換されてメモリ上に展開され、定常状態に到達する(ウォームアップが完了する)まではアプリケーションの性能を低下させることがある。特に起動にかかる時間が増加するため、起動と終了を繰り返すアプリケーションで問題になることがある。AOTコンパイラは実行時コンパイルの必要性を無くし、ソースコードから中間言語コードを生成した後に、ネイティブの機械語コードを生成させる方式で性能低下を回避できる。JITコンパイラと区別するためAOTコンパイラという言葉が使われるが、C言語やC++など、AOTコンパイラが事実上の標準となっている言語では、わざわざAOTをつけずに単にコンパイラと呼ばれることがほとんどである。
AOTコンパイルは比較的高性能で潤沢な計算資源(高速なCPU、大容量のメモリやストレージなど)を搭載した開発環境において実行され、JITコンパイルよりも時間的・空間的リソースを費やして入念な最適化を実行できる余裕があり、またコンパイラに渡すオプションによって最適化レベルをアプリケーション開発者側で制御しやすい。ただし、AOTコンパイルしたコードが常にJITコンパイルしたコードよりも高速であるとは限らない。一般的にAOTコンパイラは事前に特定の命令セットを仮定し、ターゲット環境が必ずサポートする命令を使ってコードを生成するため、最適化性能はその制約を受けやすい。AOTコンパイルでSIMDのような拡張命令を利用して最適化する場合、拡張命令をサポートしない環境でもプログラムを実行できるようにするためには、個別にコードを分けて出力し、ファットバイナリのような形で提供しなければならず、実行ファイルのサイズ増大を招きやすい。一方、JITコンパイルは完全に実行環境に限定したコードのみを生成することができ、実行環境にとって最も効率のよいシンプルかつ高速なコード生成をしやすいJavaの新JITコンパイラ、Graalを解説。
.NET C#やVisual Basic .NETをはじめとする.NET言語は、JITコンパイル方式が基本ではあるが、もともと.NET Frameworkでは、マネージアセンブリから事前にネイティブコードを生成してインストールするツールとして「Ngen.exe (ネイティブ イメージ ジェネレーター)」をサポートしていたNgen.exe (ネイティブ イメージ ジェネレーター) - .NET Framework | Microsoft Learn。しかし、これは主に起動と終了を繰り返すことで蓄積されるオーバーヘッドが問題となるサーバーアプリケーション向けのソリューションであり、手軽に利用できるものではなく、また一部の動的な機能が利用できなくなるなどの欠点もある。このツールはJITコンパイラとともに、Windows 8.xのWindowsストアアプリでも使われていたMigrating Your Windows 8.x App to .NET Native - UWP applications | Microsoft Learn。Windows 10のUWPアプリケーションでは、「.NET Native」と呼ばれる類似の事前コンパイル技術がサポートされた.NET ネイティブによるアプリのコンパイル - UWP applications | Microsoft Learn。
.NET Coreでは、バージョン3.0でAOTコンパイルの一種であるReadyToRun形式がサポートされたWhat's new in .NET Core 3.0 | Microsoft Learn。2022年末にリリースされた.NET 7ではネイティブAOTがサポートされたが、ネイティブAOTと完全に互換性のあるライブラリは限定されている。
Java GraalVM は、事前コンパイルしてネイティブイメージの作成ができる。アプリの起動時間が短くなる。起動時間が短くなることによりAWS Lambdaなどでの使用に適している。
下記はJava 20現在の、GraalVM開発者によるのPetClinicサンプルアプリによるベンチマーク結果。起動は圧倒的に速くなるが、処理速度はOpenJDKの方が速い。GraalVMはprofile-guided optimization (PGO)を使用し、コンパイル前に処理内容のプロファイリングをとってからコンパイルして、ガベージコレクタにはG1 GCを使用している。
OpenJDK GraalVM 起動時間 7.18秒 0.22秒 メモリ使用量 1,751MB 694MB 処理速度 12,800リクエスト/秒 10,249リクエスト/秒
OpenJDK アプリの起動時間の短縮を目的に以下の機能が追加されている。JITコンパイルは処理に時間がかかるが、アプリを起動するたびに、毎回同じ処理を行うのは無駄であり、過去に行ったJITコンパイルの結果を保存する。 Java 24 JEP 483: Ahead-of-Time Class Loading & Linking Java 25 JEP 514: Ahead-of-Time Command-Line Ergonomics JEP 515: Ahead-of-Time Method Profiling Java 26 JEP 516: Ahead-of-Time Object Caching with Any GC 開発中 JEP draft: Ahead-of-Time Code Compilation
脚注
関連項目 実行時コンパイラ GNU Compiler for Java JRuby レトロニム