From 475c8b54fb13fc0e75fe08c7dcc208708b4cd875 Mon Sep 17 00:00:00 2001 From: Marko Lahma Date: Fri, 2 Jun 2023 19:01:19 +0300 Subject: [PATCH] Add benchmarks project --- .gitignore | 2 + Directory.Build.props | 2 + .../LargeExcelFileBenchmark.cs | 58 ++++++++ .../NPOI.Benchmarks/NPOI.Benchmarks.csproj | 17 +++ benchmarks/NPOI.Benchmarks/Program.cs | 4 + .../NPOI.Benchmarks/RangeValuesBenchmark.cs | 140 ++++++++++++++++++ solution/NPOI.Core.Test.sln | 7 + 7 files changed, 230 insertions(+) create mode 100644 benchmarks/NPOI.Benchmarks/LargeExcelFileBenchmark.cs create mode 100644 benchmarks/NPOI.Benchmarks/NPOI.Benchmarks.csproj create mode 100644 benchmarks/NPOI.Benchmarks/Program.cs create mode 100644 benchmarks/NPOI.Benchmarks/RangeValuesBenchmark.cs diff --git a/.gitignore b/.gitignore index 4caec99d9..f0947ab7d 100644 --- a/.gitignore +++ b/.gitignore @@ -159,3 +159,5 @@ $RECYCLE.BIN/ examples/packages examples/.vs/ /artifacts + +BenchmarkDotNet.Artifacts diff --git a/Directory.Build.props b/Directory.Build.props index 7c9cdd561..c4ea52c8c 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,5 +1,7 @@ + latest + true diff --git a/benchmarks/NPOI.Benchmarks/LargeExcelFileBenchmark.cs b/benchmarks/NPOI.Benchmarks/LargeExcelFileBenchmark.cs new file mode 100644 index 000000000..2e72b417e --- /dev/null +++ b/benchmarks/NPOI.Benchmarks/LargeExcelFileBenchmark.cs @@ -0,0 +1,58 @@ +using System.Net; +using BenchmarkDotNet.Attributes; +using NPOI.XSSF.UserModel; + +namespace NPOI.Benchmarks; + +[ShortRunJob] +[MemoryDiagnoser] +public class LargeExcelFileBenchmark +{ + private static XSSFWorkbook _loadedWorkBook; + private static MemoryStream _memoryStream; + private string _filePath; + + [GlobalSetup] + public void GlobalSetup() + { + // a 17MB Excel file is large so download it only when needed + _filePath = Path.Combine(Path.GetTempPath(), "test-performance.xlsx"); + if (!File.Exists(_filePath)) + { + Console.WriteLine("Downloading file..."); + new WebClient().DownloadFile( + "https://github.com/GrapeCity/GcExcel-Java/raw/master/benchmark/files/test-performance.xlsx", + _filePath); + Console.WriteLine("File downloaded"); + } + + var copyPath = Path.Combine(Path.GetTempPath(), "test-performance-copy.xlsx"); + if (!File.Exists(copyPath)) + { + File.Copy(_filePath, copyPath); + } + + _loadedWorkBook = new XSSFWorkbook(copyPath); + _memoryStream = new MemoryStream(); + } + + [Benchmark] + public void Load() + { + var workbook = new XSSFWorkbook(_filePath); + workbook.Dispose(); + } + + [Benchmark] + public void Write() + { + _memoryStream.Seek(0, SeekOrigin.Begin); + _loadedWorkBook.Write(_memoryStream, leaveOpen: true); + } + + [Benchmark] + public void Evaluate() + { + _loadedWorkBook.GetCreationHelper().CreateFormulaEvaluator().EvaluateAll(); + } +} \ No newline at end of file diff --git a/benchmarks/NPOI.Benchmarks/NPOI.Benchmarks.csproj b/benchmarks/NPOI.Benchmarks/NPOI.Benchmarks.csproj new file mode 100644 index 000000000..ffc5d92ee --- /dev/null +++ b/benchmarks/NPOI.Benchmarks/NPOI.Benchmarks.csproj @@ -0,0 +1,17 @@ + + + + Exe + net6.0 + enable + + + + + + + + + + + diff --git a/benchmarks/NPOI.Benchmarks/Program.cs b/benchmarks/NPOI.Benchmarks/Program.cs new file mode 100644 index 000000000..22a3ae331 --- /dev/null +++ b/benchmarks/NPOI.Benchmarks/Program.cs @@ -0,0 +1,4 @@ +using BenchmarkDotNet.Running; +using NPOI.Benchmarks; + +BenchmarkSwitcher.FromAssembly(typeof(LargeExcelFileBenchmark).Assembly).Run(); \ No newline at end of file diff --git a/benchmarks/NPOI.Benchmarks/RangeValuesBenchmark.cs b/benchmarks/NPOI.Benchmarks/RangeValuesBenchmark.cs new file mode 100644 index 000000000..ea2868222 --- /dev/null +++ b/benchmarks/NPOI.Benchmarks/RangeValuesBenchmark.cs @@ -0,0 +1,140 @@ +using BenchmarkDotNet.Attributes; +using NPOI.SS.Util; +using NPOI.XSSF.UserModel; + +namespace NPOI.Benchmarks; + +[MemoryDiagnoser] +public class RangeValuesBenchmark +{ + private static readonly MemoryStream _memoryStream = new(); + + [Params(1_000)] + public int RowCount { get; set; } + + [Params(30)] + public int ColumnCount { get; set; } + + [Benchmark] + public void Double() + { + var workbook = new XSSFWorkbook(); + var worksheet = workbook.CreateSheet("poi"); + + for (var r = 0; r < RowCount; r++) + { + var row = worksheet.CreateRow(r); + for (var c = 0; c < ColumnCount; c++) + { + row.CreateCell(c).SetCellValue(r + c); + } + } + + for (var r = 0; r < RowCount; r++) + { + var row = worksheet.GetRow(r); + for (var c = 0; c < ColumnCount; c++) + { + var d = row.GetCell(c).NumericCellValue; + } + } + + WriteFileAndClose(workbook); + } + + [Benchmark] + public void String() + { + var workbook = new XSSFWorkbook(); + var worksheet = workbook.CreateSheet("poi"); + + var random = new Random(); + const string alphaNumericString = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + for (var r = 0; r < RowCount; r++) + { + var row = worksheet.CreateRow(r); + for (var c = 0; c < ColumnCount; c++) + { + row.CreateCell(c).SetCellValue(alphaNumericString[random.Next(25)].ToString()); + } + } + + for (var r = 0; r < RowCount; r++) + { + var row = worksheet.GetRow(r); + for (var c = 0; c < ColumnCount; c++) + { + var s = row.GetCell(c).StringCellValue; + } + } + + WriteFileAndClose(workbook); + } + + [Benchmark] + public void Date() + { + var workbook = new XSSFWorkbook(); + var worksheet = workbook.CreateSheet("poi"); + + for (var r = 0; r < RowCount; r++) + { + var row = worksheet.CreateRow(r); + for (var c = 0; c < ColumnCount; c++) + { + row.CreateCell(c).SetCellValue(DateTime.Now); + } + } + + for (var r = 0; r < RowCount; r++) + { + var row = worksheet.GetRow(r); + for (var c = 0; c < ColumnCount; c++) + { + var d = row.GetCell(c).DateCellValue; + } + } + + WriteFileAndClose(workbook); + } + + [Benchmark] + public void Formulas() + { + var workbook = new XSSFWorkbook(); + var worksheet = workbook.CreateSheet("poi"); + + for (var r = 0; r < RowCount; r++) + { + var row = worksheet.CreateRow(r); + for (var c = 0; c < 2; c++) + { + row.CreateCell(c).SetCellValue(r + c); + } + } + + for (var r = 0; r < RowCount; r++) + { + var row = worksheet.GetRow(r); + for (var c = 2; c < ColumnCount + 2; c++) + { + var cell = row.CreateCell(c); + var reference1 = new CellReference(r, c - 2); + var reference2 = new CellReference(r, c - 1); + cell.CellFormula = $"SUM({reference1.FormatAsString()}, {reference2.FormatAsString()})"; + } + } + + workbook.GetCreationHelper().CreateFormulaEvaluator().EvaluateAll(); + + WriteFileAndClose(workbook); + } + + private static void WriteFileAndClose(XSSFWorkbook workbook) + { + _memoryStream.Seek(0, SeekOrigin.Begin); + workbook.Write(_memoryStream, leaveOpen: true); + workbook.Close(); + } +} \ No newline at end of file diff --git a/solution/NPOI.Core.Test.sln b/solution/NPOI.Core.Test.sln index e4b0206b3..041d16c76 100644 --- a/solution/NPOI.Core.Test.sln +++ b/solution/NPOI.Core.Test.sln @@ -24,6 +24,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NPOI.OOXML.TestCases.Core", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "_build", "..\build\_build.csproj", "{94B18BCF-84E8-401F-BAAB-0496AA136628}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NPOI.Benchmarks", "..\benchmarks\NPOI.Benchmarks\NPOI.Benchmarks.csproj", "{3DA1149D-46F8-4181-9976-E002BF2BFB76}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -60,6 +62,11 @@ Global {DA2CA3BD-1CAC-470C-9FA2-611A5768A76A}.Release|Any CPU.Build.0 = Release|Any CPU {94B18BCF-84E8-401F-BAAB-0496AA136628}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {94B18BCF-84E8-401F-BAAB-0496AA136628}.Release|Any CPU.ActiveCfg = Release|Any CPU + {94B18BCF-84E8-401F-BAAB-0496AA136628}.Release|Any CPU.Build.0 = Release|Any CPU + {3DA1149D-46F8-4181-9976-E002BF2BFB76}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3DA1149D-46F8-4181-9976-E002BF2BFB76}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3DA1149D-46F8-4181-9976-E002BF2BFB76}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3DA1149D-46F8-4181-9976-E002BF2BFB76}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE