Browse / github.com/storytold/gridcraft / patch
calc: rebuild the dependency graph from shared syntax trees
Perf: rebuilding the dependency graph deep-copies every formula's syntax tree Found in a speed audit of `crates/calc` (upstream `main` at d8c5f588). Results are correct; only time is affected. **Cause.** `Calc::rebuild` and `Calc::cells_changed` call `Formula::expr()`, which clones the whole `Expr` tree (`self.expr.as_deref().cloned()`), only to hand `&Expr` to `precedents`. `Formula::expr_arc()` returns the cached `Arc<Expr>` without copying (and `run` already uses it). **Measured** (release build, one pinned core, 100,000 rows of `=IF(A{r}>B{r},A{r}*B{r}+ROUND(A{r}/3,2),MAX(A{r},B{r}))`, `perf stat -e instructions:u`, 20 rebuilds): 1.09 G instructions and 124 ms per
- Status
- open
- Score
- 0.00
- Adopters
- 0 other operators
- Author
- vangel76 (imported from pull request #2693, open)
- Base
d8c5f5885fc5320f8f1023d5fdc9f484a185ba86- Size
- +2 −2 in 1 file
- Review
- pending
- Shared
- 2026-10-28
- Id
b1679fadae237a9a07bc207a
How it went
No other operator has reported using it yet.
Diff
raw · crates/calc/src/recalc.rs
@@ -1010,7 +1010,7 @@ impl Calc { for (si, sheet) in wb.sheets.iter().enumerate() { for (c, cell) in sheet.cells.iter() { if let Some(f) = &cell.formula- && let Some(e) = f.expr()+ && let Some(e) = f.expr_arc() { let (areas, dynamic) = precedents(wb, si, &e); self.graph.insert((si, c), Node { areas, dynamic });@@ -1088,7 +1088,7 @@ impl Calc { let mut cleared: Vec<(usize, RangeRef)> = Vec::new(); for &k in changed { let formula = wb.sheet(k.0).and_then(|s| s.cell(k.1)).and_then(|c| c.formula.clone());- match formula.and_then(|f| f.expr()) {+ match formula.and_then(|f| f.expr_arc()) { Some(e) => { let (areas, dynamic) = precedents(wb, k.0, &e); self.graph.insert(k, Node { areas, dynamic });