Skip to content

Workbooks and sheets replace the current-sheet reader, across the library, C ABI and bindings (v6.0.0) - #136

Merged
GabrielMarquezMatte merged 49 commits into
masterfrom
develop
Oct 7, 2026
Merged

GabrielMarquezMatte merged 49 commits into
masterfrom
develop

Conversation

@GabrielMarquezMatte

Copy link
Copy Markdown
Owner

Summary

A v5 reader owned one "current sheet" that callers moved with MoveToSheet. In v6 the reader is a
workbook that hands out sheets, and each sheet is read on its own, so the sheets of one workbook can
be read in parallel. The change runs through the C# library, the C ABI (now version 6) and the
Python, Rust and C++ bindings. This is a breaking release; docs/guide/migrating-to-v6.md maps
every v5 name to its v6 replacement.

  • Workbooks and sheets (C#). XlsxReader, XlsbReader and XlsReader become XlsxWorkbook,
    XlsbWorkbook and XlsWorkbook. IExcelRowReader splits into IExcelWorkbook and IExcelSheet.
    A sheet (XlsxSheet, XlsbSheet, XlsSheet, CsvSheet) is a readonly struct made of a workbook
    and an index, and it holds no resources. MoveToSheet, SheetName and Sheets() give way to
    workbook.Sheets[i], SheetAt, TryGetSheet, sheet.Name and sheet.Visibility.
    Excel.Open returns an IExcelWorkbook. ExcelParser<T>.Parse, Excel.InferSchema,
    ExcelDataReader and ToArrowRecordBatch take a sheet. The CLI, benchmarks, fuzz harness and
    tests read through sheets.
  • C ABI v6. XL_ABI_VERSION is 6, with 50 exports. A workbook handle has no current sheet, and
    every read takes a zero-based sheet index right after the handle. Rows come from a cursor handle:
    xl_rows_open, xl_rows_next, xl_rows_next_view, xl_rows_read_all_blob,
    xl_rows_read_all_decoded and xl_rows_close. Each _ex export is folded into its base export:
    xl_open_file/xl_open_memory take an options pointer, xl_parse_typed/xl_parse_arrow take a
    degree of parallelism, and xl_infer_schema takes flags. New: xl_sheet_visibility_at and
    xl_sheet_index. Typed readers, Arrow streams and cursors are independent of each other and keep
    working after xl_close. Handle ids are never reused, and each is checked against its type.
    Reading functions clear xl_last_error on entry, so after a non-OK status the message belongs to
    that call.
  • Python. Reads move to workbook.sheets[i] or workbook.sheets["Name"], which raises
    KeyError on a miss. Sheet has value equality. Workbook.close() is safe to call from several
    threads at once. Each rows(), read_all() and read_all_columnar() call starts at the sheet's
    first row.
  • Rust. workbook.sheet(i)? returns a Copy Sheet<'w>. RowCursor owns a native cursor.
    Workbook, RowCursor, TypedChunks, DecodedRows and TableView are Send + Sync, and
    Workbook no longer needs mut. New: Sheet::infer_schema_parse_text, parse_sheet_parallel
    and arrow::parse_arrow_parallel.
  • C++. Reads move to xl::Sheet. sheet.rows() returns a move-only xl::RowCursor, and
    RowCursor::read_all_decoded() reads what the cursor has left. The comparison benchmarks build
    against the v6 wrapper and configure under CMake 4.
  • NativeAOT first-call race. Concurrent first calls into the runtime could crash it. All three
    bindings now run a single-threaded warm-up inside their one-time ABI check.
  • Static PGO. excelreader.mibc is regenerated for the v6 entry points. In an alternating
    old/new A/B on a 65K-row XLSX from Python, to_polars is 3.5% faster and the row and read_all
    paths are 0.9 to 1.2% faster. CSV and XLSB are flat; XLSB is left out of the profile on purpose.

Behavior changes

  • xl_parse_typed and xl_parse_arrow used to read sequentially. Pass 1 as the degree of
    parallelism to keep that; 0 uses every processor on a CSV.
  • A second typed reader or Arrow stream on one workbook used to fail. Now any number can be open
    at once, and none interferes with another read.
  • read_all_blob / read_all_decoded on a sheet read the whole sheet; they no longer continue a
    cursor. Use the cursor's own method for its remainder.
  • A binding built for ABI 5 refuses a v6 library at load or open, with an ABI-version error.

Test plan

  • dotnet test (Core, Native, Arrow, CLI)
  • Native C smoke test (tests/ExcelReader.NativeSmoke)
  • pytest python/tests
  • cargo test in rust/excelreader
  • C++ tests and the package consumer (cpp/tests)
  • CI green on all three binding workflows

GabrielMarquezMatte and others added 30 commits October 6, 2026 15:20
XlsxReader, XlsbReader and XlsReader become XlsxWorkbook, XlsbWorkbook
and XlsWorkbook. The internal XLSB parsing helper that held the name
becomes XlsbWorkbookParts. No member or behaviour changes. The old names
are recorded as removed in PublicAPI.Unshipped.txt.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Add IExcelWorkbook, IExcelSheet, ExcelSheetList<TSheet> and the XLSX, XLSB,
XLS and CSV sheet structs, with Sheets, FirstSheet and TryGetSheet on each
workbook. The v5 reader interfaces inherit the new ones so callers can move
over file by file; nothing is removed yet.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Adds sheet overloads next to the reader ones: ExcelParser<T>.Parse, Excel.InferSchema,
ExcelDataReader and ToArrowRecordBatch. ExcelEnumerable and AsyncRowEnumerator
now constrain to IExcelSheet<TEnumerator>; CsvEnumerable holds a CsvSheet.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…a reader

CliCommands.Open returns the workbook plus the selected sheet; the write and
schema paths take IExcelSheet. The parallel-CSV internals open the CSV sheet
directly through OpenSheet.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
NativeHandle now holds an IExcelWorkbook and its own current sheet index;
the exported C functions keep their status codes and messages.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… AOT check

Benchmarks, the fuzz harness, FuzzTests and the AOT sanity app obtain a
sheet from the workbook (FirstSheet / SheetAt) instead of enumerating a
reader with a current sheet. The differential harness is typed over
IExcelWorkbook. Sheet lookup stays inside every measured method.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Move the tests directly under tests/ExcelReader.Tests/Reader off the v5
reader-with-a-current-sheet members: enumerators, parsers, schema inference
and ExcelDataReader take a sheet, navigation tests assert the v6 workbook
and sheet equivalents.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Csv, Xls, Xlsb, Xlsx and Internal reader tests read through Sheets,
FirstSheet, SheetAt and TryGetSheet instead of the reader's current
sheet. The dispose tests assert on the workbook's Sheets and on a sheet
taken before the dispose. The CSV dialect helper drops its v5 overloads.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…oks and sheets

The v5 "reader with a current sheet" surface is gone; only the workbook/sheet API remains.

- IExcelRowReader, IExcelRowReader<TEnumerator> -> IExcelWorkbook (the workbook) or IExcelSheet / IExcelSheet<TEnumerator> (one sheet)
- ExcelRowReaderExtensions.Sheets(), ExcelSheet -> for n in [0, SheetCount): workbook.SheetAt(n), or foreach over workbook.Sheets
- MoveToSheet(int) -> Sheets[n] / SheetAt(n)
- TryMoveToSheet(name) -> TryGetSheet(name, out sheet)
- SheetName, SheetNameAt(n) -> sheet.Name
- SheetVisibility, SheetVisibilityAt(n) -> sheet.Visibility
- workbook GetEnumerator() / GetAsyncEnumerator(ct) -> sheet.GetEnumerator() / sheet.GetAsyncEnumerator(ct)
- Excel.Open / OpenAsync returning IExcelRowReader -> returning IExcelWorkbook / ValueTask<IExcelWorkbook>
- Excel.InferSchema(IExcelRowReader, ...) -> Excel.InferSchema(IExcelSheet, ...)
- ExcelParser<T>.Parse(XlsxWorkbook | XlsWorkbook | XlsbWorkbook | IExcelRowReader | CsvReader) -> Parse(XlsxSheet | XlsSheet | XlsbSheet | IExcelSheet | CsvSheet)
- ExcelDataReader(IExcelRowReader, int) -> ExcelDataReader(IExcelSheet, int)
- ToArrowRecordBatch(this IExcelRowReader, ...) -> ToArrowRecordBatch(this IExcelSheet, ...)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…verloads

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The concurrency theories now run over memory, path, FileStream, a
seekable stream and a non-seekable stream. A new theory obtains sheets
by index and by name and opens their enumerators from twelve threads at
once, racing the first shared-string load. A fact checks that a disposed
workbook refuses a new enumerator while a live one reads to the end.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e notes

The quick start, the feature notes and the reader table now use the v6
workbook and sheet types, ARCHITECTURE.md gains a paragraph on the
workbook, sheet and enumerator split and the reference-counted lifetime,
and the style guide example names a member that still exists.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Re-wrap the reflowed open paragraph, align the reader table and list
XlsbWorkbookParts.cs in the partial-class layout.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The dispose-while-live check now drains the live enumerator and
compares every cell, shared strings included, with a sequential read,
and runs over memory, FileStream and a seekable stream, where an early
release of the zip or the shared table would break the read.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…workbook

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
rust/.cargo/config.toml points EXCELREADER_NATIVE_LIB_DIR at a local
build that the publish job never makes, so the package verification
failed with "no .so file found" and excelreader 5.2.0 never reached
crates.io. Remove the override in that job so the verification downloads
the native library from the release, as users do. Let the derive step
skip a version that is already published, so the job can be re-run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rewrite the reading, parsing, CSV, encryption and writing guides for the
workbook and sheet API, and add a v5 to v6 migration guide.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each read opens its own enumerator, so reads on one workbook handle no
longer invalidate one another and the live-session interlock is removed.
xl_parse_typed_ex, xl_parse_arrow_ex and xl_infer_schema_ex fold into the
unsuffixed functions. XL_ABI_VERSION is 6.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
xl_rows_open returns a cursor with its own enumerator, so several cursors
read one workbook at once and survive xl_close. The workbook handle no
longer has a current sheet: xl_move_to_sheet and xl_sheet_name are removed,
and xl_sheet_visibility_at and xl_sheet_index are added. The struct filled
by the decoded read-all is renamed xl_rows_decoded.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…en functions

xl_open_file and xl_open_memory take the options pointer; NULL means the
defaults.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Cursors and typed parses over all sheets of one handle match a sequential
read in XLSX, XLSB and XLS, and a cursor reads to the end after its
workbook id is closed.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
…t to v6

A reading call now clears the thread's error first, so a non-OK status never
shows an earlier call's message; xl_sheet_name_at reports a message for a
bad index like every other function. The C smoke test binds the v6 exports.
The parallel tests assert per-sheet content, not only row counts.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
xl::Workbook hands out xl::Sheet values; rows(), read_all_decoded() and
infer_schema() move to the sheet, and parse_sheet, typed_reader,
parse_arrow and arrow_stream take one. xl::RowCursor owns its native
cursor. move_to_sheet and the index-less sheet_name are removed.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Workbook hands out Sheet values; rows(), read_all_*, typed_chunks,
infer_schema, parse_sheet and the Arrow functions take a sheet and open
their own native cursor. move_to_sheet and the index-less sheet_name are
removed.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Workbook.sheets is a sequence of Sheet objects, looked up by index, slice
or name. The read methods move from Workbook to Sheet and each opens its
own native cursor. move_to_sheet, sheet_name and the sheets() method are
removed.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
GabrielMarquezMatte and others added 18 commits October 7, 2026 00:50
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The v5 versions asserted that a fresh reader could start, which proved
the workbook's single cursor slot was free. That slot is gone, so a
leaked reader or stream passed. They now spy on xl_typed_reader_close
and on the Arrow stream release, and the parse_typed test keeps a row
cursor alive while it runs.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
DecodedRows and TableView own plain native allocations, so they are Send
and Sync and can be returned from scoped threads. Workbook::sheet takes
its error codes from the native side. The migration guide says read-alls
now start from the first row.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…drop jthread

RowCursor::read_all_decoded returns what the cursor has left; the sheet
form reads the whole sheet, and the migration guide says so. The first
ABI check runs one small read so later threads do not race the runtime's
start-up. The sheets test uses std::thread for libc++.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…arm the runtime once

Sheets.__getitem__ is overloaded so sheets[i] types as a Sheet, and two
Sheet objects for the same workbook and index compare equal. The first
library load runs one small read on one thread so later threads do not
race the runtime's start-up. ArrowStream no longer holds the workbook.
The migration guide says reads now start from the first row.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Workbook.close() takes the handle under a lock, so two threads closing
the same workbook call xl_close once and neither raises.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Sheet::infer_schema_parse_text passes XL_INFER_PARSE_TEXT, and
parse_sheet_parallel / parse_arrow_parallel pass a degree of
parallelism, matching what the Python and C++ bindings expose.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Park each closing thread at its first read of the handle so both pass
the is-None check; fails every time on the unlocked close, passes with
the lock. Drops the stub test, which also passed on the old code.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The sources already compile against v6; the compare option failed to
configure because xlsxio's C sources need the C language enabled.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The benchmark hardcoded format="xlsb", so a path of any other format failed.
open_workbook now infers the format from the path; the CSV legs keep their
explicit format.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The profile predated the v6 row cursor and sheet-index signatures, so ILC
found no data for ReadApi.NextRow/NextRowView or the typed entry points.
Python, XLSX 65K, alternated old/new/old/new: read_all -1.1%, rows -0.9%,
read_all_columnar -1.2%, to_polars -3.5%; CSV parse_typed and to_polars flat.
Rust and C++ on XLSB (unprofiled by design): within noise.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
xlnt 1.5.0 and zsv declare a CMake minimum that CMake 4 no longer
accepts; raise the policy minimum for those fetched projects.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@codecov-commenter

codecov-commenter commented Oct 7, 2026 •

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 93.77289% with 34 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.03%. Comparing base (1723f07) to head (2d351eb).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
src/ExcelReader.Core/Reader/Xlsb/XlsbWorkbook.cs 92.85% 8 Missing and 2 partials ⚠️
src/ExcelReader.Core/Reader/Excel.cs 84.44% 7 Missing ⚠️
src/ExcelReader.Native/Reading/ReadApi.Rows.All.cs 76.00% 5 Missing and 1 partial ⚠️
.../ExcelReader.Core/Reader/Xlsb/XlsbWorkbookParts.cs 85.71% 2 Missing and 2 partials ⚠️
src/ExcelReader.Native/Reading/ReadApi.Rows.cs 91.11% 4 Missing ⚠️
src/ExcelReader.Core/Reader/ExcelSheetList.cs 88.88% 1 Missing ⚠️
src/ExcelReader.Core/Reader/Xlsx/XlsxWorkbook.cs 96.55% 1 Missing ⚠️
src/ExcelReader.Native/Reading/ReadApi.Open.cs 83.33% 1 Missing ⚠️
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #136      +/-   ##
==========================================
+ Coverage   89.88%   90.03%   +0.15%     
==========================================
  Files         185      190       +5     
  Lines       13911    13963      +52     
  Branches     2550     2558       +8     
==========================================
+ Hits        12504    12572      +68     
+ Misses        941      933       -8     
+ Partials      466      458       -8     

☔ 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

github-actions Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Benchmark Results

Baseline: master 1161344d, from the series published by benchmark-pages.yml. Both sides ran on a shared ubuntu-latest runner, so treat anything under ±10% as noise.

Moved by at least 10%

Benchmark master This PR Delta
ExcelReader.Benchmarks.RealDataReadBenchmark.Xlsm_ExcelReader_Memory_Prefetch 33.85 ms 46.95 ms +38.7%
ExcelReader.Benchmarks.RealDataReadBenchmark.Xlsm_Sylvan 231.32 ms 317.15 ms +37.1%
ExcelReader.Benchmarks.RealDataReadBenchmark.Xlsx_ExcelReader_Prefetch 34.66 ms 47.46 ms +37.0%
ExcelReader.Benchmarks.RealDataReadBenchmark.Xlsx_Sylvan 232.60 ms 315.43 ms +35.6%
ExcelReader.Benchmarks.RealDataReadBenchmark.Xlsm_ExcelReader_Prefetch 35.82 ms 46.79 ms +30.6%
ExcelReader.Benchmarks.RealDataReadBenchmark.Csv_ExcelReader_Memory 6.43 ms 8.26 ms +28.4%
ExcelReader.Benchmarks.RealDataReadBenchmark.Xls_ExcelReader_Memory 13.03 ms 16.34 ms +25.5%
ExcelReader.Benchmarks.RealDataReadBenchmark.Xlsx_ExcelReader_Memory_Prefetch 37.75 ms 46.78 ms +23.9%
ExcelReader.Benchmarks.RealDataReadBenchmark.Xls_ExcelReader 13.28 ms 16.37 ms +23.2%
ExcelReader.Benchmarks.RealDataReadBenchmark.Xlsx_ExcelReader 59.59 ms 73.26 ms +23.0%
ExcelReader.Benchmarks.RealDataReadBenchmark.Xlsx_ExcelReader_Materialized 62.31 ms 76.53 ms +22.8%
ExcelReader.Benchmarks.RealDataReadBenchmark.Xlsb_ExcelReader 29.34 ms 35.97 ms +22.6%
ExcelReader.Benchmarks.RealDataReadBenchmark.Xlsm_ExcelReader 60.38 ms 74.02 ms +22.6%
ExcelReader.Benchmarks.RealDataReadBenchmark.Xlsx_ExcelReader_Memory 59.78 ms 73.14 ms +22.3%
ExcelReader.Benchmarks.RealDataReadBenchmark.Xlsm_ExcelReader_Memory 60.02 ms 73.25 ms +22.0%
ExcelReader.Benchmarks.RealDataReadBenchmark.Csv_ExcelReader 6.91 ms 8.42 ms +21.7%
ExcelReader.Benchmarks.RealDataReadBenchmark.Xlsm_ExcelReader_Materialized 62.37 ms 75.15 ms +20.5%
ExcelReader.Benchmarks.RealDataReadBenchmark.Xlsb_Sylvan 36.52 ms 43.94 ms +20.3%
ExcelReader.Benchmarks.RealDataReadBenchmark.Xls_Sylvan 22.32 ms 26.85 ms +20.3%
ExcelReader.Benchmarks.RealDataReadBenchmark.Csv_ExcelReader_Materialized 25.72 ms 30.83 ms +19.9%
ExcelReader.Benchmarks.RealDataTypedParseBenchmark.Xlsx_ExcelParser 73.34 ms 87.56 ms +19.4%
ExcelReader.Benchmarks.RealDataReadBenchmark.Xls_ExcelReader_Materialized 15.75 ms 18.76 ms +19.1%
ExcelReader.Benchmarks.ParseBenchmark.OfficeIMO(Rows: 50000) 162.12 ms 191.59 ms +18.2%
ExcelReader.Benchmarks.RealDataReadBenchmark.Xlsb_ExcelReader_Memory 30.51 ms 35.74 ms +17.1%
ExcelReader.Benchmarks.RealDataTypedParseBenchmark.Xlsb_ExcelParser 40.52 ms 46.51 ms +14.8%
ExcelReader.Benchmarks.RealDataReadBenchmark.Xlsb_ExcelReader_Prefetch 19.01 ms 21.36 ms +12.3%
ExcelReader.Benchmarks.NativeRowReadBenchmark.ReadAllDecoded 80.87 ms 90.42 ms +11.8%
ExcelReader.Benchmarks.RealDataReadBenchmark.Xlsb_ExcelReader_Materialized 33.04 ms 36.74 ms +11.2%
ExcelReader.Benchmarks.StringHeavyReadBenchmark.Xlsb_ExcelReader_Materialized_Interned(Rows: 65536) 79.38 ms 70.75 ms -10.9%
ExcelReader.Benchmarks.StringHeavyReadBenchmark.Xlsb_ExcelReader_Materialized(Rows: 65536) 79.50 ms 70.76 ms -11.0%
ExcelReader.Benchmarks.StringHeavyReadBenchmark.Xlsx_ExcelReader(Rows: 65536) 62.38 ms 55.48 ms -11.1%
ExcelReader.Benchmarks.EncryptedWorkbookBenchmark.Plain_Stream 53.36 µs 46.96 µs -12.0%
ExcelReader.Benchmarks.EncryptedWorkbookBenchmark.Encrypted_Stream_VerifyIntegrity 58.69 ms 51.04 ms -13.0%
ExcelReader.Benchmarks.StringHeavyReadBenchmark.Xlsx_ExcelReader_Materialized(Rows: 65536) 93.07 ms 79.96 ms -14.1%
ExcelReader.Benchmarks.StringHeavyReadBenchmark.Xlsx_ExcelReader_Materialized_Interned(Rows: 65536) 93.64 ms 79.66 ms -14.9%
ExcelReader.Benchmarks.ArrowConversionBenchmark.XlsbTyped(Rows: 100000) 20.51 ms 17.41 ms -15.1%
ExcelReader.Benchmarks.ArrowConversionBenchmark.CsvTyped(Rows: 100000) 15.21 ms 12.65 ms -16.8%
ExcelReader.Benchmarks.XlsxSharedStringHotPathBenchmark.EndToEnd_Prefetch(Rows: 65536) 51.84 ms 42.70 ms -17.6%
ExcelReader.Benchmarks.EncryptedWorkbookBenchmark.Large_Encrypted_Stream 121.27 ms 98.59 ms -18.7%
ExcelReader.Benchmarks.XlsxSharedStringHotPathBenchmark.IndexParse_Utf8Parser(Rows: 65536) 3.21 ms 2.54 ms -20.8%
ExcelReader.Benchmarks.EncryptedWorkbookBenchmark.Large_Plain_Stream 61.09 ms 47.80 ms -21.8%
ExcelReader.Benchmarks.DataReaderBenchmark.DataReader_TypedGetters(Rows: 50000) 14.03 ms 10.97 ms -21.8%
ExcelReader.Benchmarks.DataReaderBenchmark.DataReader_GetValue(Rows: 50000) 14.26 ms 11.03 ms -22.7%
ExcelReader.Benchmarks.EncryptedWorkbookBenchmark.Encrypted_OpenOnly 60.42 ms 46.07 ms -23.8%
ExcelReader.Benchmarks.EncryptedWorkbookBenchmark.Encrypted_Memory 59.30 ms 45.17 ms -23.8%
ExcelReader.Benchmarks.DataReaderBenchmark.Baseline_RawRows(Rows: 50000) 10.62 ms 8.06 ms -24.1%
ExcelReader.Benchmarks.DataReaderBenchmark.DataReader_GetBytes(Rows: 50000) 11.13 ms 8.40 ms -24.5%
ExcelReader.Benchmarks.StringHeavyReadBenchmark.Xlsx_ExcelReader_Prefetch(Rows: 65536) 55.88 ms 41.83 ms -25.1%
ExcelReader.Benchmarks.XlsxSharedStringHotPathBenchmark.Stored_EndToEnd(Rows: 65536) 31.13 ms 22.92 ms -26.4%
ExcelReader.Benchmarks.ArrowConversionBenchmark.CsvAllString(Rows: 100000) 28.52 ms 20.91 ms -26.7%
ExcelReader.Benchmarks.StringHeavyReadBenchmark.Xlsx_Sylvan(Rows: 65536) 287.60 ms 204.54 ms -28.9%
ExcelReader.Benchmarks.ArrowConversionBenchmark.CsvTypedWithInference(Rows: 100000) 15.76 ms 11.06 ms -29.8%
ExcelReader.Benchmarks.EncryptedWorkbookBenchmark.Large_Encrypted_OpenOnly 66.83 ms 46.56 ms -30.3%
ExcelReader.Benchmarks.EncryptedWorkbookBenchmark.Encrypted_Stream 67.45 ms 46.96 ms -30.4%
ExcelReader.Benchmarks.ReadBenchmark.ExcelReaderXlsbAsync(Rows: 50000) 7.20 ms 4.33 ms -39.9%
ExcelReader.Benchmarks.ReadBenchmark.ExcelReaderXlsb(Rows: 50000) 7.33 ms 4.38 ms -40.2%
ExcelReader.Benchmarks.ReadBenchmark.ExcelReader(Rows: 50000) 11.43 ms 6.74 ms -41.1%
ExcelReader.Benchmarks.WritePathBenchmark.NativeStrings_Xlsx 27.18 ms 15.79 ms -41.9%
ExcelReader.Benchmarks.WritePathBenchmark.ArrowStrings_Xlsx 34.13 ms 19.72 ms -42.2%
ExcelReader.Benchmarks.ReadBenchmark.ExcelReaderMaterialized(Rows: 50000) 13.72 ms 7.93 ms -42.2%
ExcelReader.Benchmarks.XlsWriteBenchmark.XlsxWriter(Rows: 50000) 14.74 ms 8.47 ms -42.5%
ExcelReader.Benchmarks.WritePathBenchmark.NativeStrings_Csv 7.26 ms 4.09 ms -43.6%
ExcelReader.Benchmarks.WriteBenchmark.OfficeIMO(Rows: 50000) 25.15 ms 14.14 ms -43.8%
ExcelReader.Benchmarks.WriteBenchmark.ExcelReaderWriterSharedStrings(Rows: 50000) 14.86 ms 8.35 ms -43.8%
ExcelReader.Benchmarks.CsvWriteBenchmark.SylvanWriter(Rows: 50000) 10.47 ms 5.85 ms -44.2%
ExcelReader.Benchmarks.ReadBenchmark.ExcelReaderAsync(Rows: 50000) 12.64 ms 7.04 ms -44.2%
ExcelReader.Benchmarks.CsvWriteBenchmark.ExcelReaderWriter(Rows: 50000) 6.57 ms 3.65 ms -44.4%
ExcelReader.Benchmarks.WriteBenchmark.ExcelReaderWriter(Rows: 50000) 15.05 ms 8.29 ms -44.9%
ExcelReader.Benchmarks.WriteBenchmark.ExcelReaderWriterPrefetch(Rows: 50000) 11.87 ms 6.53 ms -45.0%
ExcelReader.Benchmarks.ReadBenchmark.OfficeIMO(Rows: 50000) 159.25 ms 87.23 ms -45.2%
ExcelReader.Benchmarks.WritePathBenchmark.StyledRows_Xlsx 8.68 ms 4.72 ms -45.6%
ExcelReader.Benchmarks.CsvWriteBenchmark.Sep(Rows: 50000) 9.99 ms 5.41 ms -45.9%
ExcelReader.Benchmarks.RecordWriteBenchmark.XlsbMapped(Rows: 50000) 11.70 ms 6.29 ms -46.2%
ExcelReader.Benchmarks.ReadBenchmark.OfficeIMOXlsb(Rows: 50000) 17.46 ms 9.38 ms -46.3%
ExcelReader.Benchmarks.RecordWriteBenchmark.Xlsx(Rows: 50000) 16.67 ms 8.96 ms -46.3%
ExcelReader.Benchmarks.RecordWriteBenchmark.CsvMapped(Rows: 50000) 7.92 ms 4.25 ms -46.4%
ExcelReader.Benchmarks.RecordWriteBenchmark.XlsxMapped(Rows: 50000) 17.71 ms 9.49 ms -46.4%
ExcelReader.Benchmarks.XlsReadBenchmark.ExcelReader(Rows: 50000) 5.36 ms 2.84 ms -47.0%
ExcelReader.Benchmarks.XlsReadBenchmark.ExcelReaderAsync(Rows: 50000) 4.99 ms 2.64 ms -47.0%
ExcelReader.Benchmarks.XlsReadBenchmark.OfficeIMO(Rows: 50000) 10.17 ms 5.33 ms -47.6%
ExcelReader.Benchmarks.CsvReadBenchmark.Sep(Rows: 50000) 13.50 ms 7.05 ms -47.8%
ExcelReader.Benchmarks.CsvReadBenchmark.ExcelReader(Rows: 50000) 3.97 ms 2.07 ms -48.0%
ExcelReader.Benchmarks.CsvReadBenchmark.ExcelReaderAsync(Rows: 50000) 4.11 ms 2.13 ms -48.1%
ExcelReader.Benchmarks.RecordWriteBenchmark.Csv(Rows: 50000) 7.22 ms 3.72 ms -48.5%
ExcelReader.Benchmarks.ReadBenchmark.Sylvan(Rows: 50000) 59.21 ms 30.46 ms -48.5%
ExcelReader.Benchmarks.RecordWriteBenchmark.Xlsb(Rows: 50000) 10.85 ms 5.58 ms -48.6%
ExcelReader.Benchmarks.WriteBenchmark.ExcelReaderXlsbWriterSharedStrings(Rows: 50000) 10.55 ms 5.42 ms -48.6%
ExcelReader.Benchmarks.WriteBenchmark.ExcelReaderXlsbWriter(Rows: 50000) 10.77 ms 5.51 ms -48.9%
ExcelReader.Benchmarks.XlsReadBenchmark.Sylvan(Rows: 50000) 8.75 ms 4.39 ms -49.8%
ExcelReader.Benchmarks.WriteBenchmark.SpreadCheetah(Rows: 50000) 25.31 ms 12.55 ms -50.4%
ExcelReader.Benchmarks.CsvReadBenchmark.ExcelReaderWide(Rows: 50000) 12.37 ms 6.08 ms -50.9%
ExcelReader.Benchmarks.CsvReadBenchmark.Sylvan(Rows: 50000) 6.27 ms 2.95 ms -52.9%
ExcelReader.Benchmarks.WriteBenchmark.ExcelReaderXlsbWriterPrefetch(Rows: 50000) 9.57 ms 4.32 ms -54.9%
ExcelReader.Benchmarks.RecordWriteBenchmark.Xls(Rows: 50000) 7.39 ms 3.20 ms -56.6%
ExcelReader.Benchmarks.XlsWriteBenchmark.XlsWriter(Rows: 50000) 8.87 ms 3.45 ms -61.1%

64 within ±10%.

Full results per group

ExcelReader.Benchmarks.ArrowConversionBenchmark


BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.5 LTS (Noble Numbat)
AMD EPYC 9V74 2.60GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.401
  [Host]     : .NET 10.0.12 (10.0.12, 10.0.1226.42308), X64 RyuJIT x86-64-v4
  Job-MEHJPP : .NET 10.0.12 (10.0.12, 10.0.1226.42308), X64 RyuJIT x86-64-v4

IterationCount=5  WarmupCount=1  

Method Rows Mean Error StdDev Ratio RatioSD Gen0 Gen1 Gen2 Allocated Alloc Ratio
CsvAllString 100000 20.91 ms 1.119 ms 0.291 ms 1.00 0.02 687.5000 562.5000 562.5000 16.27 MB 1.00
CsvTyped 100000 12.65 ms 0.103 ms 0.027 ms 0.60 0.01 343.7500 296.8750 296.8750 8.13 MB 0.50
XlsbTyped 100000 17.41 ms 1.431 ms 0.372 ms 0.83 0.02 468.7500 437.5000 437.5000 8.14 MB 0.50
CsvTypedWithInference 100000 11.06 ms 0.755 ms 0.117 ms 0.53 0.01 625.0000 593.7500 562.5000 14.89 MB 0.92

ExcelReader.Benchmarks.ChunkedParseBenchmark


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

IterationCount=5  WarmupCount=1  

Method Rows Mean Error StdDev Ratio Gen0 Gen1 Allocated Alloc Ratio
WholeSheet 50000 12.68 ms 0.070 ms 0.011 ms 1.00 78.1250 46.8750 1.29 MB 1.00
Batched_10k 50000 12.82 ms 0.103 ms 0.027 ms 1.01 93.7500 31.2500 1.59 MB 1.23
Batched_1k 50000 12.92 ms 0.085 ms 0.022 ms 1.02 93.7500 - 1.51 MB 1.16

ExcelReader.Benchmarks.ColdStartBenchmark


BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.5 LTS (Noble Numbat)
AMD EPYC 9V74 3.69GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.401
  [Host]     : .NET 10.0.12 (10.0.12, 10.0.1226.42308), X64 RyuJIT x86-64-v4
  Job-GOEFXF : .NET 10.0.12 (10.0.12, 10.0.1226.42308), X64 RyuJIT x86-64-v4

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

Method Rows Mean Error StdDev Allocated
TypedParseFirstUse 200 42.24 ms 5.079 ms 4.988 ms 21.09 KB
RecordWriteFirstUse 200 21.02 ms 0.380 ms 0.373 ms 84.25 KB
FluentParseFirstUse 200 34.94 ms 0.502 ms 0.493 ms 23.61 KB
FluentParseWithAttributeFallbackFirstUse 200 43.76 ms 1.043 ms 1.024 ms 25.31 KB

ExcelReader.Benchmarks.CsvParallelParseBenchmark


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

IterationCount=5  WarmupCount=1  

Method Dop Mean Error StdDev Ratio RatioSD Gen0 Gen1 Allocated Alloc Ratio
ConversionHeavy 1 1,467.6 ms 218.70 ms 56.80 ms 1.00 0.05 42000.0000 - 671.45 MB 1.000
NarrowInt 1 893.6 ms 161.41 ms 41.92 ms 0.61 0.03 15000.0000 - 245.7 MB 0.366
ConversionHeavyAggregate 1 1,226.1 ms 142.73 ms 37.07 ms 0.84 0.04 - - 1.17 MB 0.002
NarrowIntAggregate 1 728.5 ms 140.16 ms 36.40 ms 0.50 0.03 - - 1.16 MB 0.002
ConversionHeavy 2 1,148.3 ms 81.12 ms 12.55 ms 1.00 0.01 42000.0000 24000.0000 678.68 MB 1.000
NarrowInt 2 723.1 ms 35.95 ms 9.34 ms 0.63 0.01 15000.0000 3000.0000 253.08 MB 0.373
ConversionHeavyAggregate 2 775.4 ms 55.22 ms 14.34 ms 0.68 0.01 - - 1.21 MB 0.002
NarrowIntAggregate 2 493.3 ms 27.46 ms 4.25 ms 0.43 0.01 - - 1.19 MB 0.002
ConversionHeavy 4 752.2 ms 64.04 ms 16.63 ms 1.00 0.03 42000.0000 21000.0000 678.81 MB 1.000
NarrowInt 4 477.6 ms 46.43 ms 12.06 ms 0.64 0.02 15000.0000 1000.0000 253.23 MB 0.373
ConversionHeavyAggregate 4 512.7 ms 70.49 ms 10.91 ms 0.68 0.02 - - 1.23 MB 0.002
NarrowIntAggregate 4 323.8 ms 62.18 ms 16.15 ms 0.43 0.02 - - 1.2 MB 0.002
ConversionHeavy 8 751.9 ms 92.56 ms 14.32 ms 1.00 0.02 42000.0000 19000.0000 678.94 MB 1.000
NarrowInt 8 469.5 ms 47.69 ms 7.38 ms 0.62 0.01 15000.0000 1000.0000 253.27 MB 0.373
ConversionHeavyAggregate 8 494.8 ms 18.88 ms 2.92 ms 0.66 0.01 - - 1.23 MB 0.002
NarrowIntAggregate 8 332.2 ms 104.45 ms 27.12 ms 0.44 0.03 - - 1.23 MB 0.002
ConversionHeavy 16 756.7 ms 51.71 ms 13.43 ms 1.00 0.02 42000.0000 12000.0000 679.14 MB 1.000
NarrowInt 16 473.4 ms 22.36 ms 5.81 ms 0.63 0.01 15000.0000 1000.0000 253.77 MB 0.374
ConversionHeavyAggregate 16 518.4 ms 17.17 ms 4.46 ms 0.69 0.01 - - 1.27 MB 0.002
NarrowIntAggregate 16 322.0 ms 55.28 ms 8.55 ms 0.43 0.01 - - 1.26 MB 0.002

ExcelReader.Benchmarks.CsvParallelVsSepBenchmark


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

IterationCount=5  WarmupCount=1  

Method Corpus Mean Error StdDev Ratio RatioSD Gen0 Gen1 Gen2 Allocated Alloc Ratio
ExcelReader_Sequential NarrowInt 705.1 ms 41.46 ms 10.77 ms 1.00 0.02 15000.0000 - - 244.15 MB 1.00
ExcelReader_Parallel NarrowInt 476.6 ms 73.13 ms 18.99 ms 0.68 0.03 15000.0000 1000.0000 - 253.22 MB 1.04
Sep_Sequential NarrowInt 583.9 ms 4.53 ms 0.70 ms 0.83 0.01 15000.0000 - - 244.15 MB 1.00
Sep_Parallel NarrowInt 454.5 ms 72.94 ms 18.94 ms 0.64 0.03 17000.0000 13000.0000 1000.0000 257.87 MB 1.06
ExcelReader_Sequential ConversionHeavy 944.8 ms 7.60 ms 1.97 ms 1.00 0.00 29000.0000 - - 467.31 MB 1.00
ExcelReader_Parallel ConversionHeavy 536.8 ms 57.49 ms 14.93 ms 0.57 0.01 29000.0000 14000.0000 - 473.61 MB 1.01
Sep_Sequential ConversionHeavy 1,189.7 ms 18.54 ms 4.81 ms 1.26 0.01 29000.0000 - - 467.31 MB 1.00
Sep_Parallel ConversionHeavy 819.1 ms 23.97 ms 3.71 ms 0.87 0.00 33000.0000 32000.0000 3000.0000 477.74 MB 1.02

ExcelReader.Benchmarks.CsvParseBenchmark


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

IterationCount=5  WarmupCount=1  

Method Rows Mean Error StdDev Ratio Gen0 Gen1 Allocated Alloc Ratio
ExcelParserSync 50000 6.869 ms 0.0748 ms 0.0194 ms 1.00 242.1875 - 3.86 MB 1.00
ExcelParserAsync 50000 7.120 ms 0.1171 ms 0.0304 ms 1.04 242.1875 - 3.86 MB 1.00
Sep 50000 14.924 ms 0.3187 ms 0.0828 ms 2.17 234.3750 - 3.87 MB 1.00
Sylvan 50000 19.805 ms 0.1702 ms 0.0442 ms 2.88 656.2500 62.5000 10.95 MB 2.84

ExcelReader.Benchmarks.CsvReadBenchmark


BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.5 LTS (Noble Numbat)
AMD EPYC 9V45 2.60GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.401
  [Host]     : .NET 10.0.12 (10.0.12, 10.0.1226.42308), X64 RyuJIT x86-64-v4
  Job-MEHJPP : .NET 10.0.12 (10.0.12, 10.0.1226.42308), X64 RyuJIT x86-64-v4

IterationCount=5  WarmupCount=1  

Method Rows Mean Error StdDev Ratio RatioSD Allocated Alloc Ratio
ExcelReader 50000 2.068 ms 0.0504 ms 0.0078 ms 1.00 0.00 848 B 1.00
ExcelReaderWide 50000 6.076 ms 0.4706 ms 0.0728 ms 2.94 0.03 848 B 1.00
ExcelReaderAsync 50000 2.133 ms 0.0458 ms 0.0119 ms 1.03 0.01 920 B 1.08
Sep 50000 7.048 ms 0.1283 ms 0.0333 ms 3.41 0.02 4024 B 4.75
Sylvan 50000 2.950 ms 0.1355 ms 0.0352 ms 1.43 0.02 38681 B 45.61

ExcelReader.Benchmarks.CsvWriteBenchmark


BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.5 LTS (Noble Numbat)
AMD EPYC 9V45 2.60GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.401
  [Host]     : .NET 10.0.12 (10.0.12, 10.0.1226.42308), X64 RyuJIT x86-64-v4
  Job-MEHJPP : .NET 10.0.12 (10.0.12, 10.0.1226.42308), X64 RyuJIT x86-64-v4

IterationCount=5  WarmupCount=1  

Method Rows Mean Error StdDev Ratio RatioSD Gen0 Gen1 Gen2 Allocated Alloc Ratio
ExcelReaderWriter 50000 3.655 ms 0.4464 ms 0.1159 ms 1.00 0.04 500.0000 500.0000 500.0000 4 MB 1.00
Sep 50000 5.406 ms 0.6528 ms 0.1695 ms 1.48 0.06 500.0000 500.0000 500.0000 4.01 MB 1.00
SylvanWriter 50000 5.845 ms 0.3852 ms 0.1000 ms 1.60 0.05 500.0000 500.0000 500.0000 4.04 MB 1.01

ExcelReader.Benchmarks.DataReaderBenchmark


BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.5 LTS (Noble Numbat)
AMD EPYC 9V74 2.60GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.401
  [Host]     : .NET 10.0.12 (10.0.12, 10.0.1226.42308), X64 RyuJIT x86-64-v4
  Job-MEHJPP : .NET 10.0.12 (10.0.12, 10.0.1226.42308), X64 RyuJIT x86-64-v4

IterationCount=5  WarmupCount=1  

Method Rows Mean Error StdDev Ratio RatioSD Gen0 Gen1 Gen2 Allocated Alloc Ratio
Baseline_RawRows 50000 8.065 ms 0.0096 ms 0.0025 ms 1.00 0.00 - - - 3.87 KB 1.00
DataReader_GetValue 50000 11.027 ms 0.1061 ms 0.0164 ms 1.37 0.00 312.5000 - - 5131.51 KB 1,326.94
DataReader_TypedGetters 50000 10.967 ms 0.0335 ms 0.0087 ms 1.36 0.00 93.7500 - - 1615.88 KB 417.84
DataReader_GetBytes 50000 8.401 ms 0.0321 ms 0.0050 ms 1.04 0.00 - - - 4.55 KB 1.18
DataTable_Load 50000 67.423 ms 47.1124 ms 12.2349 ms 8.36 1.38 1500.0000 1000.0000 500.0000 23392.49 KB 6,048.97

ExcelReader.Benchmarks.EncryptedWorkbookBenchmark


BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.5 LTS (Noble Numbat)
AMD EPYC 9V74 3.37GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.401
  [Host]     : .NET 10.0.12 (10.0.12, 10.0.1226.42308), X64 RyuJIT x86-64-v4
  Job-MEHJPP : .NET 10.0.12 (10.0.12, 10.0.1226.42308), X64 RyuJIT x86-64-v4

IterationCount=5  WarmupCount=1  

Method Mean Error StdDev Ratio RatioSD Gen0 Allocated Alloc Ratio
Plain_Stream 46.96 μs 12.33 μs 3.202 μs 1.00 0.09 0.4883 9.14 KB 1.00
Encrypted_Stream 46,962.44 μs 2,236.12 μs 580.713 μs 1,003.76 61.90 - 38.2 KB 4.18
Encrypted_Stream_VerifyIntegrity 51,037.09 μs 328.15 μs 50.781 μs 1,090.85 66.48 - 40.06 KB 4.38
Encrypted_Memory 45,169.97 μs 104.94 μs 16.239 μs 965.45 58.83 - 53.16 KB 5.82
Encrypted_OpenOnly 46,066.93 μs 1,214.62 μs 315.434 μs 984.62 60.00 - 32.51 KB 3.56
Large_Plain_Stream 47,798.13 μs 2,216.95 μs 575.734 μs 1,021.62 62.94 - 3.87 KB 0.42
Large_Encrypted_Stream 98,592.65 μs 10,199.45 μs 2,648.766 μs 2,107.29 137.84 - 29.65 KB 3.24
Large_Encrypted_OpenOnly 46,557.48 μs 1,472.62 μs 382.435 μs 995.11 60.78 - 28.95 KB 3.17

ExcelReader.Benchmarks.NativeRowReadBenchmark


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

IterationCount=5  WarmupCount=1  

Method Mean Error StdDev Ratio RatioSD Gen0 Gen1 Gen2 Allocated Alloc Ratio
NextRowBlob 56.33 ms 0.665 ms 0.103 ms 1.00 0.00 222.2222 222.2222 222.2222 3.71 MB 1.00
NextRowDecoded 64.65 ms 3.279 ms 0.507 ms 1.15 0.01 250.0000 250.0000 250.0000 3.71 MB 1.00
ReadAllBlob 61.18 ms 0.961 ms 0.149 ms 1.09 0.00 1000.0000 888.8889 - 21.06 MB 5.68
ReadAllDecoded 90.42 ms 3.753 ms 0.975 ms 1.61 0.02 500.0000 500.0000 500.0000 5.71 MB 1.54

ExcelReader.Benchmarks.NativeTypedParseBenchmark


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

IterationCount=5  WarmupCount=1  

Method Mean Error StdDev Ratio Gen0 Gen1 Gen2 Allocated Alloc Ratio
ParseTyped 55.15 ms 1.134 ms 0.175 ms 1.00 700.0000 500.0000 300.0000 11.61 MB 1.00
ParseArrow 56.96 ms 3.313 ms 0.860 ms 1.03 700.0000 500.0000 300.0000 11.62 MB 1.00

ExcelReader.Benchmarks.ParseBenchmark


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

IterationCount=5  WarmupCount=1  

Method Rows Mean Error StdDev Ratio RatioSD Gen0 Allocated Alloc Ratio
ExcelParserSync 50000 16.80 ms 0.117 ms 0.030 ms 1.00 0.00 218.7500 3959.44 KB 1.000
ExcelParserSyncSharedStrings 50000 14.94 ms 0.024 ms 0.004 ms 0.89 0.00 140.6250 2349.38 KB 0.593
ExcelParserStructSync 50000 15.65 ms 0.167 ms 0.026 ms 0.93 0.00 93.7500 1615.69 KB 0.408
RefStructModelSync 50000 14.45 ms 0.050 ms 0.013 ms 0.86 0.00 - 4.36 KB 0.001
ExcelParserAsync 50000 17.63 ms 0.262 ms 0.068 ms 1.05 0.00 218.7500 3959.55 KB 1.000
ExcelParserXlsbSync 50000 11.72 ms 0.280 ms 0.073 ms 0.70 0.00 234.3750 3959.75 KB 1.000
ExcelParserXlsbAsync 50000 11.57 ms 0.112 ms 0.017 ms 0.69 0.00 234.3750 3959.86 KB 1.000
OfficeIMO 50000 191.59 ms 102.131 ms 15.805 ms 11.41 0.84 - 5834.04 KB 1.473
Sylvan 50000 86.75 ms 0.634 ms 0.098 ms 5.16 0.01 500.0000 10726.37 KB 2.709
SylvanAsync 50000 89.37 ms 1.082 ms 0.167 ms 5.32 0.01 - 10727.74 KB 2.709

ExcelReader.Benchmarks.ReadBenchmark


BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.5 LTS (Noble Numbat)
AMD EPYC 9V45 2.60GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.401
  [Host]     : .NET 10.0.12 (10.0.12, 10.0.1226.42308), X64 RyuJIT x86-64-v4
  Job-MEHJPP : .NET 10.0.12 (10.0.12, 10.0.1226.42308), X64 RyuJIT x86-64-v4

IterationCount=5  WarmupCount=1  

Method Rows Mean Error StdDev Ratio RatioSD Gen0 Allocated Alloc Ratio
ExcelReader 50000 6.736 ms 0.4507 ms 0.1171 ms 1.00 0.02 - 3.82 KB 1.00
ExcelReaderAsync 50000 7.044 ms 0.2092 ms 0.0543 ms 1.05 0.02 - 3.89 KB 1.02
ExcelReaderXlsb 50000 4.382 ms 0.1279 ms 0.0198 ms 0.65 0.01 - 4.13 KB 1.08
ExcelReaderXlsbAsync 50000 4.328 ms 0.0075 ms 0.0019 ms 0.64 0.01 - 4.2 KB 1.10
ExcelReaderMaterialized 50000 7.927 ms 0.3730 ms 0.0577 ms 1.18 0.02 93.7500 1615.15 KB 422.78
Sylvan 50000 30.463 ms 2.7169 ms 0.7056 ms 4.52 0.12 62.5000 1939.18 KB 507.60
OfficeIMO 50000 87.233 ms 11.5015 ms 2.9869 ms 12.95 0.45 333.3333 7001.62 KB 1,832.73
OfficeIMOXlsb 50000 9.383 ms 0.3195 ms 0.0830 ms 1.39 0.02 406.2500 6858.69 KB 1,795.32

ExcelReader.Benchmarks.RealDataReadBenchmark


BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.5 LTS (Noble Numbat)
AMD EPYC 7763 3.19GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.401
  [Host]     : .NET 10.0.12 (10.0.12, 10.0.1226.42308), X64 RyuJIT x86-64-v3
  Job-MEHJPP : .NET 10.0.12 (10.0.12, 10.0.1226.42308), X64 RyuJIT x86-64-v3

IterationCount=5  WarmupCount=1  

Method Mean Error StdDev Ratio RatioSD Gen0 Allocated Alloc Ratio
Xlsx_ExcelReader 73.263 ms 2.3248 ms 0.3598 ms 1.00 0.01 - 7808 B 1.00
Xlsx_Sylvan 315.434 ms 14.2467 ms 3.6998 ms 4.31 0.05 - 659568 B 84.47
Xlsx_ExcelReader_Materialized 76.530 ms 1.3083 ms 0.3398 ms 1.04 0.01 - 17504 B 2.24
Xlsx_ExcelReader_Prefetch 47.462 ms 13.3935 ms 3.4782 ms 0.65 0.04 - 27117 B 3.47
Xlsx_ExcelReader_Memory 73.139 ms 1.6812 ms 0.4366 ms 1.00 0.01 - 7552 B 0.97
Xlsx_ExcelReader_Memory_Prefetch 46.778 ms 8.1714 ms 2.1221 ms 0.64 0.03 - 27646 B 3.54
Xlsm_ExcelReader 74.023 ms 0.4893 ms 0.0757 ms 1.01 0.00 - 7808 B 1.00
Xlsm_Sylvan 317.153 ms 6.1591 ms 1.5995 ms 4.33 0.03 - 660072 B 84.54
Xlsm_ExcelReader_Materialized 75.150 ms 1.3836 ms 0.2141 ms 1.03 0.01 - 17504 B 2.24
Xlsm_ExcelReader_Prefetch 46.789 ms 15.4044 ms 4.0005 ms 0.64 0.05 - 27607 B 3.54
Xlsm_ExcelReader_Memory 73.248 ms 0.9224 ms 0.2395 ms 1.00 0.01 - 7552 B 0.97
Xlsm_ExcelReader_Memory_Prefetch 46.951 ms 11.9883 ms 3.1133 ms 0.64 0.04 - 27636 B 3.54
Xlsb_ExcelReader 35.971 ms 0.3972 ms 0.1032 ms 0.49 0.00 - 7352 B 0.94
Xlsb_Sylvan 43.937 ms 0.2295 ms 0.0355 ms 0.60 0.00 - 346673 B 44.40
Xlsb_ExcelReader_Materialized 36.744 ms 0.6587 ms 0.1711 ms 0.50 0.00 - 17048 B 2.18
Xlsb_ExcelReader_Prefetch 21.360 ms 3.3503 ms 0.5185 ms 0.29 0.01 - 20383 B 2.61
Xlsb_ExcelReader_Memory 35.738 ms 0.1504 ms 0.0233 ms 0.49 0.00 - 9096 B 1.16
Xlsb_ExcelReader_Memory_Prefetch 19.142 ms 2.1750 ms 0.3366 ms 0.26 0.00 - 22515 B 2.88
Xls_ExcelReader 16.370 ms 0.2989 ms 0.0776 ms 0.22 0.00 - 12304 B 1.58
Xls_Sylvan 26.849 ms 0.4935 ms 0.1282 ms 0.37 0.00 - 190366 B 24.38
Xls_ExcelReader_Materialized 18.755 ms 0.2068 ms 0.0537 ms 0.26 0.00 - 22000 B 2.82
Xls_ExcelReader_Memory 16.342 ms 0.0910 ms 0.0141 ms 0.22 0.00 - 12168 B 1.56
Csv_ExcelReader 8.415 ms 0.0077 ms 0.0012 ms 0.11 0.00 - 848 B 0.11
Csv_Sylvan 6.406 ms 0.0135 ms 0.0035 ms 0.09 0.00 - 41226 B 5.28
Csv_ExcelReader_Materialized 30.826 ms 0.7080 ms 0.1096 ms 0.42 0.00 2218.7500 37443456 B 4,795.52
Csv_ExcelReader_Memory 8.259 ms 0.1791 ms 0.0277 ms 0.11 0.00 - 576 B 0.07

ExcelReader.Benchmarks.RealDataTypedParseBenchmark


BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.5 LTS (Noble Numbat)
AMD EPYC 7763 3.19GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.401
  [Host]     : .NET 10.0.12 (10.0.12, 10.0.1226.42308), X64 RyuJIT x86-64-v3
  Job-MEHJPP : .NET 10.0.12 (10.0.12, 10.0.1226.42308), X64 RyuJIT x86-64-v3

IterationCount=5  WarmupCount=1  

Method Mean Error StdDev Ratio Gen0 Allocated Alloc Ratio
Xlsx_ExcelParser 87.56 ms 3.057 ms 0.794 ms 1.00 500.0000 8.02 MB 1.00
Xlsb_ExcelParser 46.51 ms 0.250 ms 0.039 ms 0.53 454.5455 8.02 MB 1.00

ExcelReader.Benchmarks.RecordWriteBenchmark


BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.5 LTS (Noble Numbat)
AMD EPYC 9V45 2.60GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.401
  [Host]     : .NET 10.0.12 (10.0.12, 10.0.1226.42308), X64 RyuJIT x86-64-v4
  Job-MEHJPP : .NET 10.0.12 (10.0.12, 10.0.1226.42308), X64 RyuJIT x86-64-v4

IterationCount=5  WarmupCount=1  

Method Rows Mean Error StdDev Ratio RatioSD Gen0 Gen1 Gen2 Allocated Alloc Ratio
Xlsx 50000 8.961 ms 1.3649 ms 0.3545 ms 1.00 0.05 500.0000 500.0000 500.0000 4.02 MB 1.00
Xlsb 50000 5.582 ms 0.4020 ms 0.1044 ms 0.62 0.02 500.0000 500.0000 500.0000 4.02 MB 1.00
Xls 50000 3.202 ms 0.2814 ms 0.0435 ms 0.36 0.01 277.3438 277.3438 277.3438 4.03 MB 1.00
Csv 50000 3.717 ms 0.3714 ms 0.0964 ms 0.42 0.02 500.0000 500.0000 500.0000 4 MB 1.00
XlsxMapped 50000 9.493 ms 0.7617 ms 0.1978 ms 1.06 0.04 500.0000 500.0000 500.0000 4.02 MB 1.00
XlsbMapped 50000 6.293 ms 0.2277 ms 0.0591 ms 0.70 0.03 500.0000 500.0000 500.0000 4.02 MB 1.00
CsvMapped 50000 4.249 ms 0.6919 ms 0.1797 ms 0.47 0.02 500.0000 500.0000 500.0000 4 MB 1.00

ExcelReader.Benchmarks.StringHeavyReadBenchmark


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

IterationCount=5  WarmupCount=1  

Method Rows Mean Error StdDev Ratio RatioSD Gen0 Allocated Alloc Ratio
Xlsx_ExcelReader 65536 55.48 ms 0.286 ms 0.044 ms 1.00 0.00 - 2.18 MB 1.00
Xlsx_ExcelReader_Prefetch 65536 41.83 ms 5.393 ms 1.401 ms 0.75 0.02 - 2.27 MB 1.04
Xlsx_Sylvan 65536 204.54 ms 9.090 ms 2.361 ms 3.69 0.04 - 17.41 MB 7.98
Xlsx_ExcelReader_Materialized 65536 79.96 ms 7.501 ms 1.948 ms 1.44 0.03 142.8571 15.25 MB 6.99
Xlsx_ExcelReader_Materialized_Interned 65536 79.66 ms 7.962 ms 2.068 ms 1.44 0.03 142.8571 15.26 MB 7.00
Xlsb_ExcelReader 65536 48.87 ms 0.440 ms 0.068 ms 0.88 0.00 - 2.18 MB 1.00
Xlsb_ExcelReader_Prefetch 65536 39.24 ms 0.904 ms 0.235 ms 0.71 0.00 - 2.33 MB 1.07
Xlsb_Sylvan 65536 80.55 ms 13.915 ms 3.614 ms 1.45 0.06 142.8571 17.38 MB 7.97
Xlsb_ExcelReader_Materialized 65536 70.76 ms 5.808 ms 1.508 ms 1.28 0.02 142.8571 15.25 MB 6.99
Xlsb_ExcelReader_Materialized_Interned 65536 70.75 ms 1.126 ms 0.174 ms 1.28 0.00 125.0000 15.26 MB 7.00
Xlsb_ExcelReader_Memory 65536 48.22 ms 0.570 ms 0.148 ms 0.87 0.00 - 6.47 MB 2.97

ExcelReader.Benchmarks.WriteBenchmark


BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.5 LTS (Noble Numbat)
AMD EPYC 9V45 2.60GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.401
  [Host]     : .NET 10.0.12 (10.0.12, 10.0.1226.42308), X64 RyuJIT x86-64-v4
  Job-MEHJPP : .NET 10.0.12 (10.0.12, 10.0.1226.42308), X64 RyuJIT x86-64-v4

IterationCount=5  WarmupCount=1  

Method Rows Mean Error StdDev Ratio RatioSD Gen0 Gen1 Gen2 Allocated Alloc Ratio
ExcelReaderWriter 50000 8.288 ms 0.5045 ms 0.1310 ms 1.00 0.02 500.0000 500.0000 500.0000 4.02 MB 1.00
ExcelReaderWriterSharedStrings 50000 8.353 ms 0.1822 ms 0.0473 ms 1.01 0.02 500.0000 500.0000 500.0000 4.06 MB 1.01
ExcelReaderWriterPrefetch 50000 6.531 ms 0.8638 ms 0.2243 ms 0.79 0.03 492.1875 492.1875 492.1875 4.03 MB 1.00
ExcelReaderXlsbWriter 50000 5.507 ms 0.3407 ms 0.0527 ms 0.66 0.01 500.0000 500.0000 500.0000 4.02 MB 1.00
ExcelReaderXlsbWriterSharedStrings 50000 5.422 ms 0.2998 ms 0.0779 ms 0.65 0.01 500.0000 500.0000 500.0000 4.06 MB 1.01
ExcelReaderXlsbWriterPrefetch 50000 4.315 ms 0.3655 ms 0.0566 ms 0.52 0.01 492.1875 492.1875 492.1875 4.03 MB 1.00
OfficeIMO 50000 14.141 ms 0.8836 ms 0.1367 ms 1.71 0.03 328.1250 328.1250 328.1250 4.02 MB 1.00
SpreadCheetah 50000 12.552 ms 0.3815 ms 0.0590 ms 1.51 0.02 1390.6250 656.2500 656.2500 15.84 MB 3.94

ExcelReader.Benchmarks.WritePathBenchmark


BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.5 LTS (Noble Numbat)
AMD EPYC 9V45 2.60GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.401
  [Host]     : .NET 10.0.12 (10.0.12, 10.0.1226.42308), X64 RyuJIT x86-64-v4
  Job-MEHJPP : .NET 10.0.12 (10.0.12, 10.0.1226.42308), X64 RyuJIT x86-64-v4

IterationCount=5  WarmupCount=1  

Method Mean Error StdDev Allocated
NativeStrings_Xlsx 15.793 ms 0.9674 ms 0.1497 ms 18448 B
NativeStrings_Csv 4.092 ms 0.1796 ms 0.0278 ms 416 B
ArrowStrings_Xlsx 19.720 ms 0.8702 ms 0.2260 ms 18496 B
StyledRows_Xlsx 4.723 ms 0.4156 ms 0.1079 ms 18632 B

ExcelReader.Benchmarks.XlsReadBenchmark


BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.5 LTS (Noble Numbat)
AMD EPYC 9V45 2.60GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.401
  [Host]     : .NET 10.0.12 (10.0.12, 10.0.1226.42308), X64 RyuJIT x86-64-v4
  Job-MEHJPP : .NET 10.0.12 (10.0.12, 10.0.1226.42308), X64 RyuJIT x86-64-v4

IterationCount=5  WarmupCount=1  

Method Rows Mean Error StdDev Ratio Gen0 Gen1 Allocated Alloc Ratio
ExcelReader 50000 2.839 ms 0.1084 ms 0.0168 ms 1.00 - - 2.84 KB 1.00
ExcelReaderAsync 50000 2.644 ms 0.0451 ms 0.0117 ms 0.93 - - 2.91 KB 1.02
Sylvan 50000 4.392 ms 0.2148 ms 0.0332 ms 1.55 101.5625 23.4375 1717.73 KB 605.70
OfficeIMO 50000 5.327 ms 0.1993 ms 0.0308 ms 1.88 640.6250 - 10499.25 KB 3,702.21

ExcelReader.Benchmarks.XlsWriteBenchmark


BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.5 LTS (Noble Numbat)
AMD EPYC 9V45 2.60GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.401
  [Host]     : .NET 10.0.12 (10.0.12, 10.0.1226.42308), X64 RyuJIT x86-64-v4
  Job-MEHJPP : .NET 10.0.12 (10.0.12, 10.0.1226.42308), X64 RyuJIT x86-64-v4

IterationCount=5  WarmupCount=1  

Method Rows Mean Error StdDev Ratio RatioSD Gen0 Gen1 Gen2 Allocated Alloc Ratio
XlsWriter 50000 3.453 ms 0.0352 ms 0.0055 ms 1.00 0.00 496.0938 496.0938 496.0938 16.03 MB 1.00
XlsxWriter 50000 8.474 ms 0.2124 ms 0.0552 ms 2.45 0.02 500.0000 500.0000 500.0000 4.02 MB 0.25

ExcelReader.Benchmarks.XlsxSharedStringHotPathBenchmark


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

IterationCount=5  WarmupCount=1  

Method Rows Mean Error StdDev Ratio RatioSD Allocated Alloc Ratio
EndToEnd 65536 55.804 ms 2.4258 ms 0.3754 ms 1.00 0.01 2286056 B 1.00
EndToEnd_Prefetch 65536 42.705 ms 7.0137 ms 1.8214 ms 0.77 0.03 2381875 B 1.04
SharedStringPrologue 65536 21.015 ms 0.3276 ms 0.0851 ms 0.38 0.00 765192 B 0.33
Stored_EndToEnd 65536 22.918 ms 0.1943 ms 0.0301 ms 0.41 0.00 2284976 B 1.00
Stored_SharedStringPrologue 65536 5.286 ms 0.0180 ms 0.0047 ms 0.09 0.00 764112 B 0.33
IndexParse_Utf8Parser 65536 2.539 ms 0.0418 ms 0.0065 ms 0.05 0.00 - 0.00
IndexParse_DigitLoop 65536 1.831 ms 0.0136 ms 0.0035 ms 0.03 0.00 - 0.00

@GabrielMarquezMatte
GabrielMarquezMatte merged commit ca5b50f into master Oct 7, 2026
58 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