collective

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

diff --git a/crates/calc/src/recalc.rs b/crates/calc/src/recalc.rs--- a/crates/calc/src/recalc.rs+++ b/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 });