SQSやKinesisをイベントソースとするPython Lambda関数を最適化してみた
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) を組み込みました。
- 名前: 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レイヤーの継続的な維持の実現性について、評価、検討してご利用ください。