Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/skip-empty-write-metadata.md
Original file line number Diff line number Diff line change
@@ -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.
14 changes: 14 additions & 0 deletions packages/wasm/src/sheet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<CellKey> = self
.spill_ranges
.iter()
Expand Down
39 changes: 24 additions & 15 deletions packages/wasm/src/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
};
Expand Down Expand Up @@ -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
};
Expand Down Expand Up @@ -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
};
Expand All @@ -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
};
Expand Down Expand Up @@ -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);
}
}
Expand All @@ -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 {
Expand All @@ -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;
Expand Down Expand Up @@ -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);
}
}
}
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
}
}
Expand Down Expand Up @@ -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));
Expand Down
Loading