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