SQSやKinesisをイベントソースとするPython Lambda関数を最適化してみた

SQSやKinesis DataStreamsをイベントソースとする PythonランタイムのLambda関数、メモリ割り当て設定の見直しと、KeepAliveの導入で改善を試みました。
2023.10.16

SQS や Kinesis DataStreams をイベントソースとし、 バッチで複数のエントリを受け取り、特定APIを連続して実行する、PythonランタイムのLambda関数の最適化を試みる機会がありました。

今回、 Lambdaの メモリ割り当て設定の調整と、 Requests モジュールを利用した Keep Aliveの導入について、紹介させて頂きます。

Lambda設定

  • ランタイム: Python 3.9
  • アーキテクチャ: x86_64
  • メモリ: 128, 256, 512, 1024, 2048 MB

レイヤ-

Requests モジュール、 LambdaのPythonランタイム、3.8 以降 で標準バンドル外となりました。

AWS Lambdaで利用できるPythonライブラリの一覧をpkg_resourcesを使って確認してみた

今回、Python 3.9 のランタイム で Requests モジュールを利用するため、、有志が公開されているレイヤー Keith's Layers (Klayers) を組み込みました。

Lambdaレイヤ設定

  • 名前: Klayers-p39-requests
  • バージョン ARN: arn:aws:lambda:ap-northeast-1:770693421928:layer:Klayers-p39-requests:17

検証

15KBのアイコンファイルを10回連続してダウンロードするLambda関数を用意しました。

割当メモリ、利用するモジュール別に 実行時間 (Billed Duration) を測定、比較しました。

url='https://dev.classmethod.jp/favicon.ico'
curl -v ${url} > /dev/null

< HTTP/2 200 
< content-type: image/x-icon
< content-length: 15086
< x-cache: Hit from cloudfront

urllib.request

Python ランタイムで標準で利用できるライブラリを利用しました。

import urllib.request
url='https://dev.classmethod.jp/favicon.ico'
def lambda_handler(event, context):
  for i in range(10):
    response = urllib.request.urlopen(url)
    print(str(i) + ':' + str(response.getcode()))

requests (キープアライブ無効)

requestsモジュール、キープアライブ無効で利用しました。

import requests
url='https://dev.classmethod.jp/favicon.ico'
def lambda_handler(event, context):
  for i in range(10):
    response = urllib.request.urlopen(url)
    print(str(i) + ':' + str(response.getcode()))

requests (キープアライブ有効)

requestsモジュールを利用して、キープアライブ有効な利用としました。

import requests
url='https://dev.classmethod.jp/favicon.ico'
def lambda_handler(event, context):
  with requests.Session() as s:
    for i in range(10):
      response = s.get(url)
      print(str(i) + ':' + str(response.status_code))

結果

Lambda実行時間(Billed Duration:ms)比較

メモリ(MB) urllib requests (Keep Alive無効) requests (Keep Alive有効)
128 2706 ms 1980 ms 604 ms
256 1330 ms 948 ms 292 ms
512 625 ms 417 ms 121 ms
1024 309 ms 235 ms 73 ms
2048 258 ms 226 ms 72 ms

まとめ

AWS Lambda、メモリ割り当て量に比例した CPU性能を利用する事が可能です。

関数メモリの設定 (コンソール)

今回のLambda関数 メモリ消費量は50MB前後と記録されていましたが、 メモリ割り当てを1024MBとする事でCPU処理性能が向上、実行時間が短縮する事で、費用対効果に優れた利用が可能となりました。

REPORT RequestId: ###-###-###-###-###   Duration: 603.47 ms Billed Duration: 604 ms Memory Size: 128 MB Max Memory Used: 49 MB  Init Duration: 206.76 ms
REPORT RequestId: ###-###-###-###-###   Duration: 72.56 ms  Billed Duration: 73 ms  Memory Size: 1024 MB    Max Memory Used: 49 MB  Init Duration: 208.93 ms
REPORT RequestId: ###-###-###-###-###   Duration: 71.44 ms  Billed Duration: 72 ms  Memory Size: 2048 MB    Max Memory Used: 49 MB  Init Duration: 217.65 ms

また、Lambda ランタイム Python 3.8以降で 標準バンドルされなくなった requests モジュール、 同一サーバのAPIを 繰り返し実行するワークロードでは、 KeepAlive による大きな性能改善が実現できる場合があります。

requests モジュール の効果が確認できた場合には、Lambda関数コード (ZIPパッケージ)への同梱や、Lambdaレイヤーの継続的な維持の実現性について、評価、検討してご利用ください。