gridcraft/crates
lloyd 631a49ec30 Recalc: linear in the number of range formulas
Opening a workbook with a SUM(E:J) on each of 50,000 rows took 9 s
(1.7 s at 25,000, 0.4 s at 12,500), and editing a cell that 50,000
VLOOKUPs read froze the app for 40 s or more. Two things grew with the
number of ranges:

- Evaluating a range trims it to the sheet's used range, and
  Sheet::used_range walks every stored row. It ran once per range read,
  so a full recalc was rows x range formulas. The pass's host now works
  it out once per sheet (the workbook doesn't change during a pass);
  Host::used_range keeps the old behaviour for other hosts.
- The reverse index kept each column's range precedents in a Vec:
  finding a cell's dependents scanned every range in the column, and
  re-entering a formula ran a retain over each of its columns. Pasting
  a column, filling formulas down or sorting was quadratic. Each column
  (and each sheet's wide ranges) now has an interval index: sorted by
  start, with a segment tree of the largest end, so a lookup costs
  O(log n + matches). Removals mark entries dead and insertions wait in
  a small sorted set; Graph::settle merges them after each batch, so
  edits cost O(log n) amortised. Single-cell dependents are a set, so
  removing one is O(1). dependents_of_range asks each column once
  instead of once per cell.

Semantics are unchanged: whole rows and columns, other sheets, names,
tables, spills and volatile functions go through the same paths.

Release CLI, open + full recalc: 12.5k rows 0.39 s -> 0.06 s, 25k 1.83 s
-> 0.14 s, 50k 9.3 s -> 0.28 s. The 50k x 20 bug-hunt workbook: recalc
on open 87 s -> 1.7 s, editing C2 (100k dependents) 51 s -> 1.3 s;
filling 50k SUMs down 17 s -> 0.18 s, sorting 50k rows 9 s -> 0.14 s.

Tests: the interval index against a linear scan (inserts, removals,
revivals, merges) and its query cost in tree nodes; range, wide,
whole-column, whole-row and cross-sheet dependents after edits; and a
scaling test (open, change a column, re-enter every formula) that
allows 8x the time for 4x the rows. Before the fix it took 16x.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 20:51:15 +02:00
..
calc Recalc: linear in the number of range formulas 2026-10-09 20:51:15 +02:00
chart Rename SheetCraft to GridCraft everywhere (crates, apps, bundle ids, packaging, docs) 2026-10-06 01:39:08 -04:00
core Rename SheetCraft to GridCraft everywhere (crates, apps, bundle ids, packaging, docs) 2026-10-06 01:39:08 -04:00
engine Rename SheetCraft to GridCraft everywhere (crates, apps, bundle ids, packaging, docs) 2026-10-06 01:39:08 -04:00
formula Rename SheetCraft to GridCraft everywhere (crates, apps, bundle ids, packaging, docs) 2026-10-06 01:39:08 -04:00
functions Rename SheetCraft to GridCraft everywhere (crates, apps, bundle ids, packaging, docs) 2026-10-06 01:39:08 -04:00
mcp Rename SheetCraft to GridCraft everywhere (crates, apps, bundle ids, packaging, docs) 2026-10-06 01:39:08 -04:00
model Rename SheetCraft to GridCraft everywhere (crates, apps, bundle ids, packaging, docs) 2026-10-06 01:39:08 -04:00
numfmt Rename SheetCraft to GridCraft everywhere (crates, apps, bundle ids, packaging, docs) 2026-10-06 01:39:08 -04:00
pdf Rename SheetCraft to GridCraft everywhere (crates, apps, bundle ids, packaging, docs) 2026-10-06 01:39:08 -04:00
ui-egui About: contributor and model credits compiled in (grab bag + table, self-submitted names) (#2) 2026-10-07 21:16:34 -04:00
xlsx Rename SheetCraft to GridCraft everywhere (crates, apps, bundle ids, packaging, docs) 2026-10-06 01:39:08 -04:00