openpyxlでエクセル書き込みが遅い原因を調べたら、セル書式だった(Lambda)
私たち製造ビジネステクノロジー部では、製造業に関するエンドユーザや事業会社さんに対して、システムやサービスを事業会社さんと一緒に作成・運用しています。そんな製造業において、PLCなどのデータをエクセルにまとめるため、プログラムでエクセルを作る機会がありました。
openpyxlでエクセルを作成してみると、かなりの時間が必要でした。いろいろ調べてみたところ、セル書式の有無で時間が変わったので、本記事にまとめました。
おすすめの方
- AWS Lambdaでopenpyxlを利用したい方
- AWS Lambdaでopenpyxlでセル書式を利用したい方
- AWS Lambdaでopenpyxlでセル書式を利用した場合の処理時間を知りたい方
Lambdaをデプロイする
sam init
sam init \
--runtime python3.11 \
--name lambda-openpyxl-sample \
--app-template hello-world \
--no-tracing \
--no-application-insights \
--structured-logging \
--package-type Zip
requirements.txt
にライブラリを記載する
openpyxl
SAMテンプレート
メモリは2048MBで固定とします。
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: lambda-openpyxl-sample
Resources:
HelloWorldFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: lambda-openpyxl-sample-function
CodeUri: hello_world/
Handler: app.lambda_handler
Runtime: python3.11
MemorySize: 2048
Timeout: 900
Architectures:
- x86_64
HelloWorldFunctionLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub /aws/lambda/${HelloWorldFunction}
Lambdaコード
書き込むセルに対して、次の設定をしています。
- フォント
- 太字
- 大きさ16
- 罫線
- 上下左右あり
- 数値フォーマット
- 0.0(小数1桁)
それぞれ、次の時間を計測しました。
- エクセルにデータを追加する時間
- エクセル自体を保存する時間
import openpyxl
import time
from openpyxl.cell import Cell
from openpyxl.styles import Font, Border, Side
MY_CELL_BORDER = Border(
left=Side(style="thin"),
right=Side(style="thin"),
top=Side(style="thin"),
bottom=Side(style="thin"),
)
MY_CELL_FONT = Font(bold=True, size=16)
def lambda_handler(event, context):
wb = openpyxl.Workbook()
ws = wb.active
# 30万行 x 10列のデータを書き込む
begin_write = time.perf_counter()
for i in range(300_000):
# rowとcolで小数を作ってセルに書き込む
ws.append([make_cell(ws, float(f"{i}.{j}")) for j in range(0, 10)])
end_write = time.perf_counter()
print(f"Write time: {(end_write - begin_write)} s")
begin_save = time.perf_counter()
wb.save("/tmp/hello_world.xlsx")
end_save = time.perf_counter()
print(f"Save time: {end_save - begin_save} s")
def make_cell(ws, value):
# セルごとに書式を設定する
cell = Cell(ws, value=value)
cell.font = MY_CELL_FONT
cell.border = MY_CELL_BORDER
cell.number_format = "0.0"
return cell
if __name__ == "__main__":
lambda_handler(None, None)
セル書式なしの場合は、次のようにコメントアウトして試しました。
def make_cell(ws, value):
# セルごとに書式を設定する
cell = Cell(ws, value=value)
# cell.font = MY_CELL_FONT
# cell.border = MY_CELL_BORDER
# cell.number_format = "0.0"
return cell
デプロイ
sam build --use-container
sam deploy
Lambdaを実行してみる
aws lambda invoke \
--function-name lambda-openpyxl-sample-function \
output.txt
実行結果
セル書式の有無で明らかに実行時間が変化しました。
セル書式 | 行追加[s] | 保存[s] | 全体[s] | メモリ使用量[MB] |
---|---|---|---|---|
あり | 76.6 | 44.1 | 120.7 | 1470 |
なし | 10.4 | 36.5 | 46.9 | 1102 |
write-only modeで試してみる
WriteOnlyCell
を利用します。(書式を設定しないのに WriteOnlyCell を利用するのは意味がないと思いますが、比較実験のため利用しています。)
WriteOnlyCell
を利用しない場合は、下記を参考にしてください。
Lambdaコード
import openpyxl
import time
from openpyxl.cell import WriteOnlyCell
from openpyxl.styles import Font, Border, Side
MY_CELL_BORDER = Border(
left=Side(style="thin"),
right=Side(style="thin"),
top=Side(style="thin"),
bottom=Side(style="thin"),
)
MY_CELL_FONT = Font(bold=True, size=16)
def lambda_handler(event, context):
wb = openpyxl.Workbook(write_only=True)
ws = wb.create_sheet()
# 30万行 x 10列のデータを書き込む
begin_write = time.perf_counter()
for i in range(300_000):
# rowとcolで小数を作ってセルに書き込む
ws.append([make_cell(ws, float(f"{i}.{j}")) for j in range(0, 10)])
end_write = time.perf_counter()
print(f"Write time: {(end_write - begin_write)} s")
begin_save = time.perf_counter()
wb.save("/tmp/hello_world.xlsx")
end_save = time.perf_counter()
print(f"Save time: {end_save - begin_save} s")
def make_cell(ws, value):
# セルごとに書式を設定する
cell = WriteOnlyCell(ws, value=value)
cell.font = MY_CELL_FONT
cell.border = MY_CELL_BORDER
cell.number_format = "0.0"
return cell
セル書式なしの場合は、先ほどと同じくコメントアウトして試します。
実行結果
write-only modeでも、セル書式の有無で実行時間が変わりました。
セル書式 | 行追加[s] | 保存[s] | 全体[s] | メモリ使用量[MB] |
---|---|---|---|---|
あり | 134.0 | 3.1 | 137.1 | 201 |
なし | 50.8 | 3.2 | 54.0 | 183 |
まとめ
今回のテストコードでは、write-only modeだと合計の実行時間が増えました。WriteOnlyCellを利用する影響かもしれません。(メモリ使用量を少なくするための影響など。)
write-only | セル書式 | 行追加[s] | 保存[s] | 全体[s] | メモリ使用量[MB] |
---|---|---|---|---|---|
No | あり | 76.6 | 44.1 | 120.7 | 1470 |
No | なし | 10.4 | 36.5 | 46.9 | 1102 |
Yes | あり | 134.0 | 3.1 | 137.1 | 201 |
Yes | なし | 50.8 | 3.2 | 54.0 | 183 |