Environment
- MacBook Pro, Apple M1 Max, 64 GB RAM, internal SSD
- macOS 15.7, Release build, for the rows recorded July 2 and September 9, 2026. The 10 GB find and filter rows were re-measured September 23, the two index rows and the 10 million row sort September 24, and the 1 GB CSV rows October 5, 2026, on the same machine on macOS 27.0 with Caxton 2.0
- Files up to 10 GB run from a warm file cache, except the index rows, which run from disk; the 50 GB rows do not fit the cache and run from disk (see the notes below)
- Corpus: generated log/CSV/JSONL files from 10 KB to 50 GB: realistic server-log lines, 50-column CSVs, single-line minified bundles, mixed-script UTF-8. The 50 GB log and the 10 million row CSV were added September 9, 2026 with Caxton 1.7.2
Results
| Operation | File | Result |
|---|---|---|
| Open, usable (scroll, search, edit) | 10 GB log · 80,610,954 lines | immediate |
| Full background line index, from disk | 10 GB log | 3.7 s |
| Find, literal (1,613,344 matches) | 10 GB log | 1.0 s |
| Find, case-insensitive | 10 GB log | 2.1 s |
| Find, whole word | 10 GB log | 1.0 s |
| Regex alternation (5,643,688 matches) | 10 GB log | 1.8 s |
| Replace All: 1,613,344 replacements | 10 GB log | 1.7 s |
| Filter to matching lines (1.61M of 80.6M) | 10 GB log | 1.0 s |
| Filter, two-condition AND | 10 GB log | 2.1 s |
| Filter, two-condition OR (5.6M lines) | 10 GB log | 1.8 s |
| CSV open to a working grid (index and parse) | 1 GB · 2,022,947 rows × 50 cols | 2.3 s |
| CSV sort by numeric column, streamed to a new document | 1 GB · 2M rows | 4.4 s |
| Save after scattered edits (atomic rewrite) | 10 GB log | 11.3 s |
| Single keystroke apply latency | 10 GB document | ≈ 1 ms |
| One keystroke across 1,000 cursors | 10 GB document | < 120 ms |
| Undo a 1,000-cursor edit | 10 GB document | < 130 ms |
| Find in a one-line file | 500 MB minified JS · 1 line | 0.5 s |
| Bookmark 5,000 lines + persist + re-anchor | 500 MB · 5M lines | 0.2 s |
| Open, usable (scroll, search, edit) | 50 GB log · 403,046,288 lines | immediate |
| Full background line index, from disk | 50 GB log | 22 s |
| Find, literal (8,059,509 matches, counted exactly) | 50 GB log | 46 s |
| Regex alternation (28,211,000 matches) | 50 GB log | 49 s |
| Filter to matching lines (8.06M of 403M) | 50 GB log | 47 s |
| Filter, negate (394,986,779 lines, exact complement) | 50 GB log | 47 s |
| Replace All: 1,574,039 replacements, one undo step | 50 GB log | 43 s, undo 0.6 s |
| Save after scattered edits (atomic rewrite) | 50 GB log | 122 s |
| Single keystroke apply latency | 50 GB document | ≈ 1 ms |
| One keystroke across 1,000 cursors | 50 GB document | 3.7 s first, 0.2 s after |
| Scroll, worst frame | 50 GB log | 7 ms |
| Memory footprint through every 50 GB row above, on macOS 15.7 | 50 GB log | under 160 MB |
| CSV parse to working grid | 5.1 GB · 10,000,000 rows × 50 cols | 4.4 s |
| CSV sort by numeric column, streamed to a new document | 5.1 GB · 10M rows | 25 s |
| Find in a CSV, literal (2,146,318 matches, counted exactly) | 5.1 GB · 10M rows | 0.8 s |
| Filter a CSV to matching rows (1.93M of 10M) | 5.1 GB · 10M rows | 1.0 s |
2.0: the first pass from disk, and Table Mode at scale
Measured September 24, 2026 on the same MacBook Pro (Apple M1 Max, 64 GB) on macOS 27.0 with Caxton 2.0.0. The index rows ran with a cold page cache (a 70 GB scratch file read through before each run, so the corpus came from disk); every Table Mode figure is reported at the size it was measured and is not generalized to another size. The 1 GB rows and the 50 million row memory rows were measured again on October 5, 2026 with Caxton 2.0.1, on the same machine, and show that run. Grid memory scales with the record count on the memory tier and stays flat on the disk tier, where the record index lives in scratch space.
| Operation | File | Result |
|---|---|---|
| Full background line index, from disk (was 16.4 s) | 10 GB log | 3.7 s |
| Full background line index, from disk (was 85.5 s) | 50 GB log | 22 s |
| Grid memory, footprint (record index in memory) | 1 GB · 2M rows | 229 MB |
| Grid memory, footprint (record index in memory) | 5.1 GB · 10M rows | 369 MB |
| Grid memory, footprint (record index in memory) | 7.7 GB · 50M rows | 687 MB |
| Grid memory, footprint (record index on disk) | 7.7 GB · 50M rows | 212 MB |
| Memory after the grid closes | 7.7 GB · 50M rows | under 130 MB, 0.3 s after the close |
| Validate every row (structure and a type rule) | 1 GB · 2M rows | 18 s |
| Validate every row (structure and a type rule) | 5.1 GB · 10M rows | 91 s |
| Validate every row (structure and a type rule) | 7.7 GB · 50M rows | 236 s |
| Column profile with a frequency table (1,000,000 distinct values) | 1 GB · 2M rows | 15 s |
| Column profile with a frequency table (1,000,000 distinct values) | 5.1 GB · 10M rows | 75 s |
| Column profile with a frequency table (1,000,000 distinct values) | 7.7 GB · 50M rows | 341 s |
| Sort by a numeric column, streamed to a new file | 1 GB · 2M rows | 4.4 s |
| Sort by a numeric column, streamed to a new file | 5.1 GB · 10M rows | 25 s |
| Sort by a numeric column (five runs in scratch), streamed to a new file | 7.7 GB · 50M rows | 86 s |
| Find duplicates on a column (45,153 groups) | 1 GB · 2M rows | 4.5 s |
| Find duplicates on a column (198,582 groups) | 5.1 GB · 10M rows | 21 s |
| Find duplicates on a column (2,246,521 groups) | 7.7 GB · 50M rows | 61 s, 4.0 GB at the peak |
Evidence: bench-results/2026-09-24-d3/ in the repository, with the cold-cache A/B and every run's memory and scratch peaks; the duplicates row at 50M rows is bench-results/2026-09-24-d4/, after the finder's tables moved onto pages; the October 5 rows are bench-results/2026-10-05-2.0.1/ (pillar_1gb_suite.jsonl and the two lifecycle_50m suites).
Methodology notes
- The harness runs inside the real app (real document model, real UI, real renderer), driven by environment variables, with results streamed as JSON.
- Correctness is asserted alongside speed: filter results are cross-checked against find results line-for-line, negated filters must complement exactly, and undo must restore byte-identical content (verified by re-searching).
- "Immediate" open means the window is interactive before indexing completes; indexing streams in the background and the status bar reports progress.
- Numbers vary with hardware and file content. These are honest medians on the stated machine, not best-of-N cherry-picks.
- At 50 GB the file no longer fits the page cache of a 64 GB machine, so every full pass faults its pages back from disk and lands near 1 GB/s; the same passes over 10 GB ran from memory at about 7.7 GB/s on macOS 15.7. The memory footprint does not grow with the file: under 160 MB at 50 GB on macOS 15.7. macOS 26 and later also count the window's own drawing buffers in an app's footprint, about 270 MB of the 351 MB an idle Caxton window reports on this Mac's built-in display, so the same runs read higher there without the engine using more. A control run of the 10 GB suite immediately after a 50 GB pass reproduced the 10 GB rows.
- The index rows marked from disk ran with a cold page cache: a 70 GB scratch file was read through before each run, and Caxton 2.0 reads ahead of a sequential pass. Find keeps the first 2,000,000 matches for highlighting and navigation and counts the rest exactly; the 50 GB find rows report the exact count. Sorting a CSV larger than 256 MB streams the sorted rows to a new document and leaves the original unchanged; both CSV sort rows, at 1 GB and at 10 million rows, are that path.
The same file on a base M1 Air
On September 16, 2026 the rows above were re-run, with Caxton 1.7.3, on the smallest Mac Caxton is likely to meet: a base M1 MacBook Air with 8 GB of memory. Every match count came back identical to the M1 Max: 1,613,344 literal matches, 5,643,688 regex matches, 78,997,610 lines in the negated filter, 2,022,947 CSV rows, 5,000 bookmarks re-anchored exactly. The interactive numbers did not move either: keystroke apply latency p95 1 ms, worst scroll frame 3 ms. What changed is the time each full pass over the 10 GB file takes, and the reason is the machine's memory, not the engine.
Environment
- MacBook Air, Apple M1, 8 GB RAM, 256 GB internal SSD
- macOS 26.6.2, Release build with CAXTON_BENCH, on AC power, machine otherwise idle
- Same corpus files, suites and queries as the rows above; mutating suites run on a fresh APFS clone of the file
- The M1 Max column gives the Pro's current figure from the table above, so its index row includes the read-ahead Caxton 2.0 added and its CSV rows are the 2.0.1 runs
Results, Pro beside Air
| Operation | File | M1 Max, 64 GB | M1 Air, 8 GB |
|---|---|---|---|
| Full background line index | 10 GB log · 80,610,954 lines | 3.7 s | 20.9 s |
| Find, literal (1,613,344 matches) | 10 GB log | 1.0 s | 15.7 s |
| Find, case-insensitive | 10 GB log | 2.1 s | 15.4 s |
| Find, whole word | 10 GB log | 1.0 s | 14.8 s |
| Regex alternation (5,643,688 matches) | 10 GB log | 1.8 s | 15.6 s |
| Filter to matching lines (1.61M of 80.6M) | 10 GB log | 1.0 s | 14.8 s |
| Filter, two-condition AND | 10 GB log | 2.1 s | 15.5 s |
| Filter, two-condition OR (5.6M lines) | 10 GB log | 1.8 s | 14.9 s |
| Filter, negate (78,997,610 lines, exact complement) | 10 GB log | 1.0 s | 14.2 s |
| Scroll, worst frame | 10 GB log | 3 ms | 3 ms |
| Replace All: 1,613,344 replacements | 10 GB log | 1.7 s | 26.8 s, undo 0.4 s |
| Save after scattered edits (atomic rewrite) | 10 GB log | 11.3 s | 35.1 s |
| Single keystroke apply latency, p95 | 10 GB document | ≈ 1 ms | 1 ms |
| CSV parse to working grid (Pro: open, index included) | 1 GB · 2,022,947 rows × 50 cols | 2.3 s | 0.5 s |
| CSV sort by numeric column, streamed to a new document | 1 GB · 2M rows | 4.4 s | 5.2 s |
| Find in a one-line file | 500 MB minified JS · 1 line | 0.5 s | 0.5 s |
| Bookmark 5,000 lines + persist + re-anchor | 500 MB · 5M lines | 0.2 s | 0.3 s |
Why the search rows are slower
A 10 GB file does not fit an 8 GB machine's page cache. The index is one sequential read, so the Air's 20.9 s is close to what the Pro took before 2.0 added read-ahead. Every pass after it has to fault the file back in from the SSD, and on this Air that runs at about 650 MB/s, so a find or filter that takes about a second from memory on the Pro takes about 15 s here. It is the same effect the 50 GB rows show on the 64 GB Pro at about 1 GB/s, arriving five times earlier on this machine. A second pass of the whole 10 GB suite, run 40 minutes later after every other suite had been through the cache, reproduced every row within 1.3 seconds.
The two CSV rows measure slightly different spans. The Air's 0.5 s is the parse alone, after the line index, with the 1.4.1 CSV engine that builds the grid in place; the Pro's 2.3 s runs from opening the file to a working grid, index included. Both sort rows are the same path: a sort on a file past the 256 MB in-place cap streams the sorted rows to a new document with the source unchanged.
These numbers are reproducible on your files.
Download Caxton for Free7-day free trial of Caxton Pro, no card · 7 MB · macOS 13.0+ · notarized