0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Snap Startだけでは速くならないJava+AWS Lambda

Posted at

はじめに

Java+AWS LambdaはSnap Startさえしておけばなんとかなる!って思ってた愚かな自分が、少しパフォーマンスチューニングしてみたお話です。

なお、今回検証に使ったソースは以下に格納しています。

ソースのベースは、以下を利用させていただき、2つの改造をしております。

  • DynamoDBへの接続
  • Powertools for AWS Lambda (Java)のTracing適用

何もしていない状態

DynamoDBの初期化などをハンドラ外に定義しているだけの状態で動作確認してみます。

初回起動時

トータル7.27秒でした。
起動に4.4秒、実際の処理は2.57秒くらいです。

初回だけとはいえ、流石に遅すぎです!

スクリーンショット 2025-02-01 10.50.58.png

2回目以降

ちなみに2回目は29ミリ秒。さすがJavaさんです!

スクリーンショット 2025-02-01 9.36.28.png

Lambda SnapStart適用

次にLambda SnapStartを有効にしてみます。

Lambda SnapStartはコールドスタート時間を大幅に短縮するための機能で、初期化済み状態のスナップショットを作成・保存し、それを新しい実行環境のベースとして使用することで、起動時間を大幅に軽減する機能です。

デプロイの時間は少し長くなりましたが、初回起動時の性能はトータル7.27→3.73秒となり、半減しました。

起動時間が約80%程度まで削減されていることがわかります。
Init->Restoreになってる。)

スクリーンショット 2025-02-01 10.47.10.png

ただし、起動以降の処理時間は変化がなく、2回目以降のスピードが出ていません。

Snap Startを使えば、常に2回目以降のスピードが出るものとずっと思い込んでました。。

公式のページ通りですが、リストアにも803ミリ秒と結構時間かかってますね。

Warm up(クラスローディング対応)

以下のベストプラクティスを参考に、初回起動時にクラスローディングされない部分を手動コールするようにしてみます。

サンプルでは、実際にハンドラから呼び出しをしていますが、クラスローディングが主目的だと思うので、ハンドラのStaticイニシャライザからPetsControllerを手動でコールしてみました。

public class StreamLambdaHandler implements RequestStreamHandler {
    private static final Logger logger = LoggerFactory.getLogger(StreamLambdaHandler.class);
    private static SpringBootLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler;
    static {
        try {
            handler = SpringBootLambdaContainerHandler.getAwsProxyHandler(Application.class);

registration.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "/*");
            });

            DynamoDbClient dynamoDbClient = DynamoDbClient.create();
            PetsController controller = new PetsController(dynamoDbClient);
            controller.listPets(Optional.of(1), null);

            logger.info("warm up!");
        } catch (ContainerInitializationException e) {
            e.printStackTrace();
            throw new RuntimeException("Could not initialize Spring Boot application", e);
        }
    }

これで初回起動時の性能がトータル約3.73→1.79秒となり、さらに半減しました!

スクリーンショット 2025-02-01 10.41.58.png

Javaは起動時には全てのクラスをローディング(メモリに格納し、実行可能状態にすること)するわけではなく、初回呼び出し時にローディングすることで起動時間やメモリの節約をしている模様です。

AWS Lambda Power Tuning

最後にAWS Lambda Power Tuningを使って、Lambdaそのものの性能を上げてみます。

変更可能なのはメモリサイズだけですが、メモリサイズと共にCPU性能も上がる仕様になっています。

ここからは札束で殴る力技ですが、性能を上げると処理時間が減り、結果、コスト面も優位になったりするので、AWS Lambda Power Tuningを使って調整するのが良いです。

なお、ここまでの検証はx86_64512MBでした。

x86_64の検証結果

速度面で見ると、1024MBが性能面では最適。

スクリーンショット 2025-02-01 10.16.42.png

arm64の検証結果

x86_64とほぼ同じ傾向が出るかなと思いきや、結構違いますね。

こちらは2048MBが良いみたい。

スクリーンショット 2025-02-01 11.09.30.png

比較

それぞれの結果を同時に表示してみます。

スクリーンショット 2025-02-01 10.30.43.png

コストを一旦無視すると、arm642048MBが良さげ!

arm642048MBで再測定すると、トータル約1.79->1.26秒となりました。

スクリーンショット 2025-02-01 11.04.09.png

なお、何も考えずメモリサイズを10240MBにしてみても、性能はあまり変わらないですね。

スクリーンショット 2025-02-02 7.13.04.png

まとめ

ここまでの対策で7.27→1.26秒まで短縮できました!

Snap StartはJavaを利用する上で必須の機能ですが、それだけでもダメなこともわかりました。クラスローディング等、Javaの言語知識を深める必要もありそうです!

そもそもSpringを使わないとか、Native化してしまうなどの本質的な対策もあるかもですが、今回はSpringで頑張ってみました!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?