fix(incremental): meter the whole-cache recompute and correct the step 5 notes (improvise-shbc)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
This commit is contained in:
committed by
Edward Langley
parent
effca3f663
commit
4abeb7829d
@@ -221,10 +221,11 @@ taken where clause 4 and clause 7 leave a choice; evidence is in
|
|||||||
|
|
||||||
## Step 5: formulas (improvise-shbc)
|
## Step 5: formulas (improvise-shbc)
|
||||||
|
|
||||||
`Incremental` no longer calls `recompute_formulas`. It keeps the formula cache (`Model::formula_cell`,
|
`Incremental` calls `recompute_formulas` only for the documents item 2 calls wide. Otherwise it keeps
|
||||||
`set_formula_cell`, `evaluate_formula_cell`, the three small additions to `improvise-core`) and decides
|
the formula cache itself (`Model::formula_cell`, `set_formula_cell`, `clear_formula_cells` and the
|
||||||
which cells of it to evaluate again; the evaluator that computes each value is core's own. The ADR
|
now-public `evaluate_formula_cell`, the small additions to `improvise-core`) and decides which cells of
|
||||||
(clauses 5 and 6) is followed in outline, and the places it is not are the reason for this section.
|
it to evaluate again; the evaluator that computes each value is core's own. The ADR (clauses 5 and 6)
|
||||||
|
is followed in outline, and the places it is not are the reason for this section.
|
||||||
|
|
||||||
1. **A stem is the unit, because eager's cache is per stem.** `Stems` (`formulas.rs`) holds, per
|
1. **A stem is the unit, because eager's cache is per stem.** `Stems` (`formulas.rs`) holds, per
|
||||||
target category, how many stored cells give each stem (a cell minus that category and the
|
target category, how many stored cells give each stem (a cell minus that category and the
|
||||||
|
|||||||
@@ -453,7 +453,7 @@ One shared text is that text, two are `mixed`, a number among text is `mixed`, a
|
|||||||
|
|
||||||
**How the faults were injected.** Twenty-seven faults went into `src/formulas.rs` and
|
**How the faults were injected.** Twenty-seven faults went into `src/formulas.rs` and
|
||||||
`src/incremental.rs`, one at a time, each run against the whole crate and reverted before the next.
|
`src/incremental.rs`, one at a time, each run against the whole crate and reverted before the next.
|
||||||
A first pass left eight alive; five were a gap in the tests and were closed by the tests below, and
|
A first pass left eight alive; six were a gap in the tests and were closed by the tests below, and
|
||||||
the remaining two (`born-stems-not-evaluated`, `target-category-not-read`) cannot change an answer.
|
the remaining two (`born-stems-not-evaluated`, `target-category-not-read`) cannot change an answer.
|
||||||
What each fault was and the test that names it:
|
What each fault was and the test that names it:
|
||||||
- a cell that gives a stem is not enough to evaluate it / no reader is dirtied:
|
- a cell that gives a stem is not enough to evaluate it / no reader is dirtied:
|
||||||
@@ -596,3 +596,9 @@ sixteenth pass to see nothing move.
|
|||||||
### formulas::tests::what_a_formula_reads_includes_what_the_formulas_it_names_read
|
### formulas::tests::what_a_formula_reads_includes_what_the_formulas_it_names_read
|
||||||
|
|
||||||
`Margin` names `Profit`, which names `Cost`; the reads of `Margin` include `Cost`.
|
`Margin` names `Profit`, which names `Cost`; the reads of `Margin` include `Cost`.
|
||||||
|
|
||||||
|
### the_whole_cache_recompute_of_a_wide_document_is_metered
|
||||||
|
|
||||||
|
A document with formulas in two target categories falls back to `recompute_formulas`; the fallback was
|
||||||
|
doing the work with the meter at zero (Copilot on PR #351). It now counts `Meter::recomputes`, and a
|
||||||
|
document with one target category counts none.
|
||||||
|
|||||||
@@ -225,6 +225,7 @@ impl Incremental {
|
|||||||
/// cells, recomputes every formula cell as eager does.
|
/// cells, recomputes every formula cell as eager does.
|
||||||
fn evaluate(&mut self, stems: impl IntoIterator<Item = CellKey>) {
|
fn evaluate(&mut self, stems: impl IntoIterator<Item = CellKey>) {
|
||||||
if self.stems.is_wide() {
|
if self.stems.is_wide() {
|
||||||
|
self.work.recomputes += 1;
|
||||||
return self.model.recompute_formulas(&self.none_cats);
|
return self.model.recompute_formulas(&self.none_cats);
|
||||||
}
|
}
|
||||||
for stem in stems {
|
for stem in stems {
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ pub struct Meter {
|
|||||||
pub formula_cells: u64,
|
pub formula_cells: u64,
|
||||||
/// Times the stems were rebuilt from the store, as a formula edit does.
|
/// Times the stems were rebuilt from the store, as a formula edit does.
|
||||||
pub rebuilds: u64,
|
pub rebuilds: u64,
|
||||||
|
/// Whole-cache recomputes by the fallback for documents whose stems read each other. One is
|
||||||
|
/// every stem's formulas for every pass, so it is counted as an event, not as cells.
|
||||||
|
pub recomputes: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Meter {
|
impl Meter {
|
||||||
@@ -36,6 +39,7 @@ impl Meter {
|
|||||||
stems: self.stems + o.stems,
|
stems: self.stems + o.stems,
|
||||||
formula_cells: self.formula_cells + o.formula_cells,
|
formula_cells: self.formula_cells + o.formula_cells,
|
||||||
rebuilds: self.rebuilds + o.rebuilds,
|
rebuilds: self.rebuilds + o.rebuilds,
|
||||||
|
recomputes: self.recomputes + o.recomputes,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -191,6 +191,33 @@ fn two_target_categories_read_each_others_stems_whatever_the_order_they_are_adde
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn the_whole_cache_recompute_of_a_wide_document_is_metered() {
|
||||||
|
let wb = formula_workbook();
|
||||||
|
let mut backend = Incremental::new(wb.model.clone(), none_of(&wb));
|
||||||
|
let edit = bump("East", "A", "Revenue", 3.0);
|
||||||
|
backend.apply(&edit);
|
||||||
|
assert_eq!(
|
||||||
|
backend.meter().recomputes,
|
||||||
|
0,
|
||||||
|
"one target category: by stem"
|
||||||
|
);
|
||||||
|
backend.apply(&formula_edit(14));
|
||||||
|
let before = backend.meter();
|
||||||
|
backend.apply(&edit);
|
||||||
|
backend.apply(&bump("East", "A", "Revenue", 4.0));
|
||||||
|
let spent = backend.meter();
|
||||||
|
assert_eq!(
|
||||||
|
spent.recomputes - before.recomputes,
|
||||||
|
2,
|
||||||
|
"Region and _Measure targets: wide"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
spent.stems, before.stems,
|
||||||
|
"and no stem is evaluated on its own"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn a_name_in_another_category_reads_another_stems_cell() {
|
fn a_name_in_another_category_reads_another_stems_cell() {
|
||||||
let mut wb = formula_workbook();
|
let mut wb = formula_workbook();
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ fn since(before: Meter, after: Meter) -> Meter {
|
|||||||
stems: after.stems - before.stems,
|
stems: after.stems - before.stems,
|
||||||
formula_cells: after.formula_cells - before.formula_cells,
|
formula_cells: after.formula_cells - before.formula_cells,
|
||||||
rebuilds: after.rebuilds - before.rebuilds,
|
rebuilds: after.rebuilds - before.rebuilds,
|
||||||
|
recomputes: after.recomputes - before.recomputes,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user