AWS Lambda と Python Boto3 を使って Amazon S3 Express One Zone の性能比較をしてみた
Amazon S3 Express One Zone について
Amazon S3 Express One Zone については、JAWS-UG 名古屋 2023年 AWS re:Inventの復習 - JAWS-UG名古屋 | Doorkeeper の LT 登壇資料でざっくり説明したので、以下をご確認ください!
Lambda からアップロード・ダウンロード速度を計測する
AWS Lambda からアクセスする速度を time を使って簡易計測してみました!
最新の boto3 を追加する
まず、Boto3 のバージョンを確認してみます。
import boto3
def lambda_handler(event, context):
print(f'boto3 version: {boto3.__version__}')
出力結果:
boto3 version: 1.28.72
なお、以下のページで ランタイムのバージョンとそのSDKのバージョンが確認できます。
(少し更新が遅れていそうです)
Amazon S3 Express One Zone を扱うためには、
Boto3 を最新のものにアップデートする必要があるので、
Lambda レイヤーで最新の boto3 を追加します。
CloudShell で以下のコマンド実行することで、レイヤーを追加できます。
mkdir python
pip3 install boto3 -t ./python
zip -r ./python.zip .
aws lambda publish-layer-version --layer-name python-layer --zip-file fileb://python.zip --compatible-runtimes python3.12
aws lambda update-function-configuration --function-name {バケット名} --layers arn:aws:lambda:ap-northeast-1:{アカウントID}:layer:python-layer:1
バージョンアップしたことを確認します。
出力結果:
boto3 version: 1.34.2
Python のソースコード
標準ライブラリ time で開始と終了時間の差分を出して、パフォーマンスを測っていきます。
import boto3
import json
import time
def upload_files(s3_client, count=10, is_standard=True, file_name="sample.csv"):
# 開始時間
start = time.time()
file_path = f'./files/{file_name}'
bucket_name = 'bucket-name'
args = None
if not is_standard:
bucket_name = 'bucket-name-express-one-zone'
args = {'StorageClass': 'EXPRESS_ONEZONE'}
# ファイルアップロード
for i in range(count):
s3_client.upload_file(file_path, bucket_name, f'temp/dummy_{i}.csv', ExtraArgs=args)
# 実行時間 = 終了時間 - 開始時間
elapsed = time.time() - start
return elapsed
def download_files(s3_client, count=10, is_standard=True):
# 開始時間
start = time.time()
bucket_name = 'bucket-name'
if not is_standard:
bucket_name = 'bucket-name-express-one-zone'
# ファイルアップロード
for i in range(count):
s3_client.download_file(bucket_name, f'temp/dummy_{i}.csv', f'/tmp/dummy.csv')
# 実行時間 = 終了時間 - 開始時間
elapsed = time.time() - start
return elapsed
def lambda_handler(event, context):
print(f'event: {event}')
# S3クライアント
s3_client = boto3.client('s3', region_name='ap-northeast-1')
if event["method"] == "upload":
# Standard Storage
standard_elapsed = upload_files(s3_client, event["count"], True, event["file_name"])
print(f'Standard: {standard_elapsed}')
# Express One Zone Storage
ex_elapsed = upload_files(s3_client, event["count"], False, event["file_name"])
print(f'Express One Zone: {ex_elapsed}')
performance = standard_elapsed/ex_elapsed*100
print(f'Performance: {performance}%')
if event["method"] == "download":
# Standard Storage
standard_elapsed = download_files(s3_client, event["count"], True)
print(f'Standard: {standard_elapsed}')
# Express One Zone Storage
ex_elapsed = download_files(s3_client, event["count"], False)
print(f'Express One Zone: {ex_elapsed}')
performance = standard_elapsed/ex_elapsed*100
print(f'Performance: {performance}%')
return {
'statusCode': 200,
'body': json.dumps('Success!')
}
テストのリクエストは以下のように設定します。
-
count
: 実行回数 -
method
:upload
ordownload
-
file_name
: ファイル名(files
フォルダに格納しておく)
{
"count": 1000,
"method": "upload",
"file_name": "sample.csv"
}
アップロード
アップロードでは 4 倍前後の性能差が出ました。
ファイルサイズ | 標準 (s) | Express One Zone (s) | 性能差 (%) |
---|---|---|---|
1.4 KB | 433.41064834594727 | 112.82194685935974 | 384.1545553953463 |
30.2 KB | 531.4169392585754 | 97.15754437446594 | 546.9641525833351 |
2.3 MB | 334.6522886753082 | 93.27737593650818 | 358.7711224885856 |
2.3 MB のファイルでは 15 分でタイムアウトしたため、3000 回で実行しています。
ダウンロード
ダウンロードでも、4 倍前後の性能差が出ました。
ファイルサイズ | 標準 (s) | Express One Zone (s) | 性能差 (%) |
---|---|---|---|
1.4 KB | 532.2136123180389 | 133.81951093673706 | 397.71002643227564 |
30.2 KB | 589.7837207317352 | 132.4846966266632 | 445.171205240197 |
2.3 MB | 411.7004597187042 | 122.26232695579529 | 336.735337834325 |
2.3 MB のファイルでは 15 分でタイムアウトしたため、3000 回で実行しています。
性能差が出なかったケース
最初、メモリが最小の 128 MB で実行していたとき、性能差がほとんど出ませんでした笑
以下は 1000 回実行時の結果です。
ファイルサイズ | 標準 (s) | Express One Zone (s) | 性能差 (%) |
---|---|---|---|
2.3 MB | 507.78030037879944 | 364.30071902275085 | 139.38492950025943 |
マシンスペックに依存するのも、当然と言えば当然の結果かと思われます。
Rust は S3 Express One Zone に非対応?
AWS SDK for Rust は、まだ S3 Express One Zone に非対応のようでした。
サポートしてほしいという issue が上がっていました。
PutObject を試してみた
AWS SDK for Rust で PutObject だけ試してみました。
(本当は、ここまで実装した後に Express One Zone に非対応って知りましたw)
[dependencies]
dotenvx = "0.0.2"
tokio = { version = "1.35.0", features = ["full"] }
aws-config= { version = "1.0.3", features = ["behavior-version-latest"] }
aws-sdk-s3= { version = "1.5.0", features = ["rt-tokio"] }
use aws_config::meta::region::RegionProviderChain;
use aws_sdk_s3::config::Region;
use aws_sdk_s3::primitives::ByteStream;
use aws_sdk_s3::Client;
use std::fs;
#[tokio::main]
async fn main() {
let region = Some("ap-northeast-1");
let region_provider = RegionProviderChain::first_try(region.map(Region::new))
.or_default_provider()
.or_else(Region::new("ap-northeast-1"));
let shared_config = aws_config::from_env().region(region_provider).load().await;
let client = Client::new(&shared_config);
let file_data = fs::read("./files/dummy.csv").expect("Failed to read 'dummy.csv'.");
let body = ByteStream::from(file_data);
let request = client
.put_object()
.bucket("bucket-name")
.key("dummy.csv")
.body(body)
.send()
.await;
match request {
Ok(r) => {
println!("{:?}", r);
}
Err(e) => {
eprintln!("{:?}", e)
}
}
}
まとめ
10 倍の性能は出せませんでした!
精進します🔥🔥🔥
Discussion