Skip to content

Enhance shared-string caching and buffer management in ExcelReader - #28

Merged
GabrielMarquezMatte merged 3 commits into
masterfrom
develop
Jul 11, 2026
Merged

Enhance shared-string caching and buffer management in ExcelReader#28
GabrielMarquezMatte merged 3 commits into
masterfrom
develop

Conversation

@GabrielMarquezMatte

Copy link
Copy Markdown
Owner

This pull request introduces several performance and memory optimizations for reading Excel files, particularly around buffer sizing, pooling, and string deduplication. The main themes are improved buffer management for large files, reduced allocations via pooling, and more efficient handling of shared strings in XLS and XLSB readers.

Buffer management and pooling improvements:

  • The initial buffer size for reading worksheet streams is now dynamically sized based on the actual uncompressed entry length, reducing the number of buffer expansions and memory moves for large sheets (WorkbookLookups.InitialBufferCapacity, BufferedStreamCursor, XlsbReader.Enumerator). [1] [2] [3] [4] [5]
  • Sector chains and FAT tables in XLS compound files are now rented from the shared array pool and returned after use, minimizing allocations and improving memory reuse (XlsCompoundFile.cs, WorkbookStream.cs). [1] [2] [3] [4] [5] [6] [7]

Shared string deduplication:

  • Both XlsReader and XlsbReader now lazily cache shared/categorical strings by their byte offset, ensuring repeated values are only decoded once per worksheet, which reduces memory and CPU usage for files with many repeated strings. [1] [2] [3] [4] [5] [6]

CSV and parsing optimizations:

  • The CSV parser now avoids redundant type map construction for types with no date properties, reusing the main map when possible for better performance (TypeMapper<T>). [1] [2]
  • Enumerator methods in CSV and XLS readers have been refactored for faster common-case iteration, reducing unnecessary checks inside tight loops. [1] [2]

These changes collectively make reading large and/or repetitive Excel files faster and more memory-efficient, especially for workloads with many repeated strings or very large worksheets.

GabrielMarquezMatte added 3 commits July 11, 2026 15:31
- Introduced a shared-string cache to optimize repeated string values in XLSX/XLSB formats.
- Updated BufferedStreamCursor to allow dynamic initial buffer sizes based on entry length.
- Improved row parsing efficiency by ensuring full row buffering before processing.
- Refactored cell and row handling to accommodate shared-string caching, enhancing performance for large datasets.
@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 91.26984% with 11 lines in your changes missing coverage. Please review.
✅ Project coverage is 89.12%. Comparing base (ac94393) to head (a4e6818).

Files with missing lines Patch % Lines
...c/ExcelReader.Core/Reader/XlsxReader.Enumerator.cs 86.41% 6 Missing and 5 partials ⚠️
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.
Additional details and impacted files
@@            Coverage Diff             @@
##           master      #28      +/-   ##
==========================================
- Coverage   89.22%   89.12%   -0.10%     
==========================================
  Files          77       77              
  Lines        5345     5363      +18     
  Branches      956      970      +14     
==========================================
+ Hits         4769     4780      +11     
- Misses        377      383       +6     
- Partials      199      200       +1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@github-actions

Copy link
Copy Markdown

Benchmark Results

Measured on ubuntu-latest (GitHub Actions). Runner noise may affect absolute numbers; use these for relative comparisons within a PR.

ExcelReader.Benchmarks.CsvParseBenchmark


BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 7763 2.84GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.301
  [Host]     : .NET 10.0.9 (10.0.9, 10.0.926.27113), X64 RyuJIT x86-64-v3
  Job-MEHJPP : .NET 10.0.9 (10.0.9, 10.0.926.27113), X64 RyuJIT x86-64-v3

IterationCount=5  WarmupCount=1  

Method Rows Mean Error StdDev Ratio RatioSD Gen0 Gen1 Allocated Alloc Ratio
ExcelParserSync 50000 8.194 ms 0.2052 ms 0.0318 ms 1.00 0.00 234.3750 - 3.86 MB 1.00
ExcelParserAsync 50000 8.299 ms 0.1677 ms 0.0259 ms 1.01 0.00 234.3750 - 3.86 MB 1.00
Sep 50000 14.683 ms 0.2348 ms 0.0610 ms 1.79 0.01 234.3750 - 3.87 MB 1.00
Sylvan 50000 19.467 ms 0.4046 ms 0.1051 ms 2.38 0.01 656.2500 62.5000 10.95 MB 2.84
CsvHelperLib 50000 35.650 ms 0.5527 ms 0.1435 ms 4.35 0.02 866.6667 66.6667 14.41 MB 3.73

ExcelReader.Benchmarks.CsvReadBenchmark


BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 7763 2.84GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.301
  [Host]     : .NET 10.0.9 (10.0.9, 10.0.926.27113), X64 RyuJIT x86-64-v3
  Job-MEHJPP : .NET 10.0.9 (10.0.9, 10.0.926.27113), X64 RyuJIT x86-64-v3

IterationCount=5  WarmupCount=1  

Method Rows Mean Error StdDev Ratio RatioSD Gen0 Gen1 Allocated Alloc Ratio
ExcelReader 50000 5.684 ms 0.0698 ms 0.0181 ms 1.00 0.00 - - 232 B 1.00
ExcelReaderAsync 50000 5.774 ms 0.0263 ms 0.0041 ms 1.02 0.00 - - 352 B 1.52
Sep 50000 13.501 ms 0.1637 ms 0.0425 ms 2.38 0.01 - - 4024 B 17.34
Sylvan 50000 6.928 ms 0.0706 ms 0.0183 ms 1.22 0.00 93.7500 7.8125 1688701 B 7,278.88
CsvHelperLib 50000 37.428 ms 0.2770 ms 0.0719 ms 6.58 0.02 857.1429 71.4286 15073424 B 64,971.66

ExcelReader.Benchmarks.CsvWriteBenchmark


BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 7763 2.84GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.301
  [Host]     : .NET 10.0.9 (10.0.9, 10.0.926.27113), X64 RyuJIT x86-64-v3
  Job-MEHJPP : .NET 10.0.9 (10.0.9, 10.0.926.27113), X64 RyuJIT x86-64-v3

IterationCount=5  WarmupCount=1  

Method Rows Mean Error StdDev Ratio Gen0 Gen1 Gen2 Allocated Alloc Ratio
ExcelReaderWriter 50000 9.831 ms 0.2050 ms 0.0317 ms 1.00 500.0000 500.0000 500.0000 4 MB 1.00
Sep 50000 10.279 ms 0.1089 ms 0.0168 ms 1.05 500.0000 500.0000 500.0000 4.01 MB 1.00
SylvanWriter 50000 10.737 ms 0.2063 ms 0.0536 ms 1.09 500.0000 500.0000 500.0000 4.04 MB 1.01
CsvHelperLib 50000 22.368 ms 0.2600 ms 0.0402 ms 2.28 1187.5000 656.2500 593.7500 13.79 MB 3.44

ExcelReader.Benchmarks.ParseBenchmark


BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 7763 2.45GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.301
  [Host]     : .NET 10.0.9 (10.0.9, 10.0.926.27113), X64 RyuJIT x86-64-v3
  Job-MEHJPP : .NET 10.0.9 (10.0.9, 10.0.926.27113), X64 RyuJIT x86-64-v3

IterationCount=5  WarmupCount=1  

Method Rows Mean Error StdDev Ratio RatioSD Gen0 Allocated Alloc Ratio
ExcelParserSync 50000 19.970 ms 0.1152 ms 0.0299 ms 1.00 0.00 218.7500 3.88 MB 1.00
ExcelParserAsync 50000 20.914 ms 0.1910 ms 0.0296 ms 1.05 0.00 218.7500 3.88 MB 1.00
ExcelParserXlsbSync 50000 9.151 ms 0.0482 ms 0.0075 ms 0.46 0.00 234.3750 3.88 MB 1.00
ExcelParserXlsbAsync 50000 10.099 ms 0.2403 ms 0.0624 ms 0.51 0.00 234.3750 3.88 MB 1.00
MiniExcel 50000 224.255 ms 6.6762 ms 1.0332 ms 11.23 0.05 12000.0000 197.8 MB 51.05
Sylvan 50000 88.563 ms 1.0710 ms 0.1657 ms 4.43 0.01 - 10.48 MB 2.70
SylvanAsync 50000 89.013 ms 2.3391 ms 0.3620 ms 4.46 0.02 - 10.48 MB 2.70

ExcelReader.Benchmarks.ReadBenchmark


BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 7763 2.45GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.301
  [Host]     : .NET 10.0.9 (10.0.9, 10.0.926.27113), X64 RyuJIT x86-64-v3
  Job-MEHJPP : .NET 10.0.9 (10.0.9, 10.0.926.27113), X64 RyuJIT x86-64-v3

IterationCount=5  WarmupCount=1  

Method Rows Mean Error StdDev Ratio RatioSD Gen0 Allocated Alloc Ratio
ExcelReader 50000 16.849 ms 0.2479 ms 0.0644 ms 1.00 0.00 - 12.58 KB 1.00
ExcelReaderAsync 50000 16.988 ms 0.0963 ms 0.0149 ms 1.01 0.00 - 14.59 KB 1.16
ExcelReaderXlsb 50000 6.739 ms 0.0277 ms 0.0043 ms 0.40 0.00 - 14.32 KB 1.14
ExcelReaderXlsbAsync 50000 6.883 ms 0.0895 ms 0.0138 ms 0.41 0.00 - 16.76 KB 1.33
MiniExcel 50000 224.928 ms 3.2254 ms 0.4991 ms 13.35 0.05 13000.0000 214012.34 KB 17,014.65
Sylvan 50000 56.407 ms 3.7495 ms 0.5802 ms 3.35 0.03 - 1939.18 KB 154.17

ExcelReader.Benchmarks.WriteBenchmark


BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 7763 2.59GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.301
  [Host]     : .NET 10.0.9 (10.0.9, 10.0.926.27113), X64 RyuJIT x86-64-v3
  Job-MEHJPP : .NET 10.0.9 (10.0.9, 10.0.926.27113), X64 RyuJIT x86-64-v3

IterationCount=5  WarmupCount=1  

Method Rows Mean Error StdDev Ratio RatioSD Gen0 Gen1 Gen2 Allocated Alloc Ratio
ExcelReaderWriter 50000 19.473 ms 0.6734 ms 0.1749 ms 1.00 0.01 500.0000 500.0000 500.0000 4.02 MB 1.00
ExcelReaderWriterSharedStrings 50000 19.893 ms 0.3771 ms 0.0979 ms 1.02 0.01 500.0000 500.0000 500.0000 4.06 MB 1.01
ExcelReaderXlsbWriter 50000 9.511 ms 0.0795 ms 0.0123 ms 0.49 0.00 500.0000 500.0000 500.0000 4.02 MB 1.00
ExcelReaderXlsbWriterSharedStrings 50000 9.304 ms 0.5452 ms 0.1416 ms 0.48 0.01 500.0000 500.0000 500.0000 4.06 MB 1.01
MiniExcel 50000 102.098 ms 4.2367 ms 0.6556 ms 5.24 0.05 5000.0000 1000.0000 1000.0000 84.89 MB 21.13
SpreadCheetah 50000 21.331 ms 0.5370 ms 0.1395 ms 1.10 0.01 1437.5000 718.7500 718.7500 15.84 MB 3.94

ExcelReader.Benchmarks.XlsReadBenchmark


BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 7763 2.67GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.301
  [Host]     : .NET 10.0.9 (10.0.9, 10.0.926.27113), X64 RyuJIT x86-64-v3
  Job-MEHJPP : .NET 10.0.9 (10.0.9, 10.0.926.27113), X64 RyuJIT x86-64-v3

IterationCount=5  WarmupCount=1  

Method Rows Mean Error StdDev Ratio Gen0 Gen1 Allocated Alloc Ratio
ExcelReader 50000 6.834 ms 0.0046 ms 0.0007 ms 1.00 - - 2.89 KB 1.00
ExcelReaderAsync 50000 6.912 ms 0.0098 ms 0.0015 ms 1.01 - - 2.96 KB 1.02
Sylvan 50000 8.588 ms 0.1971 ms 0.0305 ms 1.26 93.7500 46.8750 1717.73 KB 594.24

ExcelReader.Benchmarks.XlsWriteBenchmark


BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 7763 2.67GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.301
  [Host]     : .NET 10.0.9 (10.0.9, 10.0.926.27113), X64 RyuJIT x86-64-v3
  Job-MEHJPP : .NET 10.0.9 (10.0.9, 10.0.926.27113), X64 RyuJIT x86-64-v3

IterationCount=5  WarmupCount=1  

Method Rows Mean Error StdDev Ratio RatioSD Gen0 Gen1 Gen2 Allocated Alloc Ratio
XlsWriter 50000 7.275 ms 0.2560 ms 0.0665 ms 1.00 0.01 492.1875 492.1875 492.1875 16.03 MB 1.00
XlsxWriter 50000 19.458 ms 0.2044 ms 0.0531 ms 2.67 0.02 500.0000 500.0000 500.0000 4.02 MB 0.25

@GabrielMarquezMatte
GabrielMarquezMatte merged commit a68dbdd into master Jul 11, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants