AOTコンパイル後のJavaプログラムの性能がどう変わるのか

はじめに

こんにちは、システムエンジニアのキュウです。私はGraalVMがリリースされたとき、JavaプログラムをAOT(Ahead-of-Time)コンパイルできる機能にとても興味が湧きました。

JavaのAOTコンパイルとは、ソースコードを直接ネイティブマシンコードにコンパイルすることで、バイナリ実行可能ファイルが生成され、デプロイ時にJavaランタイム環境が不要になります。これに対して、従来のJIT(Just-In-Time)コンパイルでは、ソースコードをJavaバイトコードに変換し、Javaランタイム環境で実行します。

実際にこれらコンパイラの性能を比較した結果を共有します。

目的

AOTコンパイル後、ネイティブ化されたJavaプログラムと以下の2点を比較して、どのくらい性能が違うか確認することを目的としました。

  1. 従来のJITコンパイルしたJavaプログラム
  2. C++などのネイティブ言語

以下のツールと環境を使って簡単なテストを行いました。

QuarkusとH2データベースを使ったのは、公式サイトでAOTコンパイル後に性能が大幅に向上すると書かれていたためです。ドキュメントも分かりやすく、簡単にコンパイルできました。C++とSQLiteデータベースは従来型のネイティブプログラムとして比較するために使いました。AWS Lambdaを実行環境として使用したのは、コールドスタート時間やメモリ使用量を測定するのに便利だからです。

詳細内容

テストシナリオ1

CPU使用率の高いアプリケーションシナリオを選びました。最も理解しやすいアルゴリズムであるフィボナッチ数列の第40位を計算するコードを使い、AOTとJITでの違いを確認しました。

テスト結果1

テストシナリオ1_処理時間
テストシナリオ1_最大メモリ

テストシナリオ2

次に、I/O負荷の高いシナリオとして、メモリ型のデータベースに10万件のランダムデータを挿入し、そのうち5万件を更新してから、残りの5万件を削除する一連の処理を実施しました。

テスト結果2

テストシナリオ2_処理時間
テストシナリオ2_最大メモリ

結果のまとめと感想

上記2つのシナリオのテスト結果から、以下の結論を得ました。

  1. AOTコンパイル後のJavaプログラムは、初回起動の速度とメモリ使用量で従来のJITよりも改善が見られます。
  2. 無償版のGraalVMのAOTコンパイル機能は、Javaコードのネイティブ化のみで、実行時の最適化は行われていません。C++プログラムと比較すると性能に大きな差があります。

今回の検証では、JavaのAOTコンパイル処理自体がかなり重いと感じました。その上、リフレクション情報を手動で定義してコンパイラに提供しないとスムーズにコンパイルできない場合もありました。これらの作業には多くの時間を費やします。逆に、処理パフォーマンスと起動時間を重視する場合、直接C++を使う方が速いかもしれません。