Lambdaの実行速度を各言語で計測してみた

## はじめに
Javaはコールドスタート時の実行速度が遅いと言うのはよく見聞きしていましたが実際に計測したことはなかったのでやってみました。


## 環境
計測に使うサンプルプログラムを作成するのにAWS SAMを使用しています。
* Windows 10 Pro
* AWS CLI：2.6.2
* AWS SAM CLI：1.105.0

## 準備
計測は次の言語で行いました。
| 言語 | ランタイム | メモリ | コードサイズ | リージョン |
|:-:|:-:|:-:|:-:|:-:|
| Go | provided.al2023 | 128MB | 4.6 MB | ap-northeast-1 |
| Java | java21 | 128MB | 854.6 KB | ap-northeast-1 |
| JavaScript | nodejs20.x | 128MB | 1.3 KB | ap-northeast-1 |
| Python | python3.10 | 128MB | 521.1 KB | ap-northeast-1 |

計測に使用するサンプルプログラムをAWS SAMで作成します。
以下はJava21のランタイムを選択した場合の例になります。他のランタイムも若干選択肢は異なりますが基本的には同じものを選択するようにしています。

```bash:sam init
$ sam init

You can preselect a particular runtime or package type when using the `sam init` experience.
Call `sam init --help` to learn more.

Which template source would you like to use?
        1 - AWS Quick Start Templates
        2 - Custom Template Location
Choice: 1

Choose an AWS Quick Start application template
        1 - Hello World Example
        2 - Data processing
        3 - Hello World Example with Powertools for AWS Lambda
        4 - Multi-step workflow
        5 - Scheduled task
        6 - Standalone function
        7 - Serverless API
        8 - Infrastructure event management
        9 - Lambda Response Streaming
        10 - Serverless Connector Hello World Example
        11 - Multi-step workflow with Connectors
        12 - GraphQLApi Hello World Example
        13 - Full Stack
        14 - Lambda EFS example
        15 - Hello World Example With Powertools for AWS Lambda
        16 - DynamoDB Example
        17 - Machine Learning
Template: 1

Use the most popular runtime and package type? (Python and zip) [y/N]:

Which runtime would you like to use?
        1 - aot.dotnet7 (provided.al2)
        2 - dotnet6
        3 - go1.x
        4 - go (provided.al2)
        5 - go (provided.al2023)
        6 - graalvm.java11 (provided.al2)
        7 - graalvm.java17 (provided.al2)
        8 - java21
        9 - java17
        10 - java11
        11 - java8.al2
        12 - java8
        13 - nodejs20.x
        14 - nodejs18.x
        15 - nodejs16.x
        16 - python3.9
        17 - python3.8
        18 - python3.12
        19 - python3.11
        20 - python3.10
        21 - ruby3.2
        22 - ruby2.7
        23 - rust (provided.al2)
        24 - rust (provided.al2023)
Runtime: 8

What package type would you like to use?
        1 - Zip
        2 - Image
Package type: 1

Which dependency manager would you like to use?
        1 - gradle
        2 - maven
Dependency manager: 1

Would you like to enable X-Ray tracing on the function(s) in your application?  [y/N]: y
X-Ray will incur an additional cost. View https://aws.amazon.com/xray/pricing/ for more details

Would you like to enable monitoring using CloudWatch Application Insights?
For more info, please view https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/cloudwatch-application-insights.html [y/N]:

Would you like to set Structured Logging in JSON format on your Lambda functions?  [y/N]:

Project name [sam-app]: Java

    -----------------------
    Generating application:
    -----------------------
    Name: Java
    Runtime: java21
    Architectures: x86_64
    Dependency Manager: gradle
    Application Template: hello-world
    Output Directory: .
    Configuration file: Java\samconfig.toml

    Next steps can be found in the README file at Java\README.md


Commands you can use next
=========================
[*] Create pipeline: cd Java && sam pipeline init --bootstrap
[*] Validate SAM template: cd Java && sam validate
[*] Test Function in the Cloud: cd Java && sam sync --stack-name {stack-name} --watch
```

次のようなAPIGateway＋Lambda構成のSAMテンプレートが作成されます。
```yaml:template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  Java

  Sample SAM Template for Java2

# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
  Function:
    Timeout: 20
    MemorySize: 512

    Tracing: Active
  Api:
    TracingEnabled: true
Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
    Properties:
      CodeUri: HelloWorldFunction
      Handler: helloworld.App::handleRequest
      Runtime: java21
      Architectures:
      - x86_64
      MemorySize: 512
      Environment: # More info about Env Vars: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#environment-object
        Variables:
          PARAM1: VALUE
      Events:
        HelloWorld:
          Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
          Properties:
            Path: /hello
            Method: get

Outputs:
  # ServerlessRestApi is an implicit API created out of Events key under Serverless::Function
  # Find out more about other implicit resources you can reference within SAM
  # https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api
  HelloWorldApi:
    Description: API Gateway endpoint URL for Prod stage for Hello World function
    Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"
  HelloWorldFunction:
    Description: Hello World Lambda Function ARN
    Value: !GetAtt HelloWorldFunction.Arn
  HelloWorldFunctionIamRole:
    Description: Implicit IAM Role created for Hello World function
    Value: !GetAtt HelloWorldFunctionRole.Arn
```

このままデプロイしても問題ありませんが、APIGatewayは不要なためコメントアウトすることにしました。
```yaml:APIGatewayに関する記述をコメントアウト
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  Java

  Sample SAM Template for Java

# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
  Function:
    Timeout: 20
    MemorySize: 512

    Tracing: Active
  Api:
    TracingEnabled: true
Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
    Properties:
      CodeUri: HelloWorldFunction
      Handler: helloworld.App::handleRequest
      Runtime: java21
      Architectures:
      - x86_64
      MemorySize: 512
      Environment: # More info about Env Vars: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#environment-object
        Variables:
          PARAM1: VALUE
#       Events:
#         HelloWorld:
#           Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
#           Properties:
#             Path: /hello
#             Method: get

# Outputs:
#   # ServerlessRestApi is an implicit API created out of Events key under Serverless::Function
#   # Find out more about other implicit resources you can reference within SAM
#   # https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api
#   HelloWorldApi:
#     Description: API Gateway endpoint URL for Prod stage for Hello World function
#     Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"
#   HelloWorldFunction:
#     Description: Hello World Lambda Function ARN
#     Value: !GetAtt HelloWorldFunction.Arn
#   HelloWorldFunctionIamRole:
#     Description: Implicit IAM Role created for Hello World function
#     Value: !GetAtt HelloWorldFunctionRole.Arn
```

また、MemorySizeは比較の条件を合わせるため128MBを指定するように変更します。
選択するランタイムによってはSAMテンプレートにMemorySizeの指定がされていない場合があるため追記が必要です。
```yaml:template.yaml
Globals:
  Function:
    Timeout: 20
    MemorySize: 128 # ここを追加・変更
```

## 計測結果
計測の方法は[こちらの記事](https://dev.classmethod.jp/articles/lambda-measure/)を参考にさせていただきました。
各言語のLambdaを100回実行した平均になっています。

| Lang | Cold Start or Warm Start | total(ms) | initialization(ms) | invocation(ms) | overhead(ms) |
|:-:|:-:|:-:|:-:|:-:|:-:|
| Go | warm | 10.13 | - | 0.61 | 0.33 |
| Java | warm | 226.54 | - | 218.44 | 0.92 |
| JavaScript | warm | 10.63 | - | 1.12 | 0.51 |
| Python | warm | 7.99 | - | 0.76 | 0.18 |
| Go | cold | 273.99 | 57.39 | 0.91 | 0.95 |
| Java | cold | 11448.99 | 591.27 | 10676.08 | 20.06 |
| JavaScript | cold | 292.99 | 132.11 | 14.15 | 58.87 |
| Python | cold | 256 | 109.34 | 0.67 | 0.12 |

Javaはwarmスタートでもかなり遅い結果となっています。
AWS SAMで作成したサンプルプログラムが外部URLを参照する作りになっていることやメモリサイズが128MBとそもそも少なすぎる設定にしてしまった影響を受けているのかもしれません。

それ以外の言語ではあまり大きな差は見られなかったと言っても良さそうです。

前述の参考にした記事の時点から言語のバージョンも上がっているためパフォーマンス向上に期待をしていましたが誤差程度の差しかなかったと言えそうです。

## おわりに
見聞きしていた通りの結果でしたが、自分で試してみる貴重な経験になりました。
選定の参考になりましたら幸いです。

最近であればJavaのスナップスタートやRustの実行速度も気になっているため、次の機会にこのあたりも触ってみたいと思います。