Skip to content

Enhance public API and add trimming compatibility attributes - #104

Merged
GabrielMarquezMatte merged 7 commits into
masterfrom
develop
Sep 4, 2026
Merged

Enhance public API and add trimming compatibility attributes#104
GabrielMarquezMatte merged 7 commits into
masterfrom
develop

Conversation

@GabrielMarquezMatte

Copy link
Copy Markdown
Owner

No description provided.

@github-actions

github-actions Bot commented Sep 4, 2026

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.ColdStartBenchmark


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.400
  [Host]     : .NET 10.0.11 (10.0.11, 10.0.1126.37416), X64 RyuJIT x86-64-v3
  Job-GOEFXF : .NET 10.0.11 (10.0.11, 10.0.1126.37416), X64 RyuJIT x86-64-v3

InvocationCount=1  IterationCount=1  LaunchCount=16  
RunStrategy=ColdStart  UnrollFactor=1  WarmupCount=0  

Method Rows Mean Error StdDev Allocated
TypedParseFirstUse 200 40.24 ms 0.280 ms 0.275 ms 28.38 KB
RecordWriteFirstUse 200 26.58 ms 2.256 ms 2.215 ms 84.27 KB
FluentParseFirstUse 200 35.36 ms 0.263 ms 0.258 ms 30.76 KB
FluentParseWithAttributeFallbackFirstUse 200 44.28 ms 0.954 ms 0.937 ms 32.46 KB

ExcelReader.Benchmarks.CsvParseBenchmark


BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
INTEL XEON PLATINUM 8573C 2.30GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.400
  [Host]     : .NET 10.0.11 (10.0.11, 10.0.1126.37416), X64 RyuJIT x86-64-v4
  Job-MEHJPP : .NET 10.0.11 (10.0.11, 10.0.1126.37416), X64 RyuJIT x86-64-v4

IterationCount=5  WarmupCount=1  

Method Rows Mean Error StdDev Ratio RatioSD Gen0 Gen1 Allocated Alloc Ratio
ExcelParserSync 50000 7.419 ms 0.1885 ms 0.0489 ms 1.00 0.01 46.8750 - 3.86 MB 1.00
ExcelParserAsync 50000 6.733 ms 0.0978 ms 0.0254 ms 0.91 0.01 46.8750 - 3.86 MB 1.00
Sep 50000 10.300 ms 0.1297 ms 0.0337 ms 1.39 0.01 46.8750 - 3.87 MB 1.00
Sylvan 50000 14.402 ms 0.1670 ms 0.0434 ms 1.94 0.01 125.0000 15.6250 10.95 MB 2.84
CsvHelperLib 50000 28.820 ms 0.6808 ms 0.1054 ms 3.88 0.03 156.2500 - 14.41 MB 3.73

ExcelReader.Benchmarks.CsvReadBenchmark


BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 9V74 2.60GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.400
  [Host]     : .NET 10.0.11 (10.0.11, 10.0.1126.37416), X64 RyuJIT x86-64-v3
  Job-MEHJPP : .NET 10.0.11 (10.0.11, 10.0.1126.37416), X64 RyuJIT x86-64-v3

IterationCount=5  WarmupCount=1  

Method Rows Mean Error StdDev Ratio Gen0 Gen1 Allocated Alloc Ratio
ExcelReader 50000 5.317 ms 0.0146 ms 0.0023 ms 1.00 - - 368 B 1.00
ExcelReaderWide 50000 13.573 ms 0.0436 ms 0.0067 ms 2.55 - - 304 B 0.83
ExcelReaderAsync 50000 5.041 ms 0.0090 ms 0.0014 ms 0.95 - - 440 B 1.20
Sep 50000 11.947 ms 0.0461 ms 0.0071 ms 2.25 - - 4024 B 10.93
Sylvan 50000 6.159 ms 0.0250 ms 0.0039 ms 1.16 93.7500 7.8125 1688701 B 4,588.86
CsvHelperLib 50000 33.932 ms 0.0488 ms 0.0076 ms 6.38 866.6667 66.6667 15073424 B 40,960.39

ExcelReader.Benchmarks.CsvWriteBenchmark


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.400
  [Host]     : .NET 10.0.11 (10.0.11, 10.0.1126.37416), X64 RyuJIT x86-64-v3
  Job-MEHJPP : .NET 10.0.11 (10.0.11, 10.0.1126.37416), X64 RyuJIT x86-64-v3

IterationCount=5  WarmupCount=1  

Method Rows Mean Error StdDev Ratio RatioSD Gen0 Gen1 Gen2 Allocated Alloc Ratio
ExcelReaderWriter 50000 9.908 ms 0.4563 ms 0.1185 ms 1.00 0.02 500.0000 500.0000 500.0000 4 MB 1.00
Sep 50000 10.419 ms 0.1250 ms 0.0325 ms 1.05 0.01 500.0000 500.0000 500.0000 4.01 MB 1.00
SylvanWriter 50000 10.633 ms 0.3209 ms 0.0497 ms 1.07 0.01 500.0000 500.0000 500.0000 4.04 MB 1.01
CsvHelperLib 50000 21.588 ms 0.6459 ms 0.1677 ms 2.18 0.03 1200.0000 600.0000 600.0000 13.79 MB 3.44

ExcelReader.Benchmarks.ParseBenchmark


BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
INTEL XEON PLATINUM 8573C 2.30GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.400
  [Host]     : .NET 10.0.11 (10.0.11, 10.0.1126.37416), X64 RyuJIT x86-64-v4
  Job-MEHJPP : .NET 10.0.11 (10.0.11, 10.0.1126.37416), X64 RyuJIT x86-64-v4

IterationCount=5  WarmupCount=1  

Method Rows Mean Error StdDev Ratio RatioSD Gen0 Allocated Alloc Ratio
ExcelParserSync 50000 19.502 ms 0.0695 ms 0.0181 ms 1.00 0.00 31.2500 3966.76 KB 1.000
ExcelParserSyncSharedStrings 50000 14.725 ms 0.0757 ms 0.0117 ms 0.76 0.00 15.6250 2357.25 KB 0.594
ExcelParserStructSync 50000 18.605 ms 0.1497 ms 0.0232 ms 0.95 0.00 - 1623.03 KB 0.409
RefParserParseNamedSync 50000 16.800 ms 0.1135 ms 0.0295 ms 0.86 0.00 - 11.63 KB 0.003
ExcelParserAsync 50000 19.429 ms 0.0697 ms 0.0181 ms 1.00 0.00 31.2500 3968.91 KB 1.001
ExcelParserXlsbSync 50000 9.645 ms 0.0227 ms 0.0059 ms 0.49 0.00 46.8750 3968.86 KB 1.001
ExcelParserXlsbAsync 50000 9.620 ms 0.0510 ms 0.0132 ms 0.49 0.00 46.8750 3971.43 KB 1.001
MiniExcel 50000 209.136 ms 3.5853 ms 0.5548 ms 10.72 0.03 3000.0000 262290.58 KB 66.122
Sylvan 50000 84.951 ms 58.1112 ms 15.0913 ms 4.36 0.71 - 10726.71 KB 2.704
SylvanAsync 50000 80.193 ms 20.9530 ms 5.4414 ms 4.11 0.25 - 10727.74 KB 2.704

ExcelReader.Benchmarks.ReadBenchmark


BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 9V74 2.60GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.400
  [Host]     : .NET 10.0.11 (10.0.11, 10.0.1126.37416), X64 RyuJIT x86-64-v3
  Job-MEHJPP : .NET 10.0.11 (10.0.11, 10.0.1126.37416), X64 RyuJIT x86-64-v3

IterationCount=5  WarmupCount=1  

Method Rows Mean Error StdDev Ratio RatioSD Gen0 Allocated Alloc Ratio
ExcelReader 50000 16.061 ms 0.0275 ms 0.0071 ms 1.00 0.00 - 11.13 KB 1.00
ExcelReaderAsync 50000 16.755 ms 0.0554 ms 0.0144 ms 1.04 0.00 - 13.27 KB 1.19
ExcelReaderXlsb 50000 7.076 ms 0.3034 ms 0.0788 ms 0.44 0.00 - 13.23 KB 1.19
ExcelReaderXlsbAsync 50000 7.181 ms 0.1412 ms 0.0367 ms 0.45 0.00 - 15.79 KB 1.42
ExcelReaderMaterialized 50000 18.474 ms 0.0499 ms 0.0130 ms 1.15 0.00 93.7500 1622.46 KB 145.74
MiniExcel 50000 224.065 ms 10.8595 ms 1.6805 ms 13.95 0.09 16000.0000 273792.97 KB 24,593.33
Sylvan 50000 52.800 ms 0.2813 ms 0.0435 ms 3.29 0.00 100.0000 1939.18 KB 174.19

ExcelReader.Benchmarks.RealDataReadBenchmark


BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
INTEL XEON PLATINUM 8573C 2.30GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.400
  [Host]     : .NET 10.0.11 (10.0.11, 10.0.1126.37416), X64 RyuJIT x86-64-v4
  Job-MEHJPP : .NET 10.0.11 (10.0.11, 10.0.1126.37416), X64 RyuJIT x86-64-v4

IterationCount=5  WarmupCount=1  

Method Mean Error StdDev Ratio RatioSD Gen0 Gen1 Allocated Alloc Ratio
Xlsx_ExcelReader 82.339 ms 3.1078 ms 0.8071 ms 1.00 0.01 - - 18232 B 1.00
Xlsx_Sylvan 248.905 ms 6.4747 ms 1.0020 ms 3.02 0.03 - - 659448 B 36.17
Xlsx_ExcelReader_Materialized 84.470 ms 7.5515 ms 1.9611 ms 1.03 0.02 - - 27928 B 1.53
Xlsx_ExcelReader_Prefetch 54.581 ms 3.8156 ms 0.5905 ms 0.66 0.01 - - 38370 B 2.10
Xlsx_ExcelReader_Memory 82.917 ms 4.5381 ms 1.1785 ms 1.01 0.02 - - 7120 B 0.39
Xlsx_ExcelReader_Memory_Prefetch 55.012 ms 4.1993 ms 0.6499 ms 0.67 0.01 - - 27190 B 1.49
Xlsm_ExcelReader 83.086 ms 4.3297 ms 1.1244 ms 1.01 0.02 - - 18232 B 1.00
Xlsm_Sylvan 248.827 ms 18.0923 ms 4.6985 ms 3.02 0.06 - - 659648 B 36.18
Xlsm_ExcelReader_Materialized 86.143 ms 8.8144 ms 2.2891 ms 1.05 0.03 - - 27928 B 1.53
Xlsm_ExcelReader_Prefetch 54.267 ms 6.5772 ms 1.7081 ms 0.66 0.02 - - 38403 B 2.11
Xlsm_ExcelReader_Memory 83.510 ms 4.2266 ms 1.0976 ms 1.01 0.02 - - 7120 B 0.39
Xlsm_ExcelReader_Memory_Prefetch 55.063 ms 15.3999 ms 3.9993 ms 0.67 0.04 - - 27316 B 1.50
Xlsb_ExcelReader 31.942 ms 2.3672 ms 0.6148 ms 0.39 0.01 - - 19208 B 1.05
Xlsb_Sylvan 38.354 ms 3.4077 ms 0.5273 ms 0.47 0.01 - - 346672 B 19.01
Xlsb_ExcelReader_Materialized 32.841 ms 0.8766 ms 0.2276 ms 0.40 0.00 - - 28904 B 1.59
Xlsb_ExcelReader_Prefetch 24.232 ms 0.9784 ms 0.2541 ms 0.29 0.00 - - 59098 B 3.24
Xlsb_ExcelReader_Memory 31.588 ms 1.7342 ms 0.4504 ms 0.38 0.01 - - 8848 B 0.49
Xlsb_ExcelReader_Memory_Prefetch 24.256 ms 0.6756 ms 0.1755 ms 0.29 0.00 - - 57192 B 3.14
Xls_ExcelReader 11.596 ms 0.5706 ms 0.0883 ms 0.14 0.00 - - 12176 B 0.67
Xls_Sylvan 20.848 ms 1.7541 ms 0.4555 ms 0.25 0.01 - - 190366 B 10.44
Xls_ExcelReader_Materialized 12.328 ms 0.4383 ms 0.1138 ms 0.15 0.00 - - 21872 B 1.20
Xls_ExcelReader_Memory 10.963 ms 0.9072 ms 0.2356 ms 0.13 0.00 - - 12176 B 0.67
Csv_ExcelReader 5.935 ms 0.2781 ms 0.0722 ms 0.07 0.00 - - 304 B 0.02
Csv_Sylvan 16.295 ms 1.0599 ms 0.2753 ms 0.20 0.00 437.5000 31.2500 37483923 B 2,055.94
Csv_ExcelReader_Materialized 26.391 ms 1.9487 ms 0.5061 ms 0.32 0.01 437.5000 - 37442912 B 2,053.69
Csv_ExcelReader_Memory 5.751 ms 0.0558 ms 0.0086 ms 0.07 0.00 - - 240 B 0.01

ExcelReader.Benchmarks.RecordWriteBenchmark


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.400
  [Host]     : .NET 10.0.11 (10.0.11, 10.0.1126.37416), X64 RyuJIT x86-64-v3
  Job-MEHJPP : .NET 10.0.11 (10.0.11, 10.0.1126.37416), X64 RyuJIT x86-64-v3

IterationCount=5  WarmupCount=1  

Method Rows Mean Error StdDev Ratio Gen0 Gen1 Gen2 Allocated Alloc Ratio
Xlsx 50000 27.613 ms 0.6820 ms 0.1771 ms 1.00 500.0000 500.0000 500.0000 4.02 MB 1.00
Xlsb 50000 10.748 ms 0.0737 ms 0.0191 ms 0.39 500.0000 500.0000 500.0000 4.02 MB 1.00
Xls 50000 8.209 ms 0.2522 ms 0.0390 ms 0.30 250.0000 250.0000 250.0000 4.03 MB 1.00
Csv 50000 10.117 ms 0.1381 ms 0.0214 ms 0.37 500.0000 500.0000 500.0000 4 MB 1.00
XlsxMapped 50000 27.249 ms 0.1785 ms 0.0464 ms 0.99 500.0000 500.0000 500.0000 4.02 MB 1.00
XlsbMapped 50000 11.800 ms 0.4565 ms 0.1186 ms 0.43 500.0000 500.0000 500.0000 4.02 MB 1.00
CsvMapped 50000 11.987 ms 0.2393 ms 0.0370 ms 0.43 500.0000 500.0000 500.0000 4 MB 1.00

ExcelReader.Benchmarks.WriteBenchmark


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.400
  [Host]     : .NET 10.0.11 (10.0.11, 10.0.1126.37416), X64 RyuJIT x86-64-v3
  Job-MEHJPP : .NET 10.0.11 (10.0.11, 10.0.1126.37416), X64 RyuJIT x86-64-v3

IterationCount=5  WarmupCount=1  

Method Rows Mean Error StdDev Ratio RatioSD Gen0 Gen1 Gen2 Allocated Alloc Ratio
ExcelReaderWriter 50000 23.680 ms 0.8735 ms 0.2268 ms 1.00 0.01 500.0000 500.0000 500.0000 4.02 MB 1.00
ExcelReaderWriterSharedStrings 50000 25.277 ms 0.5303 ms 0.1377 ms 1.07 0.01 500.0000 500.0000 500.0000 4.06 MB 1.01
ExcelReaderWriterPrefetch 50000 19.822 ms 4.9244 ms 1.2789 ms 0.84 0.05 468.7500 468.7500 468.7500 4.03 MB 1.00
ExcelReaderXlsbWriter 50000 10.276 ms 0.3776 ms 0.0981 ms 0.43 0.01 500.0000 500.0000 500.0000 4.02 MB 1.00
ExcelReaderXlsbWriterSharedStrings 50000 9.940 ms 0.2132 ms 0.0330 ms 0.42 0.00 500.0000 500.0000 500.0000 4.06 MB 1.01
ExcelReaderXlsbWriterPrefetch 50000 9.243 ms 1.1081 ms 0.1715 ms 0.39 0.01 484.3750 484.3750 484.3750 4.03 MB 1.00
MiniExcel 50000 103.277 ms 5.7109 ms 1.4831 ms 4.36 0.07 5000.0000 1000.0000 1000.0000 84.89 MB 21.11
SpreadCheetah 50000 21.673 ms 0.8546 ms 0.2219 ms 0.92 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 9V74 2.60GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.400
  [Host]     : .NET 10.0.11 (10.0.11, 10.0.1126.37416), X64 RyuJIT x86-64-v3
  Job-MEHJPP : .NET 10.0.11 (10.0.11, 10.0.1126.37416), X64 RyuJIT x86-64-v3

IterationCount=5  WarmupCount=1  

Method Rows Mean Error StdDev Ratio Gen0 Gen1 Allocated Alloc Ratio
ExcelReader 50000 4.323 ms 0.0157 ms 0.0041 ms 1.00 - - 3.04 KB 1.00
ExcelReaderAsync 50000 4.430 ms 0.0364 ms 0.0095 ms 1.02 - - 3.11 KB 1.02
Sylvan 50000 8.087 ms 0.1257 ms 0.0326 ms 1.87 93.7500 15.6250 1717.73 KB 565.22

ExcelReader.Benchmarks.XlsWriteBenchmark


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.400
  [Host]     : .NET 10.0.11 (10.0.11, 10.0.1126.37416), X64 RyuJIT x86-64-v3
  Job-MEHJPP : .NET 10.0.11 (10.0.11, 10.0.1126.37416), X64 RyuJIT x86-64-v3

IterationCount=5  WarmupCount=1  

Method Rows Mean Error StdDev Ratio Gen0 Gen1 Gen2 Allocated Alloc Ratio
XlsWriter 50000 7.266 ms 0.0274 ms 0.0042 ms 1.00 492.1875 492.1875 492.1875 16.03 MB 1.00
XlsxWriter 50000 23.801 ms 0.1591 ms 0.0413 ms 3.28 500.0000 500.0000 500.0000 4.02 MB 0.25

CI on PR #104 (develop -> master) failed nearly every job with RS0016/RS0017:
"HeaderNormalization.RemoveDiacritics" not declared, and 4 stale
ExcelRecordMapBuilder<T> entries declared in Shipped.txt but no longer in code.

Root cause: promote_public_api.py's union (`shipped_entries.update(new_entries)`)
never special-cased a `*REMOVED*Foo` entry. Such an entry only ever means "delete
Foo from Shipped" - the script instead added the literal `*REMOVED*Foo` string as
a new entry while leaving the real `Foo` line untouched, so a plain entry that
should have been deleted from Shipped.txt survived every future promotion. This
is what let the ExcelRecordMapBuilder<T> refactor's *REMOVED* markers (added when
the type became generic in TRow) leak through the v3.0.0 promotion (PR #103)
without deleting the old non-generic entries. RS0024 also forbids a *REMOVED*
line inside Shipped.txt at all ("shipped API file can't have removed members") -
the correct behavior is to delete the matching plain entry and discard the
*REMOVED* line itself, never copy it.

Also fixes a second, unrelated mistake on develop: HeaderNormalization is
declared in ExcelReader.Core, but its RemoveDiacritics entry had been added to
ExcelReader.Arrow's PublicAPI.Unshipped.txt (a different project's own API
surface) instead of ExcelReader.Core's Shipped.txt - so it fixed nothing.

Changes:
- promote_public_api.py: extract promote_one(unshipped, shipped) and make it
  delete a REMOVED entry's plain counterpart from Shipped instead of unioning
  the literal *REMOVED* string into it.
- .github/scripts/test_promote_public_api.py: new unit tests, including a
  replay of the exact ExcelRecordMapBuilder<T> scenario that broke this.
- ci.yml: run those tests on every PR (this script previously had zero
  coverage and only ran unattended during a real release).
- PublicAPI/*/Shipped.txt (Core): drop the 4 stale ExcelRecordMapBuilder<T>
  entries; add the missing HeaderNormalization.RemoveDiacritics entry.
- PublicAPI/*/Unshipped.txt (Arrow): remove the misplaced RemoveDiacritics line.

Verified: dotnet build ExcelReader.slnx -c Release is clean (0/0), full test
suite passes (1467/1467, Release/net10.0), and a promote_one() replay against
the real pre-fix Unshipped/Shipped content produces the correct result.
@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

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 85.76%. Comparing base (ecd0ae9) to head (b921270).
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@           Coverage Diff           @@
##           master     #104   +/-   ##
=======================================
  Coverage   85.76%   85.76%           
=======================================
  Files         164      164           
  Lines       11827    11827           
  Branches     2159     2159           
=======================================
  Hits        10143    10143           
  Misses       1299     1299           
  Partials      385      385           

☔ 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.

@GabrielMarquezMatte
GabrielMarquezMatte merged commit 7c63ee4 into master Sep 4, 2026
48 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