C# PR

【C#】エクセルライブラリの速度の比較 ClosedXML・NPOI・SpreadSheetLight 【書き込み編】

記事内に商品プロモーションを含む場合があります

はじめに

このブログでおすすめしている、エクセルライブラリ「SpreadSheetLight」と、有名な「ClosedXML」「NPOI」での書き込み速度の比較について解説します。

この記事の内容

「SpreadSheetLight」と有名ライブラリの「書き込みの速度」を比較

「Spreadsheetってなに?」という方は、以下の記事もご覧ください

同じ27インチで1.8倍の情報量

Excelで大量のデータを扱うなら、高解像度のディスプレイが絶対おすすめです。WQHD対応モニターなら、同サイズのフルHDと比べて1.8倍の情報量。解像度も高いので目にもやさしいです。30%オフなので私も仕事用に買いました。

比較結果

一万セル(100行×100列)に数値「1」を書き込んで、ファイルの保存が完了するまでを計測しています。処理時間は30回繰り返した際の平均値です。

最速の「NPOI」に対し「SpreadSheetLight」は2.6倍程度であることが分かりました。

ライブラリ 名処理時間比較
NPOI81msec
ClosedXML129msec     1.6倍
SpreadSheetLight207msec2.6倍

※小数点以下は四捨五入しています。 
※ 詳細な計測方法・環境は後述します。

SpreadSheetLightは遅くて使えないの?

「NPOIと比較すると2.6倍ですが、使えます」

NPOIと比較すると2.6倍の差はありますが、1万セルの出力が「0.2秒」で完了しています。単純計算ですが、10万セルでも2秒で完了するので「速度が遅い」ということはありません。

また、爆速のNPOIですが、コードの書き方が少々難しく、直感的ではありません。
(行のオブジェクトを作成し、その中の列の要素にデータを代入して・・・など)

ClosedXMLは分かりやすいコードになりますが、グラフや罫線に対応していません。

そのため、速度の面では最速ではありませんが、分かりやすいコードで、ヒトが見やすい「エクセルを作れる「SpreadSheetLight」は十分使えるライブラリだと思います。

  • 1万セルで0.2秒ならSpreadsheetLightは十分早い
  • NPOIは実装が難しい
  • ClosedXMLはやさしいが、罫線・グラフの作成ができない
  • SpreadsheetLightはコードもやさしい罫線グラフにも対応

比較方法

環境

今回の計測に使用した環境は以下の通りです。実行時はRelese版の実行ファイルを使用しています。
ごらんの通り化石とかしたPCで計測しているので、最新スペックのPCを使用すれはさらに早い結果となるかと思います。

環境バージョンなど
VisualStudio   2019 Community
.NET4.7.2
プロジェクトコンソールアプリケーション(.NET Framework) 
SpreadsheetLight3.5.0
ClosedXML0.95.4
NPOI2.5.3
CPUCore i 5 3427U 1.8Ghz × 2
SSDSTAT SSD
メモリ4GB

計測コードの内容

計測コードの内容は以下の通りです。

  • 100行×100列に数値「1」が入るエクセルの作成・書き込みを30回繰り返し。
  • メイン関数の処理は共通。
  • WriteExcel()の中でライブラリごとのExcel出力処理を記載。

計測コード

SpreadsheetLight

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

// SpreadSheetLight関連に使用します
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Spreadsheet;
using SpreadsheetLight;

namespace ConsoleAppSpreadsheetLight_speedTest
{
    class Program
    {
        static void Main(string[] args)
        {
            // 経過時間の集計用です
            List<long> resultTimes = new List<long>();

            // エクセルの書き込みを30回繰り返して計測します
            for (int i = 0; i < 30; i++)
            {
                long time = WriteExcel();
                resultTimes.Add(time);
            }

            // 平均時間を計算します
            double average = resultTimes.Average();

            // 平均時間を出力します
            Console.WriteLine("SpreadsheetLight Average Time: " + average + " msec ");

            // 1回ごとの時間を出力します
            foreach ( long time in resultTimes )
            {
                Console.WriteLine(" -- Time: " + time + " msec ");
            }

            // 終了待ちです
            Console.ReadKey();
        }


        // Excelデータを書き込みます
        static long WriteExcel()
        {
            // 計測用のストップウォッチです
            System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();

            // 計測を開始します
            sw.Start();

            // Excel生成用のオブジェクトを生成します
            using (SLDocument sl = new SLDocument())
            {
                // 千個(10列x100行)のデータを書き出します
                for (int col = 1; col < 100+1; col++)
                {
                    for (int row = 1; row < 100+1; row++)
                    {
                        sl.SetCellValue(col, row, 1);
                    }
                }

                // エクセルデータを出力します。
                sl.SaveAs("SpreadsheetLight_Speed.xlsx");
            }

            // ストップウォッチを停止します
            sw.Stop();

            // 経過時間を返します
            return sw.ElapsedMilliseconds;

        }
    }
}

ClosedXML

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

// ClosedXML関連で必要です
using ClosedXML.Excel;

namespace ConsoleAppClosedXml_SpeedTest
{
    class Program
    {
        static void Main(string[] args)
        {
            // 経過時間の集計用です
            List<long> resultTimes = new List<long>();

            // エクセルの書き込みを30回繰り返して計測します
            for (int i = 0; i < 30; i++)
            {
                long time = WriteExcel();
                resultTimes.Add(time);
            }

            // 平均時間を計算します
            double average = resultTimes.Average();

            // 平均時間を出力します
            Console.WriteLine("ClosedXML Average Time: " + average + " msec ");

            // 1回ごとの時間を出力します
            foreach (long time in resultTimes)
            {
                Console.WriteLine(" -- Time: " + time + " msec ");
            }

            // 終了待ちです
            Console.ReadKey();
        }

        static long WriteExcel()
        {
            // 計測用のストップウォッチです
            System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();

            // 計測を開始します
            sw.Start();

            // Excelファイルを生成します
            using (var workbook = new XLWorkbook()) 
            { 
                // ワークシート(新規)追加します
                var worksheet = workbook.Worksheets.Add("test");
           
                // 行のループです
                for (int rowIndex = 1; rowIndex < 100+1; rowIndex++)
                {
                    // 列のループです
                    for (int col = 1; col < 100+1; col++)
                    {
                        // セルにデータを書き込みます
                        worksheet.Cell(rowIndex, col).SetValue(1);
                    }
                }
                // エクセルファイルを書き込みます
                workbook.SaveAs(@"C:\ProgramData\test.xlsx");
            }

            // ストップウォッチを停止します
            sw.Stop();

            // 経過時間を返します
            return sw.ElapsedMilliseconds;
        }
    }

NPOI

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.IO;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;

namespace ConsoleAppNPOI_SpeedTest
{
    class Program
    {
        static void Main(string[] args)
        {
            // 経過時間の集計用です
            List<long> resultTimes = new List<long>();

            // エクセルの書き込みを30回繰り返して計測します
            for (int i = 0; i < 30; i++)
            {
                long time = WriteExcel();
                resultTimes.Add(time);
            }

            // 平均時間を計算します
            double average = resultTimes.Average();

            // 平均時間を出力します
            Console.WriteLine("NPOI Average Time: " + average + " msec ");

            // 1回ごとの時間を出力します
            foreach (long time in resultTimes)
            {
                Console.WriteLine(" -- Time: " + time + " msec ");
            }

            // 終了待ちです
            Console.ReadKey();
        }

        static long WriteExcel()
        {
            // 計測用のストップウォッチです
            System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();

            // 計測を開始します
            sw.Start();

            // 書き込み用のファイルストリームを作成します
            using (var fs = new FileStream(@"C:\ProgramData\test.xlsx", FileMode.Create, FileAccess.Write))
            {

                // エクセル用のオブジェクトを生成します
                IWorkbook workbook = new XSSFWorkbook();

                // エクセルから、シート用オブジェクトを作成します
                ISheet sheet = workbook.CreateSheet("sheet1");

                // 行を作成します
                for (int rowIndex = 0; rowIndex < 100; rowIndex++)
                {
                    IRow row = sheet.CreateRow(rowIndex);

                    // 作成した行に、1列ずつデータをセットします
                    for (int col = 0; col < 100; col++)
                    {
                        row.CreateCell(col).SetCellValue(1);
                    }
                }
                // 生成したファイルストリームで、エクセルファイルを書き込みます
                workbook.Write(fs);
            }

            // ストップウォッチを停止します
            sw.Stop();

            // 経過時間を返します
            return sw.ElapsedMilliseconds;

        }
    }
}

まとめ

このブログでおすすめしているエクセルライブラリ「SpreadSheetLight」と、有名な「ClosedXML」「NPOI」での書き込み速度の比較について解説しました。参考になればうれしいです。

お知らせ

今月号の『Software Design』はドメイン駆動設計(DDD)。実例を交えたDDDの手法の解説が特集されています。

編集:Software Design 編集部
¥1,562 (2024/03/13 12:59時点 | Amazon調べ)

質問・要望 大歓迎です

「こんな解説記事作って」「こんなことがしたいけど、〇〇で困ってる」など、コメント欄で教えてください。 質問・要望に、中の人ができる限り対応します。

使えたよ・設定できたよの一言コメントも大歓迎。気軽に足跡を残してみてください。記事を紹介したい方はブログ、SNSにバシバシ貼ってもらってOKです。

ABOUT ME
えす
現役のソフトウェアエンジニアです。 C++ C# Python を使ってます。10年ちょい設計/開発部門にいましたが、今はQAエンジニアっぽいことをしています。

COMMENT

メールアドレスが公開されることはありません。 が付いている欄は必須項目です