コンテンツに移動
デベロッパー

Java クライアント ライブラリのネイティブ イメージ サポートを発表 - 短期間のワークロードを最適化

2022年7月26日
https://storage.googleapis.com/gweb-cloudblog-publish/images/native_imagessupport_heroimage.max-2200x2200.png
Google Cloud Japan Team

※この投稿は米国時間 2022 年 7 月 7 日に、Google Cloud blog に投稿されたものの抄訳です。

Cloud Java クライアント ライブラリに、ネイティブ イメージ コンパイルのサポートが組み込まれました。


ネイティブ イメージ テクノロジーを使用すると、Java アプリケーションを事前にコンパイルして、スタンドアロンの実行可能ファイルにすることができます。これにより、コールド スタートアップ時間が短縮され、先行メモリ使用量が少なくなるなど、いくつかのパフォーマンス上の利点が得られます(JVM を必要としないため)。


ただし、ネイティブ イメージ コンパイルは、一部の形式の Java コード(リソースの読み込み、リフレクション)とは必ずしも互換性があるとは限らないため、追加の構成が必要になります。今回のリリースにより、Cloud クライアント ライブラリには、ネイティブ イメージのコンパイルに必要な構成が付属しているため、追加の構成を行うことなく、アプリケーションをコンパイルできるようになりました。また、このテクノロジーを使用すると、JVM 実行時に最適化が失われてしまうことから、ネイティブ コンパイルは、迅速な起動と応答時間が重要な短期間のワークロードに最適であることにも注意してください。

パフォーマンス上のメリット

ネイティブ イメージとしてビルドされたアプリケーションと、標準の Java(17.0.3、Temurin)で実行された同じアプリケーションのパフォーマンスを比較したところ、次の利点がありました。

https://storage.googleapis.com/gweb-cloudblog-publish/images/01_Average_startup_times_in_milliseconds.max-600x600.png
https://storage.googleapis.com/gweb-cloudblog-publish/images/02_Average_startup__first_request_times_in_m.max-600x600.png

上記のパフォーマンスの差は、特に起動時間を比較すると大きくなります。この例では、ネイティブ イメージの起動時間の 87.65% が 1 ミリ秒未満でした。これは、コールド スタートのレイテンシを最適化することを目的とした場合に大きな違いをもたらします。

メモリ使用量

ネイティブ イメージにコンパイルされたアプリケーションのメモリ使用量も大幅に少なくなります。ps コマンドを使用して、Resident Set Size(タスクが使用した、スワップされていない物理メモリ)を確認したところ、次の結果が得られました。

https://storage.googleapis.com/gweb-cloudblog-publish/images/03_Resident_Set_Size_in_kilobytes.max-600x600.png

スタートガイド

ここでは、ネイティブ イメージ コンパイルを使用して、Pub/Sub Storage のサンプルを実行する方法について説明します。

ネイティブ イメージ コンパイルのクライアント ライブラリ サポートによって解き放たれるパフォーマンス上の利点を示すために、このガイドに基づいて、Pub/Sub および Storage クライアント ライブラリを利用するサンプル アプリケーションを作成します。自分のマシンや Cloud Shell でも自由に試してみてください。

前提事項

上記のパフォーマンス データを収集するために使用したアプリケーションを再現するには、次のものが必要です。

サンプルアプリのビルド

まず、次の Maven の goal を使用して、プロジェクトを生成します。

読み込んでいます...

これにより生成される新しい Maven プロジェクトは、サンプルアプリの妥当な開始点となります。

まず、Maven コンパイラ プラグインのリリース バージョンを 17(以降)に設定し、クライアント ライブラリを依存関係として追加します。

読み込んでいます...

App.java と同じディレクトリに ListPubSubNotifications というクラスを追加します。

読み込んでいます...

このクラスは、特定の Storage バケットに設定されている Pub/Sub 通知を一覧表示します。

次に、PublishWithErrorHandlerExample という同じパッケージに別のクラスを追加します。

読み込んでいます...

このクラスは、特定のトピックにタイムスタンプ メッセージをパブリッシュし、失敗した場合の例外を処理します。

最後に、App.java のメイン関数に、両方のクラスの関数を呼び出して起動 / 実行時間を追跡するロジックを追加します。

読み込んでいます...


サンプル アプリケーションを配置したので、ネイティブ イメージ ビルドを構成する準備が整いました。

ビルド構成

次の「native-image」ビルド プロファイルを pom.xml に追加します。

読み込んでいます...

このプラグインとプロファイルは、アプリケーションをネイティブ イメージにビルドするために必要な構成を native-image ビルダーに提供するプロセスを簡素化します。「mainClass」パラメータがアプリケーションにとって正しいことを確認し、「buildArgs」を使用してオプションをビルダーに渡せることに注意してください。


この時点で、アプリをネイティブ イメージに組み込む準備ができています。ただし、native-image の事前ビルドで念頭に置くべきことがいくつかあります。

  • 同等のジャストインタイム ビルドよりも時間がかかる(約 5~10 分*)

  • かなりのメモリを消費する(約 6~10 GB *)

* 値は Cloud Shell の e2-standard-4 マシンタイプでこのサンプルをビルドした結果です


ビルドを実行するには、適切な JDK と native-image ビルダーがあることを確認する必要があります。このプロセスは SDKMAN! を利用することで大幅に簡素化できます。


次のコマンドを使用して、GraalVM などの適切な JDK ディストリビューションをインストールします。
読み込んでいます...

次に、現在のシェルでこのバージョンを使用するように sdkman に通知します。

読み込んでいます...

次に、native-image 拡張機能をインストールします。

読み込んでいます...

最後に「native-image」プロファイルを使用して、native-image-client-libraries-sample プロジェクトのビルドを実行します。

読み込んでいます...

このビルドプロセスには数分かかることがあります。ビルドが完了すると、パフォーマンスの違いを実際に確認するために必要なものがすべて揃います。

生成された実行可能ファイルは「ターゲット」ディレクトリにあります。プログラムを実行して、Cloud Storage バケットから通知を受信することを確認します。

読み込んでいます...

https://storage.googleapis.com/gweb-cloudblog-publish/images/Side-by-side.max-800x800.png

この例では、ネイティブ イメージは通常の jar より 159 倍速く起動し、19 倍速く終了しました。ネイティブ イメージ コンパイルの結果は、ワークロードによって大きく異なる可能性があるため、クラウド リソースを最大限に活用するために、独自のアプリケーションを自由に試してみてください。

まとめ

これは、Cloud Java クライアント ライブラリでネイティブ イメージ コンパイルがサポートされる仕組みの一例にすぎません。サポートされているライブラリと、ネイティブ イメージとしてアプリケーションを構築する方法の詳細については、公式ドキュメントをご覧ください。ドキュメント ページは、試してみることができるいくつかのサンプルへもリンクしています。


- Java デベロッパー アドボケイト Aaron Wanjala
- プロダクト マネージャー Cameron Balahan

投稿先