openpyxlで数万〜数十万行のエクセルを作る際の時間を調べてみた(Lambda)

openpyxlで数万〜数十万行のエクセルを作る際の時間を調べてみた(Lambda)

openpyxlで大量のデータを扱う際は、処理時間を気にかけましょう。
Clock Icon2025.03.26

私たち製造ビジネステクノロジー部では、製造業に関するエンドユーザや事業会社さんに対して、システムやサービスを事業会社さんと一緒に作成・運用しています。そんな製造業において、PLCなどのデータをエクセルにまとめるため、プログラムでエクセルを作る機会がありました。そこで、openpyxlで大量データを持つエクセルを作成した際の時間を調べてみました(AWS Lambda)。

おすすめの方

  • AWS Lambdaでopenpyxlを利用したい方
  • AWS Lambdaでopenpyxlを利用して大量データを扱った場合の時間を知りたい方

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にライブラリを記載する

requirements.txt
openpyxl

SAMテンプレート

タイムアウトは最大の900秒に設定します。メモリは次の値をそれぞれ利用します。

  • 128
  • 256
  • 512
  • 1024
  • 2048
  • 4096
  • 8192
template.yaml
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: 8192
      Timeout: 900
      Architectures:
      - x86_64

  HelloWorldFunctionLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub /aws/lambda/${HelloWorldFunction}

Lambdaコード

下記は「50万行 x 10列」の場合です。

app.py
import openpyxl
import time

def lambda_handler(event, context):
    wb = openpyxl.Workbook()
    ws = wb.active

    # 50万行 x 10列のデータを書き込む
    begin_write = time.perf_counter()
    for i in range(500_000):
        ws.append([f"Row {i}"] + [f"Column {j}" for j in range(1, 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")

デプロイ

sam build --use-container
sam deploy

Lambdaを実行してみる

aws lambda invoke \
    --function-name lambda-openpyxl-sample-function \
    output.txt

データ行数とメモリによる実行時間

データ量が多くなると、エクセル書き込みよりも保存時間が長くなりました。

以下にMarkdownのTable形式でデータをまとめました。なお、「???」の部分は、Lambdaのメモリ不足またはLambdaのタイムアウトにより不明を意味します。

128 MB

データ行数 行追加[s] 保存[s] 全体[s] メモリ使用量[MB]
1万 5.7 20.0 25.7 90
5万 (Error: Runtime exited with error: signal: killed) ??? ??? 21.2 128
10万 (Error: Runtime exited with error: signal: killed) ??? ??? 22.0 128
20万 (Error: Runtime exited with error: signal: killed) ??? ??? 23.0 128
30万 (Error: Runtime exited with error: signal: killed) ??? ??? 22.2 128
40万 (Error: Runtime exited with error: signal: killed) ??? ??? 22.1 128
50万 (Error: Runtime exited with error: signal: killed) ??? ??? 22.8 128

256 MB

データ行数 行追加[s] 保存[s] 全体[s] メモリ使用量[MB]
1万 2.9 10.0 12.9 90
5万 16.3 51.3 67.6 237
10万 (Task timed out after 901.73 seconds) ??? ??? 900 256
20万 (Task timed out after 901.63 seconds) ??? ??? 900 256
30万 (Task timed out after 901.92 seconds) ??? ??? 900 256
40万 (Task timed out after 901.92 seconds) ??? ??? 900 256
50万 (Task timed out after 903.10 seconds) ??? ??? 900 256

512 MB

データ行数 行追加[s] 保存[s] 全体[s] メモリ使用量[MB]
1万 1.4 4.9 6.3 89
5万 7.9 24.2 32.1 237
10万 16.1 50.2 66.3 423
20万 (Task timed out after 900.34 seconds) ??? ??? 900 512
30万 (Task timed out after 900.49 seconds) ??? ??? 900 512
40万 (Task timed out after 900.87 seconds) ??? ??? 900 512
50万 (Task timed out after 903.09 seconds) ??? ??? 900 512

1024 MB

データ行数 行追加[s] 保存[s] 全体[s] メモリ使用量[MB]
1万 0.7 2.4 3.1 90
5万 3.9 12.3 16.2 237
10万 8.1 24.8 32.9 423
20万 16.2 49.1 65.3 798
30万 (Error: Runtime exited with error: signal: killed) 25.1 ??? 47.7 1024
40万 (Task timed out after 900.69 seconds) ??? ??? 900 1024
50万 (Task timed out after 900.71 seconds) ??? ??? 900 1024

2048 MB

データ行数 行追加[s] 保存[s] 全体[s] メモリ使用量[MB]
1万 0.4 1.4 1.8 90
5万 2.2 6.9 9.1 237
10万 4.6 14.1 18.7 424
20万 8.2 24.3 32.5 798
30万 14.1 41.0 55.1 1195
40万 18.2 55.1 73.3 1535
50万 23.2 69.1 92.3 1857

4096 MB

データ行数 行追加[s] 保存[s] 全体[s] メモリ使用量[MB]
1万 0.4 1.3 1.7 91
5万 2.2 7.0 9.2 238
10万 4.6 14.2 18.8 424
20万 9.1 27.7 36.8 799
30万 14.2 41.4 55.6 1195
40万 17.8 54.3 72.1 1536
50万 22.8 69.3 92.1 1857

8192 MB

データ行数 行追加[s] 保存[s] 全体[s] メモリ使用量[MB]
1万 0.4 1.3 1.7 91
5万 1.9 6.0 7.8 238
10万 4.5 14.0 18.5 424
20万 9.3 27.9 37.2 799
30万 14.3 41.6 55.9 1196
40万 18.4 54.6 73.0 1537
50万 22.9 68.0 90.9 1858

Write-only mode を試してみる

公式で紹介されているWrite-only modeを試してみます。

Lambdaコード

下記は「50万行 x 10列」の場合です。write_only=Trueを設定してます。

app.py
import openpyxl
import time

def lambda_handler(event, context):
    wb = openpyxl.Workbook(write_only=True)
    ws = wb.create_sheet()

    begin_write = time.perf_counter()
    for i in range(500_000):
        ws.append([f"Row {i}"] + [f"Column {j}" for j in range(1, 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")

データ行数とメモリによる実行時間(Write-only mode)

全体的にエクセルへのデータ書き込みの時間が増えた代わりに保存時間が短くなっています。処理の合計時間は、若干ですが短くなっています。そしてメモリ使用量がかなり減っています。

128 MB

データ行数 行追加[s] 保存[s] 全体[s] メモリ使用量[MB]
1万 22.3 1.4 23.7 57
5万 111.9 6.5 118.4 81
10万 223.6 13.0 236.6 111
20万 441.5 27.1 468.6 124
30万 629.5 37.8 667.3 117
40万 (Task timed out after 900.09 seconds) 887.8 ??? 900 118
50万 (Task timed out after 900.11 seconds) ??? ??? 900 117

256 MB

データ行数 行追加[s] 保存[s] 全体[s] メモリ使用量[MB]
1万 11.3 0.7 12.0 57
5万 55.2 3.2 58.4 81
10万 110.3 6.4 116.7 111
20万 223.1 12.9 236.0 172
30万 332.1 19.2 351.3 234
40万 445.6 26.8 472.4 256
50万 570.4 34.3 604.7 251

512 MB

データ行数 行追加[s] 保存[s] 全体[s] メモリ使用量[MB]
1万 5.3 0.3 5.6 57
5万 25.5 1.5 37.0 81
10万 54.5 3.2 57.7 111
20万 106.9 6.2 113.1 173
30万 164.8 9.5 174.3 234
40万 217.2 12.6 229.8 296
50万 268.8 15.8 284.6 357

1024 MB

データ行数 行追加[s] 保存[s] 全体[s] メモリ使用量[MB]
1万 2.6 0.2 2.8 57
5万 13.1 0.8 13.9 81
10万 26.0 1.6 27.6 111
20万 54.1 3.2 57.3 173
30万 79.5 4.8 84.3 234
40万 105.8 6.3 112.1 296
50万 130.9 7.8 138.7 357

2048 MB

データ行数 行追加[s] 保存[s] 全体[s] メモリ使用量[MB]
1万 1.5 0.1 1.6 57
5万 7.3 0.4 7.7 81
10万 14.5 0.9 15.4 111
20万 28.9 1.8 30.7 173
30万 44.5 2.7 47.2 234
40万 58.6 3.6 62.2 296
50万 73.9 4.5 78.4 357

4096 MB

データ行数 行追加[s] 保存[s] 全体[s] メモリ使用量[MB]
1万 1.4 0.1 1.5 58
5万 7.2 0.4 7.6 82
10万 14.6 0.9 15.5 112
20万 29.5 1.8 31.3 173
30万 43.5 2.7 46.2 235
40万 58.4 3.6 62.0 296
50万 73.4 4.5 77.9 358

8192 MB

データ行数 行追加[s] 保存[s] 全体[s] メモリ使用量[MB]
1万 1.5 0.1 1.6 58
5万 7.2 0.5 7.7 82
10万 14.7 0.9 15.6 112
20万 29.2 1.8 31.0 174
30万 44.5 2.7 47.2 235
40万 58.4 3.6 62.0 297
50万 73.4 4.4 77.8 358

さいごに

openpyxlを利用してエクセルを作成する場合は、想定する通常のデータ量や想定する最大のデータ量で事前に実験することをおすすめします。

参考

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.