From 2c26d4cc4e010906d10c3b16ea7348a137ec7350 Mon Sep 17 00:00:00 2001 From: chh-ay Date: Tue, 6 Oct 2026 15:18:00 +0700 Subject: [PATCH] perf(wasm): skip empty metadata probes on cell writes Centralize empty-map formula removal in SheetData and skip spill metadata probes when no spill exists. Nine alternating comparison rounds improve one-million-row mixed-column ingestion by 35.2%; five better-warmed rounds keep the centralized helper within 2.67% of the original optimization, with identical output and 718 bytes of justified default wasm growth. --- .changeset/skip-empty-write-metadata.md | 6 ++++ packages/wasm/src/sheet.rs | 14 +++++++++ packages/wasm/src/store.rs | 39 +++++++++++++++---------- 3 files changed, 44 insertions(+), 15 deletions(-) create mode 100644 .changeset/skip-empty-write-metadata.md diff --git a/.changeset/skip-empty-write-metadata.md b/.changeset/skip-empty-write-metadata.md new file mode 100644 index 00000000..4f7fc6ce --- /dev/null +++ b/.changeset/skip-empty-write-metadata.md @@ -0,0 +1,6 @@ +--- +"@sheetwrite/wasm": patch +"@sheetwrite/formulas": patch +--- + +Skip spill and formula metadata lookups when the metadata is empty. This reduces the time to load one million rows by 35% in the measured mixed-column workload. The default WebAssembly file grows by 718 bytes (0.09%) for this improvement. diff --git a/packages/wasm/src/sheet.rs b/packages/wasm/src/sheet.rs index 45fefb6a..0e0c5f7d 100644 --- a/packages/wasm/src/sheet.rs +++ b/packages/wasm/src/sheet.rs @@ -1290,9 +1290,23 @@ impl SheetData { changed } + /// Remove a cell's formula without hashing when the sheet has none. + #[inline] + pub(crate) fn remove_formula(&mut self, key: CellKey) -> bool { + !self.formulas.is_empty() && self.formulas.remove(&key).is_some() + } + /// Detach a direct write from any spill and invalidate every attempted spill /// whose destination includes this cell. pub(crate) fn prepare_cell_write(&mut self, cell: CellKey) { + // No spill metadata means nothing to detach, invalidate or enqueue; the + // hash probes below would only pay for empty-map lookups on every write. + if self.spill_ranges.is_empty() + && self.spill_owners.is_empty() + && self.spill_errors.is_empty() + { + return; + } let affected: Vec = self .spill_ranges .iter() diff --git a/packages/wasm/src/store.rs b/packages/wasm/src/store.rs index 7c04bae7..2b763d85 100644 --- a/packages/wasm/src/store.rs +++ b/packages/wasm/src/store.rs @@ -771,7 +771,7 @@ impl CellStore { return; } - let removed_formula = s.formulas.remove(&key).is_some(); + let removed_formula = s.remove_formula(key); s.dirty_cells.insert(key); removed_formula }; @@ -803,7 +803,7 @@ impl CellStore { return; } - let removed_formula = s.formulas.remove(&key).is_some(); + let removed_formula = s.remove_formula(key); s.dirty_cells.insert(key); removed_formula }; @@ -934,7 +934,7 @@ impl CellStore { ) { return; } - let removed_formula = s.formulas.remove(&key).is_some(); + let removed_formula = s.remove_formula(key); s.dirty_cells.insert(key); removed_formula }; @@ -959,7 +959,7 @@ impl CellStore { return; } - let removed_formula = s.formulas.remove(&key).is_some(); + let removed_formula = s.remove_formula(key); s.dirty_cells.insert(key); removed_formula }; @@ -1088,8 +1088,13 @@ impl CellStore { } let mut string_ids = vec![NO_STRING; offsets.len()]; + // Sourceless blocks (the common interactive edit) own no source offsets, + // so the containment probe can be skipped for every edited cell. + let has_sources = !source_offsets.is_empty(); for (index, text) in texts.iter().enumerate() { - if !source_offsets.contains(&offsets[index]) && kinds[index] == KIND_STRING { + if kinds[index] == KIND_STRING + && !(has_sources && source_offsets.contains(&offsets[index])) + { string_ids[index] = self.intern(text); } } @@ -1104,7 +1109,11 @@ impl CellStore { let Some(key) = cell_key(row, col) else { return BLOCK_SOURCE_INVALID; }; - let source = prepared_sources.remove(&offset); + let source = if prepared_sources.is_empty() { + None + } else { + prepared_sources.remove(&offset) + }; let (kind, payload) = if source.is_some() { (KIND_FORMULA, 0) } else { @@ -1129,7 +1138,7 @@ impl CellStore { if !hydrating { data.dirty_cells.insert(key); } - changed_sources |= data.formulas.remove(&key).is_some(); + changed_sources |= data.remove_formula(key); if let Some(entry) = source { data.formulas.insert(key, entry); changed_sources = true; @@ -1196,7 +1205,7 @@ impl CellStore { } if contents { if let Some(key) = cell_key(row, col) { - removed_formula |= s.formulas.remove(&key).is_some(); + removed_formula |= s.remove_formula(key); } } } @@ -1723,7 +1732,7 @@ impl CellStore { if !s.hydrate_cell(row, col, KIND_NUMBER, encode_num(value), style) { continue; } - removed_formula |= s.formulas.remove(&key).is_some(); + removed_formula |= s.remove_formula(key); wrote = true; } if wrote { @@ -1781,7 +1790,7 @@ impl CellStore { if !s.hydrate_cell(row, col, KIND_STRING, encode_str_id(id), style) { continue; } - removed_formula |= s.formulas.remove(&key).is_some(); + removed_formula |= s.remove_formula(key); wrote = true; } if wrote { @@ -1832,7 +1841,7 @@ impl CellStore { ) { return; } - removed_formula |= s.formulas.remove(&key).is_some(); + removed_formula |= s.remove_formula(key); } if limit > 0 { s.all_dirty = true; @@ -1885,7 +1894,7 @@ impl CellStore { ) { return; } - removed_formula |= s.formulas.remove(&key).is_some(); + removed_formula |= s.remove_formula(key); } if limit > 0 { self.sheets[sheet].all_dirty = true; @@ -1942,7 +1951,7 @@ impl CellStore { ) { return; } - removed_formula |= s.formulas.remove(&key).is_some(); + removed_formula |= s.remove_formula(key); } if limit > 0 { self.sheets[sheet].all_dirty = true; @@ -2811,7 +2820,7 @@ impl CellStore { return BLOCK_RESOURCE_LIMIT; } if let Some(key) = cell_key(row, col) { - s.formulas.remove(&key); + s.remove_formula(key); } } } @@ -3071,7 +3080,7 @@ impl CellStore { }) .collect(); for key in invalid { - sheet.formulas.remove(&key); + sheet.remove_formula(key); let (row, col) = (key.0 as usize, key.1 as usize); if sheet.contains_cell(row, col) { let style = sheet.style_at(sheet.idx(row, col));