- 追加された行はこの色です。
- 削除された行はこの色です。
[[AWSメモ]] >
* S3へのファイル登録のパフォーマンス比較 [#i6ef5fce]
#setlinebreak(on)
#contents
** 概要 [#s07a1537]
#html(<div style="padding-left:10px;">)
S3へのファイル登録時に使用するAPIによってパフォーマンスがどれだけ変わるか検証する
テストの基本的な方針は以下の通り。
・s3 の client や resource オブジェクトなどは極力再利用する。
・ファイルサイズの異なる10個のファイルをアップロードする
・上記のテストを処理毎に10回繰り返す。
・さらに上記のテストを5回繰り返す。
・一時的なネットワーク遅延などを除外する為、各テストとも最速とさ最遅は結果から除く。
#html(</div>)
** 結果(平均) [#s07a1537]
#html(<div style="padding-left:10px;">)
結果からみるとさすがに大きな違いはなかったが、やはり圧縮して扱った方が速い模様。
boto3.resource.meta.client.upload_fileobj が意外と速い。(一時ファイルの作成と削除まで行っているにも関わらず)
|処理|結果平均1|h
|boto3.resource.Object.put を使用|1.1228|
|boto3.resource.Bucket.put_object を使用|1.0804|
|boto3.client.put_object を使用|1.0642|
|boto3.resource.meta.client.upload_file を使用|1.2192|
|boto3.resource.Bucket.put_object を使用(かつファイル圧縮)|0.9866|
|boto3.client.put_object で属性を指定|1.1036|
|boto3.resource.Bucket.put_object でファイル圧縮しつつ、属性を指定|0.992|
|boto3.resource.meta.client.upload_fileobj を使用(かつファイル圧縮)|1.0028|
時間をおいて、client.upload_fileobj のファイル圧縮版を加えて計測してみた。
API的にはどれも変わらないように思う(その時のAWSリソース側やネットワーク環境に左右される気がする)
ただ、今回はマシンリソースの消費具合まで見れなかったが、そこも加味して判断したい。とは思う。
|処理|結果平均2|h
|boto3.resource.Objectのputメソッドを使用|1.0612|
|boto3.resource.Bucket.put_object を使用|1.0368|
|boto3.client.put_object を使用|1.068|
|boto3.resource.meta.client.upload_file を使用|1.19|
|boto3.resource.Bucket.put_object を使用(かつファイル圧縮)|1.027|
|boto3.client.put_object で 属性を指定|1.1918|
|boto3.resource.Bucket.put_object でファイル圧縮しつつ、属性を指定|0.956|
|boto3.resource.meta.client.upload_fileobj 使用(かつファイル圧縮)|1.0342|
|boto3.client.put_object を使用(ファイル圧縮)|0.9948|
#html(</div>)
** テストスクリプト [#dc02198f]
#html(<div style="padding-left:10px;">)
*** テスト処理の雛形 [#l16473a4]
#html(<div style="padding-left:10px;">)
s3_put_test_base.py
#mycode2(){{
import boto3
from datetime import datetime
import decimal
import json
import random
import time
import sys
import hashlib
class S3PutTestBase:
BUCKET_NAME = 'test-s3-put-and-get'
FILE_DIR_PATH = 'dir1/dir2/dir3'
FILE_PUT_COUNT = 10
RESULT_FILE = sys.argv[1]
TIMES = sys.argv[2]
time_info = []
@classmethod
def main(cls):
cls.print_title()
stime = time.time()
for i in range(cls.FILE_PUT_COUNT):
file_name = f'test{i+1:04d}.json'
file_key = f'{cls.FILE_DIR_PATH}/{file_name}'
file_data = cls.get_file_data(i+1)
cls.upload(cls.BUCKET_NAME, cls.add_prefix(file_key), file_data)
etime = time.time()
cls.time_info.append(etime - stime)
with open(cls.RESULT_FILE, 'a') as f:
line = f'{cls.TIMES}回目\t' + '\t'.join(list(map(lambda x: str(round(x, 3)), cls.time_info)))
f.write(f'{line}\n')
print(f'{line}')
@classmethod
def add_prefix(cls, file_key):
return file_key
#file_prefix = hashlib.md5(bytes(file_key,'utf-8')).hexdigest()[0:4]
#return f'{file_prefix}-{file_key}'
@classmethod
def print_title(cls):
res = ''
try:
with open(cls.RESULT_FILE) as f:
res = f.read()
except:
pass
if not res:
with open(cls.RESULT_FILE, 'a') as f:
line = 'N回目\t' + '\t'.join([ f'ファイル{i+1}' for i in range(cls.FILE_PUT_COUNT)]) + '\t合計'
f.write(f'{line}\n')
if sys.argv[2] == '1':
line = 'N回目\t' + '\t'.join([ f'ファイル{i+1}' for i in range(cls.FILE_PUT_COUNT)]) + '\t合計'
print(line)
@classmethod
def get_file_data(cls,file_no):
data = {
'var1': 'abcdefg',
'var2': 'xyz1234',
'data': [{f'key{i+1:08d}': random.randrange(1,100) } for i in range(file_no*100)]
}
data_bytes = bytes(json.dumps(data, default=decimal.Decimal), 'utf-8')
etime = time.time()
return data_bytes
}}
#html(</div>)
*** boto3.resource.Object.put を使用 [#l628eed2]
#html(<div style="padding-left:10px;">)
s3_put_test1.py
#mycode2(){{
import boto3
from datetime import datetime
import decimal
import json
import random
import time
import sys
from s3_put_test_base import S3PutTestBase
class S3PutTest1(S3PutTestBase):
"""
boto3.resource.Object.put を使用
"""
s3 = boto3.resource('s3')
@classmethod
def upload(cls, bucket_name, file_key, file_bytes):
stime = time.time()
s3_obj = cls.s3.Object(bucket_name, file_key)
result = s3_obj.put(Body = file_bytes)
etime = time.time()
cls.time_info.append(etime - stime)
return result
if __name__ == '__main__':
S3PutTest1.main()
}}
#html(</div>)
*** boto3.resource.Bucket の put_object を使用 [#xb1ead91]
#html(<div style="padding-left:10px;">)
s3_put_test2.py
#mycode2(){{
import boto3
from datetime import datetime
import decimal
import json
import random
import time
from s3_put_test_base import S3PutTestBase
class S3PutTest2(S3PutTestBase):
"""
boto3.resource.Bucket.put_object を使用
"""
s3_bucket = boto3.resource('s3').Bucket(S3PutTestBase.BUCKET_NAME)
@classmethod
def upload(cls, bucket_name, file_key, file_bytes):
stime = time.time()
s3_object = cls.s3_bucket.put_object(
Key=file_key,
Body=file_bytes
)
etime = time.time()
cls.time_info.append(etime - stime)
return s3_object
if __name__ == '__main__':
S3PutTest2.main()
}}
#html(</div>)
*** boto3.client.put_object を使用 [#yc731a7d]
#html(<div style="padding-left:10px;">)
s3_put_test3.py
#mycode2(){{
import boto3
from datetime import datetime
import decimal
import json
import random
import time
import sys
from s3_put_test_base import S3PutTestBase
class S3PutTest3(S3PutTestBase):
"""
boto3.client.put_object を使用
"""
s3_client = boto3.client('s3')
@classmethod
def upload(cls, bucket_name, file_key, file_bytes):
stime = time.time()
result = cls.s3_client.put_object(
Bucket=bucket_name,
Key=file_key,
Body=file_bytes
)
etime = time.time()
cls.time_info.append(etime - stime)
return result
if __name__ == '__main__':
S3PutTest3.main()
}}
#html(</div>)
*** boto3.resource.meta.client.upload_file を使用 [#meedabf5]
#html(<div style="padding-left:10px;">)
s3_put_test4.py
#mycode2(){{
import boto3
from datetime import datetime
import decimal
import json
import random
import time
import sys
import uuid
import os
from s3_put_test_base import S3PutTestBase
class S3PutTest4(S3PutTestBase):
"""
boto3.resource.meta の client.upload_file を使用
"""
s3_meta_client = boto3.resource('s3').meta.client
@classmethod
def upload(cls, bucket_name, file_key, file_bytes):
stime = time.time()
tmp_file_name = '/tmp/' + str(uuid.uuid1()) + '.json'
with open(tmp_file_name, 'wb') as f:
f.write(file_bytes)
result = s3_meta_client.upload_file(tmp_file_name, bucket_name, file_key)
os.remove(tmp_file_name)
etime = time.time()
cls.time_info.append(etime - stime)
return result
if __name__ == '__main__':
S3PutTest4.main()
}}
#html(</div>)
*** boto3.resource.Bucket.put_object を使用(かつファイル圧縮) [#wf49f33d]
#html(<div style="padding-left:10px;">)
s3_put_test5.py
#mycode2(){{
import boto3
from datetime import datetime
import decimal
import gzip
import json
import random
import time
from s3_put_test_base import S3PutTestBase
class S3PutTest5(S3PutTestBase):
"""
s3_bucket.put_object を使用(かつファイル圧縮)
"""
s3_bucket = boto3.resource('s3').Bucket(S3PutTestBase.BUCKET_NAME)
@classmethod
def upload(cls, bucket_name, file_key, file_bytes):
stime = time.time()
file_bytes = gzip.compress(file_bytes)
s3_object = cls.s3_bucket.put_object(
Key=file_key,
Body=file_bytes
)
etime = time.time()
cls.time_info.append(etime - stime)
return s3_object
if __name__ == '__main__':
S3PutTest5.main()
}}
#html(</div>)
*** boto3.client.put_object で 属性を指定してアップロード [#xf046ac9]
#html(<div style="padding-left:10px;">)
s3_put_test6.py
#mycode2(){{
import boto3
from datetime import datetime
import decimal
import json
import random
import time
import sys
from s3_put_test_base import S3PutTestBase
class S3PutTest6(S3PutTestBase):
"""
boto3.client.put_object で 属性を指定してアップロード
"""
s3_client = boto3.client('s3')
@classmethod
def upload(cls, bucket_name, file_key, file_bytes):
stime = time.time()
result = cls.s3_client.put_object(
ACL='private',
Bucket=bucket_name,
Key=file_key,
Body=file_bytes,
ContentType='application/json',
ContentLength=len(file_bytes)
)
etime = time.time()
cls.time_info.append(etime - stime)
return result
if __name__ == '__main__':
S3PutTest6.main()
}}
#html(</div>)
*** boto3.resource.Bucket.put_object でファイル圧縮しつつ、属性を指定 [#s3f9ba3b]
#html(<div style="padding-left:10px;">)
s3_put_test7.py
#mycode2(){{
import boto3
from datetime import datetime
import decimal
import json
import random
import time
import gzip
from s3_put_test_base import S3PutTestBase
class S3PutTest7(S3PutTestBase):
"""
boto3.resource.Bucket.put_object でファイル圧縮しつつ、属性を指定
"""
s3_bucket = boto3.resource('s3').Bucket(S3PutTestBase.BUCKET_NAME)
@classmethod
def upload(cls, bucket_name, file_key, file_bytes):
stime = time.time()
file_bytes = gzip.compress(file_bytes)
s3_object = cls.s3_bucket.put_object(
ACL='private',
Key=file_key,
Body=file_bytes,
ContentType='application/gzip',
ContentLength=len(file_bytes)
)
etime = time.time()
cls.time_info.append(etime - stime)
return s3_object
if __name__ == '__main__':
S3PutTest7.main()
}}
#html(</div>)
*** boto3.resource.meta.client.upload_fileobj を使用 [#vff53e4f]
#html(<div style="padding-left:10px;">)
s3_put_test8.py
#mycode2(){{
import boto3
from datetime import datetime
import decimal
import json
import random
import time
import sys
import uuid
import gzip
import os
from s3_put_test_base import S3PutTestBase
class S3PutTest8(S3PutTestBase):
"""
boto3.resource.meta.client.upload_fileobj を使用
"""
s3_meta_client = boto3.resource('s3').meta.client
@classmethod
def upload(cls, bucket_name, file_key, file_bytes):
stime = time.time()
file_bytes = gzip.compress(file_bytes)
tmp_file_name = '/tmp/' + str(uuid.uuid1()) + '.gz'
with open(tmp_file_name, 'wb') as f:
f.write(file_bytes)
with open(tmp_file_name, 'rb') as f:
result = cls.s3_meta_client.upload_fileobj(f, bucket_name, file_key)
os.remove(tmp_file_name)
etime = time.time()
cls.time_info.append(etime - stime)
return result
if __name__ == '__main__':
S3PutTest8.main()
}}
#html(</div>)
#html(</div>)
** 結果集計用スクリプト [#mef40cca]
#html(<div style="padding-left:10px;">)
print_average.py
#mycode2(){{
import sys
def main(resulf_file):
num_min = []
num_max = []
num_info = []
line_count = 0
with open(resulf_file) as f:
result = f.read()
# print(result)
lines = result.split('\n')
for i,line in enumerate(lines):
if i == 0:
continue
cols = line.split('\t')
line_count += 1
for j,col in enumerate(cols):
if not col or j == 0:
continue
if i == 1:
num_info.append([])
num_min.append(999.0)
num_max.append(0.0)
num = float(col)
num_info[j-1].append(num)
if num_max[j-1] < num:
num_max[j-1] = num
if num_min[j-1] > num:
num_min[j-1] = num
averages = []
for i,nums in enumerate(num_info):
min_val = num_min[i]
max_val = num_max[i]
summary = 0.0
col_count = 0
for j,num in enumerate(nums):
if min_val == num or max_val == num:
continue
else:
col_count += 1
summary = summary + num
#print(f'{i}/{j} : {min_val} : {max_val} : {num}')
average = summary / col_count
averages.append(average)
print(f'平均({resulf_file})\t' + '\t'.join(list(map(lambda x: str(round(x, 3)), averages))))
if __name__ == '__main__':
resulf_file = sys.argv[1]
main(resulf_file)
}}
#html(</div>)
** 実行用シェル [#n010266a]
#html(<div style="padding-left:10px;">)
s3-put-test.sh
#myterm2(){{
#!/bin/bash
for i in `seq 1 8`
do
file_name="s3_put_test${i}.py"
result_file="result${i}.csv"
rm -rf $result_file
for j in `seq 1 10`
do
python3 $file_name $result_file $j
done
python3 print_average.py $result_file
done
}}
上記をさらに5回繰り返すシェル
s3-put-test-all.sh
#myterm2(){{
#!/bin/bash
for no in `seq 1 5`
do
./s3-put-test.sh | tee summaryA-${no}.csv
done
}}
#html(</div>)
** 結果 [#ya26e801]
#html(<div style="padding-left:10px;">)
#myterm2(){{
cat summaryA* | grep 平均 | sort
}}
&br;
|N回目|ファイル1|ファイル2|ファイル3|ファイル4|ファイル5|ファイル6|ファイル7|ファイル8|ファイル9|ファイル10|合計|h
|平均(result1.csv)|0.19|0.083|0.075|0.088|0.086|0.108|0.093|0.099|0.109|0.092|1.257|
|平均(result1.csv)|0.193|0.074|0.072|0.08|0.082|0.087|0.08|0.088|0.097|0.087|1.005|
|平均(result1.csv)|0.193|0.081|0.074|0.095|0.084|0.09|0.079|0.095|0.089|0.095|1.018|
|平均(result1.csv)|0.26|0.083|0.1|0.098|0.085|0.082|0.085|0.096|0.097|0.102|1.225|
|平均(result1.csv)|0.265|0.083|0.084|0.095|0.082|0.092|0.084|0.09|0.095|0.101|1.109|
|平均(result2.csv)|0.187|0.076|0.073|0.085|0.087|0.086|0.088|0.087|0.097|0.09|1.002|
|平均(result2.csv)|0.188|0.081|0.074|0.087|0.084|0.102|0.088|0.094|0.096|0.092|1.043|
|平均(result2.csv)|0.207|0.092|0.078|0.089|0.083|0.087|0.081|0.116|0.099|0.092|1.064|
|平均(result2.csv)|0.222|0.08|0.075|0.081|0.088|0.084|0.086|0.115|0.097|0.109|1.114|
|平均(result2.csv)|0.225|0.078|0.088|0.089|0.078|0.085|0.132|0.098|0.096|0.085|1.179|
|平均(result3.csv)|0.184|0.076|0.088|0.081|0.084|0.1|0.093|0.095|0.099|0.097|1.048|
|平均(result3.csv)|0.184|0.081|0.073|0.094|0.132|0.088|0.106|0.086|0.089|0.088|1.108|
|平均(result3.csv)|0.185|0.078|0.079|0.09|0.084|0.084|0.094|0.115|0.099|0.09|1.111|
|平均(result3.csv)|0.191|0.077|0.073|0.096|0.083|0.082|0.08|0.092|0.089|0.102|1.007|
|平均(result3.csv)|0.197|0.078|0.082|0.089|0.085|0.091|0.093|0.095|0.097|0.099|1.047|
|平均(result4.csv)|0.207|0.099|0.089|0.091|0.098|0.092|0.112|0.114|0.099|0.112|1.217|
|平均(result4.csv)|0.208|0.08|0.074|0.099|0.086|0.098|0.092|0.099|0.1|0.103|1.1|
|平均(result4.csv)|0.213|0.086|0.087|0.09|0.089|0.094|0.102|0.107|0.104|0.102|1.177|
|平均(result4.csv)|0.215|0.09|0.084|0.094|0.089|0.138|0.094|0.114|0.107|0.105|1.3|
|平均(result4.csv)|0.219|0.211|0.082|0.106|0.095|0.094|0.094|0.105|0.104|0.106|1.302|
|平均(result5.csv)|0.181|0.079|0.076|0.078|0.09|0.075|0.085|0.081|0.077|0.082|0.967|
|平均(result5.csv)|0.201|0.081|0.075|0.082|0.069|0.071|0.074|0.086|0.083|0.077|0.975|
|平均(result5.csv)|0.202|0.075|0.072|0.074|0.07|0.075|0.078|0.078|0.073|0.078|0.951|
|平均(result5.csv)|0.209|0.079|0.07|0.074|0.071|0.079|0.076|0.079|0.071|0.073|0.947|
|平均(result5.csv)|0.346|0.077|0.071|0.078|0.079|0.072|0.081|0.077|0.075|0.08|1.093|
|平均(result6.csv)|0.187|0.079|0.077|0.096|0.08|0.084|0.082|0.085|0.09|0.094|1.031|
|平均(result6.csv)|0.189|0.077|0.077|0.094|0.082|0.087|0.086|0.094|0.089|0.117|1.047|
|平均(result6.csv)|0.208|0.087|0.077|0.097|0.095|0.094|0.095|0.095|0.104|0.111|1.156|
|平均(result6.csv)|0.237|0.088|0.075|0.089|0.088|0.086|0.095|0.092|0.092|0.11|1.113|
|平均(result6.csv)|0.248|0.08|0.076|0.091|0.088|0.095|0.088|0.091|0.098|0.097|1.171|
|平均(result7.csv)|0.189|0.077|0.08|0.076|0.069|0.07|0.077|0.075|0.072|0.072|0.907|
|平均(result7.csv)|0.189|0.085|0.075|0.084|0.077|0.074|0.079|0.076|0.079|0.083|0.973|
|平均(result7.csv)|0.193|0.076|0.075|0.078|0.072|0.093|0.075|0.077|0.074|0.076|0.952|
|平均(result7.csv)|0.232|0.086|0.08|0.101|0.08|0.078|0.075|0.089|0.077|0.09|1.039|
|平均(result7.csv)|0.238|0.081|0.081|0.075|0.079|0.074|0.08|0.086|0.085|0.077|1.089|
|平均(result8.csv)|0.193|0.082|0.069|0.081|0.073|0.074|0.088|0.084|0.077|0.078|0.983|
|平均(result8.csv)|0.207|0.082|0.087|0.075|0.074|0.076|0.075|0.079|0.08|0.078|0.975|
|平均(result8.csv)|0.211|0.084|0.078|0.085|0.076|0.076|0.078|0.08|0.082|0.081|0.977|
|平均(result8.csv)|0.218|0.075|0.078|0.091|0.073|0.079|0.08|0.076|0.091|0.083|0.995|
|平均(result8.csv)|0.232|0.081|0.078|0.076|0.074|0.074|0.076|0.079|0.085|0.086|1.084|
#html(</div>)