はじめに
このブログでおすすめしている、エクセルライブラリ「SpreadSheetLight」と、有名な「ClosedXML」「NPOI」での書き込み速度の比較について解説します。
「Spreadsheetってなに?」という方は、以下の記事もご覧ください
同じ27インチで1.8倍の情報量
Excelで大量のデータを扱うなら、高解像度のディスプレイが絶対おすすめです。WQHD対応モニターなら、同サイズのフルHDと比べて1.8倍の情報量。解像度も高いので目にもやさしいです。30%オフなので私も仕事用に買いました。
比較結果
一万セル(100行×100列)に数値「1」を書き込んで、ファイルの保存が完了するまでを計測しています。処理時間は30回繰り返した際の平均値です。
最速の「NPOI」に対し「SpreadSheetLight」は2.6倍程度であることが分かりました。
ライブラリ 名 | 処理時間 | 比較 | |
NPOI | 81msec | — | |
ClosedXML | 129msec | 1.6倍 | |
SpreadSheetLight | 207msec | 2.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 |
.NET | 4.7.2 |
プロジェクト | コンソールアプリケーション(.NET Framework) |
SpreadsheetLight | 3.5.0 |
ClosedXML | 0.95.4 |
NPOI | 2.5.3 |
CPU | Core i 5 3427U 1.8Ghz × 2 |
SSD | STAT 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は「もっとTypeScriptの力を引き出そう」
JavaScriptの拡張言語でしょ?と思っているかたへ、Union型などTypeScriptの持つ秘めたる力を解説する特集となっています。
個人的には第2特集の「Ubuntuの現代的な使い方」がの方が気になりました。より詳しい内容は以下のリンクからご覧ください。
質問・要望 大歓迎です
「こんな解説記事作って」「こんなことがしたいけど、〇〇で困ってる」など、コメント欄で教えてください。 質問・要望に、中の人ができる限り対応します。
使えたよ・設定できたよの一言コメントも大歓迎。気軽に足跡を残してみてください。記事を紹介したい方はブログ、SNSにバシバシ貼ってもらってOKです。