The performance record
The time and peak memory of reading and writing synthetic data at a fixed width of 100 columns and 10,000 rows / 100,000 rows (1,000,000 cells / 10,000,000 cells) — the standard of Appendix B.39.11. These numbers are not remembered but measured by scripts/bench.sh; the numbers the README quotes are those of the 10,000-row tier, and they are checked by machine against this page's source. A cell that could not be measured says why — an operation this machine's physical memory or free disk cannot hold is decided against, and recorded, before it can fall over.
| Measurement | 10,000 rows (1,000,000 cells) | 100,000 rows (10,000,000 cells) | ||
|---|---|---|---|---|
| What was done | Time | Peak memory | Time | Peak memory |
| Build the model put every cell into a Workbook (no file) |
0.37 s | 203.1 MB | 3.85 s | 1079.2 MB |
| Write (xlsx) model -> .xlsx |
0.93 s | 214.8 MB of which the model: 202.8 MB |
10.02 s | 1168.3 MB of which the model: 1168.3 MB |
| Read (xlsx) .xlsx -> model, then sum every cell |
1.47 s | 213.3 MB | 15.37 s | 1054.4 MB |
| Streaming read (xlsx) StreamingReader, row by row, then sum every cell |
1.01 s | 13.2 MB | 10.16 s | 19.7 MB |
| Open, change one cell, save .xlsx -> model -> .xlsx |
2.20 s | 209.1 MB | 23.17 s | 1080.5 MB |
| Format detection SheetFormat.detect(contentsOf:), per call |
under 1 ms | 7.0 MB | under 1 ms | 7.0 MB |
| Inspect before reading Workbook.inspect(contentsOf:) |
2.0 ms | 10.6 MB | 3.0 ms | 10.6 MB |
| Streaming write (xlsx) StreamingWriter, row by row |
1.10 s | 10.8 MB | 11.35 s | 10.8 MB |
| Streaming read (xlsx) (the streaming writer's file) StreamingReader, row by row, then sum every cell |
1.08 s | 12.8 MB | 10.84 s | 12.8 MB |
| Format detection (the streaming writer's file) SheetFormat.detect(contentsOf:), per call |
under 1 ms | 7.0 MB | under 1 ms | 7.0 MB |
| Inspect before reading (the streaming writer's file) Workbook.inspect(contentsOf:) |
2.0 ms | 10.6 MB | 2.0 ms | 10.6 MB |
| Write (xlsx, 8 sheets) the same cells split across 8 sheets, model -> .xlsx |
0.85 s | 207.5 MB of which the model: 196.6 MB |
9.90 s | 1293.2 MB of which the model: 1098.2 MB |
| Read (xlsx, 8 sheets, one at a time) ReadOptions(concurrency: 1), then sum every cell |
1.35 s | 139.1 MB | 14.63 s | 1045.1 MB |
| Read (xlsx, 8 sheets, side by side) the default (sheets read concurrently), then sum every cell |
0.65 s | 228.8 MB | 6.99 s | 1222.2 MB |
| Write (ods) model -> .ods |
1.81 s | 213.2 MB of which the model: 202.9 MB |
18.99 s | 1209.3 MB of which the model: 1209.3 MB |
| Read (ods) .ods -> model |
2.46 s | 208.0 MB | 25.07 s | 1230.5 MB |
| Streaming read (ods) StreamingReader, row by row, then sum every cell |
2.42 s | 14.5 MB | 24.35 s | 14.5 MB |
| Streaming write (ods) StreamingWriter, row by row |
1.64 s | 21.6 MB | 16.36 s | 16.7 MB |
| Streaming read (ods) (the streaming writer's file) StreamingReader, row by row, then sum every cell |
2.42 s | 14.5 MB | 24.03 s | 14.5 MB |
| Write (csv) model -> .csv |
0.48 s | 237.6 MB of which the model: 202.9 MB |
5.60 s | 1290.5 MB of which the model: 1215.4 MB |
| Read (csv) .csv -> model (with type inference) |
0.79 s | 165.3 MB | 8.65 s | 1293.2 MB |
| Streaming read (csv) CSVStreamingReader, row by row |
0.50 s | 10.8 MB | 5.08 s | 11.0 MB |
| Streaming write (csv) CSVStreamingWriter, row by row |
0.48 s | 7.7 MB | 4.86 s | 7.7 MB |
| Streaming read (csv) (the streaming writer's file) CSVStreamingReader, row by row |
0.50 s | 10.8 MB | 5.09 s | 11.0 MB |
| Write (numbers) model -> .numbers |
2.03 s | 374.0 MB of which the model: 202.8 MB |
21.37 s | 1372.1 MB of which the model: 1104.0 MB |
| Read (numbers) .numbers -> model, then sum every cell |
1.01 s | 279.2 MB | 10.63 s | 1759.4 MB |
| Streaming read (numbers) StreamingReader, row by row, then sum every cell |
0.51 s | 24.8 MB | 4.53 s | 59.6 MB |
| Streaming write (numbers) StreamingWriter, row by row |
2.93 s | 25.5 MB | 26.84 s | 65.7 MB |
| Streaming read (numbers) (the streaming writer's file) StreamingReader, row by row, then sum every cell |
0.50 s | 24.8 MB | 4.49 s | 59.6 MB |
Conditions of measurement
- Date: 2026-09-13, commit:
602b86c - Machine: Apple M2, 8 GB of memory, 27.0
- Toolchain: swift-driver version: 1.168.6 Apple Swift version 6.4 (swiftlang-6.4.0.34.1 clang-2100.3.34.1), release build
- Material: synthetic, 100 columns: a label, five integers, three decimals and a Japanese category, repeated across the width with the numbers offset per block; the text columns past the first block draw on a vocabulary of fifty; one sheet; no formatting. Synthetic data gives synthetic numbers: a real workbook, heavy with formatting and drawings, is heavier than this.
- Peak memory is the process's lifetime maximum (
ru_maxrss), which is why every measurement runs in a process of its own. - A reading figure is taken on the file the whole-model writer wrote (the file an application writes). The figure taken on the streaming writer's file is shown separately, in the row marked "the streaming writer's file".
Measuring on your own machine
scripts/bench.sh # both tiers: rebuilds Benchmarks/ in release, measures, writes docs/performance.json
scripts/bench.sh --rows 10000 # one tier only
python3 scripts/build-performance-page.py --check # this page and the README's numbers match the source