typed-xlsx
Performance

Benchmarks

Measured streaming throughput and peak memory for 200k and 500k rows, and a real 202k-row export, comparing typed-xlsx 4.2.0 with the current engine.

These numbers come from the streaming benchmark in packages/benchmark, run on 2026-09-22 against version 4.2.0 and the current engine with the same harness, schema and data. The charts read the published report directly.

Export time

Each workload writes an 11-column report with styles, dates, number formats and summary rows, committed in 5,000-row batches. Every notes cell contains XML-reserved characters and Unicode, so escaping is part of the measurement.

Total export time, lower is better4.2.0current
200k rows, shared strings
11.66 s
3.45 s
200k rows, inline strings
12.05 s
3.72 s
200k rows, stacked layout
14.09 s
4.46 s
500k rows, inline strings
30.38 s
9.30 s
Apple M5, 32 GiB, Node v24.13.0. Measured 2026-09-22.

The current engine is 3.2× to 3.4× faster across the four workloads. The gain is in row processing and spooling: finalizing the ZIP still takes about one second at 200k rows and 2.4 seconds at 500k, as before.

Peak memory

Peak memory (RSS), lower is better4.2.0current
200k rows, shared strings
424 MiB
415 MiB
200k rows, inline strings
317 MiB
274 MiB
200k rows, stacked layout
414 MiB
380 MiB
500k rows, inline strings
417 MiB
279 MiB
Apple M5, 32 GiB, Node v24.13.0. Measured 2026-09-22.

With inline strings, peak memory barely moves between 200k and 500k rows (274 and 279 MiB). Shared strings trade memory for a smaller file: the string table stays in memory until the workbook is finalized. See Large Exports for how to choose.

Memory is not constant for every schema. Shared strings, merged sub-rows, images, hyperlinks and row-aware summary formulas keep their own metadata for the whole export.

A real application export

The same engines exported a production-shaped report from a local PostgreSQL database: 202,058 records, 25 columns, 41 batches, with file spooling and the low-memory profile. The whole worker job went from 40.3 s to 19.4 s.

Real export of 202,058 rows × 25 columns, whole jobFetch recordsCommit XLSX rowsFinalize
4.2.0
40.3 s
current
19.4 s
Apple M5, 32 GiB, Node v24.13.0. Measured 2026-09-22.
Phase4.2.0Current
Fetch hydrated records11.08 s10.58 s
Commit XLSX rows27.21 s6.88 s
Finalize XLSX1.66 s1.54 s
Whole job40.33 s19.36 s

Writing the five million cells went from 27.2 s to 6.9 s, so fetching from the database is now the slowest phase. Both versions produced byte-identical files (21,832,191 bytes).

How it was measured

  • Node 24.13.0 on an Apple M5 with 32 GiB of RAM, macOS.
  • Baseline and current engine run serially in alternating fresh processes.
  • 200k-row results are the median of three runs. The 500k-row case is a single run per engine, so read it as a scaling check.
  • Peak memory is the process high-water mark (maxRSS).
  • All 20 benchmark workbooks are byte-identical between the two engines.

These are local measurements, not a production or concurrent-load guarantee. The full report, raw JSON and the commands to reproduce it are in packages/benchmark/artifacts/reports/stream.

Copyright © 2026 Cyprien Thao. Released under the MIT License.