LambdaがS3からGetObjectするのにかかる時間を計測してみた [追記, 修正あり]
こんにちは、CX事業本部の夏目です。
サーバーレスアプリケーションの設計をしていたとき、めったに更新しないマスタデータならS3に置くのもありじゃねって思ったので、LambdaがS3からデータを落とすのにかかる時間を調べてみました。
2019/10/10 追記
はてブのコメントで「get_object で取得したデータのストリームに触れてないし、ファイルハンドラを作ってるだけでデータサイズ分読めてないんじゃないかな」「既にブコメにあるようにget_objectの戻り値にあるStreamingBodyをどげんかせんといかん気がする。」と指摘されました。
実際そのとおりで、StreamingBodyから値を受け取って長さを求める処理を入れたところ1GBのデータをダウンロードするのに15秒ほどかかりました。
間違ったデータを提供してしまい、申し訳ありません。
計測に使ったコードを修正し、測定結果はすべて差し替えました
試した環境
- 東京リージョン
- イベント実行でLambdaを500回動かして確かめた
Lambdaのメモリ量の寄与
条件
- 10MBのデータをダウンロードさせてみた
結果
メモリサイズ(MB) | 試行回数(秒) | 平均値(秒) | 中央値(秒) | 最大値(秒) | 最小値(秒) |
---|---|---|---|---|---|
3008 | 500 | 0.151557904 | 0.1410165 | 0.641314 | 0.122447 |
1024 | 500 | 0.160976518 | 0.148138 | 1.146616 | 0.122413 |
512 | 500 | 0.20648305 | 0.20103949999999998 | 0.483012 | 0.15911 |
256 | 500 | 0.38046901 | 0.36134299999999997 | 0.680586 | 0.301635 |
128 | 500 | 0.716364562 | 0.68135 | 1.379574 | 0.541196 |
落としてくるデータ量の寄与
メモリサイズ 512MBのとき
落としてきたデータサイズ | 試行回数 | 平均値(秒) | 中央値(秒) | 最大値(秒) | 最小値(秒) |
---|---|---|---|---|---|
100MB | 500 | 1.361732588 | 1.314912 | 2.53633 | 1.283921 |
10MB | 500 | 0.20648305 | 0.20103949999999998 | 0.483012 | 0.15911 |
128KB | 500 | 0.070214688 | 0.064704 | 1.248748 | 0.040343 |
1KB | 500 | 0.05961791 | 0.0573585 | 0.164185 | 0.035138 |
メモリサイズ 3008MBのとき
落としてきたデータサイズ | 試行回数 | 平均値(秒) | 中央値(秒) | 最大値(秒) | 最小値(秒) |
---|---|---|---|---|---|
1GB | 500 | 13.39081825 | 13.4641925 | 22.006347 | 12.71388 |
100MB | 500 | 1.145742728 | 1.13311 | 1.756455 | 1.12501 |
10MB | 500 | 0.151557904 | 0.1410165 | 0.641314 | 0.122447 |
128KB | 500 | 0.05326789 | 0.0440465 | 1.07839 | 0.025528 |
1KB | 500 | 0.039890174 | 0.0355075 | 0.636321 | 0.020258 |
まとめ
予想に反して、落とすデータ量よりもLambdaのメモリ量の方が影響が大きそうだった。 それでも、メモリ量128MBでも10MBのデータを1秒かからずにダウンロードできたので、頻繁に更新しないマスタデータをS3に置いてもレスポンスが悪くなることはほぼなさそう。
2019/10/10 追記
コメントを受けてあらためて測定したのですが、500件程度ではサンプル数が足りなかったかもなぁといった感じでした。 (一部データでは再測定前よりも平均値が短くなっているものもあったので)
特に、メモリ量の寄与はおもったよりもしっかりと結果にあらわれました。
でもまぁ、128MBのメモリのLambdaで10MBのデータをダウンロードしても1秒かからずに取得することができたので、自分としては満足のいく結果です。
おまけ - 測定に使用したLambdaのコード -
S3 BucketやKey, 結果を流し込むためのKinesisは環境変数を使って渡した。
import boto3 import os from datetime import datetime, timezone from uuid import uuid4 import json def get_bucket_name() -> str: return os.environ['TARGET_BUCKET_NAME'] def get_key_name() -> str: return os.environ['TARGET_KEY_NAME'] def get_region_name() -> str: return os.environ['AWS_REGION'] def get_stream_name() -> str: return os.environ['OUTPUT_STREAM_NAME'] def get_memory_size(context) -> str: return str(context.memory_limit_in_mb) def measure_download_time() -> float: s3 = boto3.client('s3') bucket = get_bucket_name() key = get_key_name() start = datetime.now() resp = s3.get_object(Bucket=bucket, Key=key) binary = resp['Body'].read() size = len(binary) end = datetime.now() delta = end - start return delta.total_seconds() def create_result_data(time: float, memory_size: str) -> bytes: key = get_key_name() region = get_region_name() return json.dumps({ 'id': str(uuid4()), 'key': key, 'region': region, 'memory_size': memory_size, 'download_time': time, 'timestamp': datetime.now(timezone.utc).timestamp() }).encode() def put_kinesis(data: bytes) -> None: option = { 'PartitionKey': f'k-{uuid4()}', 'StreamName': get_stream_name(), 'Data': data } kinesis = boto3.client('kinesis') kinesis.put_record(**option) def lambda_handler(event, context) -> None: download_time = measure_download_time() memory_size = get_memory_size(context) data = create_result_data(download_time, memory_size) put_kinesis(data)