From c13b17b868e73f07dd58b2f0e808a9ae48e1d125 Mon Sep 17 00:00:00 2001 From: Firstyear Date: Thu, 30 May 2024 15:44:13 +1000 Subject: [PATCH] Improve memory use by shrinking arrays during operations (#11) --- Cargo.toml | 2 +- src/v1.rs | 6 +++--- src/v2.rs | 60 +++++++++++++++++++++++++++++++++++++++++++----------- 3 files changed, 52 insertions(+), 16 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 91aaf1d..a852fde 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "idlset" -version = "0.2.4" +version = "0.2.5" authors = ["William Brown "] edition = "2021" diff --git a/src/v1.rs b/src/v1.rs index a0df649..18f0e8e 100644 --- a/src/v1.rs +++ b/src/v1.rs @@ -201,7 +201,7 @@ impl IDLBitRange { let r = self.list.binary_search(&candidate); match r { Ok(idx) => { - let mut existing = self.list.get_mut(idx).unwrap(); + let existing = self.list.get_mut(idx).unwrap(); existing.mask |= candidate.mask; } Err(idx) => { @@ -227,7 +227,7 @@ impl IDLBitRange { // // To do this, we not the candidate, so all other bits remain, // then we perform and &= so that the existing bits survive. - let mut existing = self.list.get_mut(idx).unwrap(); + let existing = self.list.get_mut(idx).unwrap(); existing.mask &= !candidate.mask; @@ -252,7 +252,7 @@ impl IDLBitRange { // Get the highest IDLRange out: if let Some(last) = self.list.last_mut() { - if (*last).range == range { + if last.range == range { // Insert the bit. (*last).push_id(bvalue); return; diff --git a/src/v2.rs b/src/v2.rs index 8fb96da..be8f3c4 100644 --- a/src/v2.rs +++ b/src/v2.rs @@ -88,6 +88,13 @@ enum IDLState { } impl IDLState { + fn shrink_to_fit(&mut self) { + match self { + IDLState::Sparse(svec) => svec.shrink_to_fit(), + IDLState::Compressed(vec) => vec.shrink_to_fit(), + } + } + fn sparse_bitand_fast_path(smol: &[u64], lrg: &[u64]) -> Self { let mut nlist = SmallVec::with_capacity(smol.len()); // We cache the idx inbetween to narrow the bst sizes. @@ -102,6 +109,8 @@ impl IDLState { idx_min = idx; } }); + + nlist.shrink_to_fit(); IDLState::Sparse(nlist) } @@ -123,6 +132,7 @@ impl IDLState { } }); + nlist.shrink_to_fit(); IDLState::Sparse(nlist) } } @@ -204,7 +214,7 @@ impl Default for IDLBitRange { /// Construct a new, empty set. fn default() -> Self { IDLBitRange { - state: IDLState::Sparse(SmallVec::new()), + state: IDLState::Sparse(SmallVec::with_capacity(0)), } } } @@ -337,8 +347,8 @@ impl IDLBitRange { let range: u64 = id - bvalue; if let Some(last) = list.last_mut() { - debug_assert!(id >= (*last).range); - if (*last).range == range { + debug_assert!(id >= last.range); + if last.range == range { // Insert the bit. (*last).push_id(bvalue); return; @@ -369,7 +379,7 @@ impl IDLBitRange { let r = list.binary_search(&candidate); match r { Ok(idx) => { - let mut existing = list.get_mut(idx).unwrap(); + let existing = list.get_mut(idx).unwrap(); existing.mask |= candidate.mask; } Err(idx) => { @@ -405,7 +415,7 @@ impl IDLBitRange { // // To do this, we not the candidate, so all other bits remain, // then we perform and &= so that the existing bits survive. - let mut existing = list.get_mut(idx).unwrap(); + let existing = list.get_mut(idx).unwrap(); existing.mask &= !candidate.mask; @@ -425,7 +435,7 @@ impl IDLBitRange { if self.is_compressed() { return; } - let mut prev_state = IDLState::Compressed(Vec::new()); + let mut prev_state = IDLState::Compressed(Vec::with_capacity(0)); std::mem::swap(&mut prev_state, &mut self.state); match prev_state { IDLState::Sparse(list) => list.into_iter().for_each(|i| unsafe { @@ -444,14 +454,15 @@ impl IDLBitRange { None } else { let mut maybe = IDLBitRange { - state: IDLState::Compressed(Vec::new()), + state: IDLState::Compressed(Vec::with_capacity(0)), }; list.iter().for_each(|id| unsafe { maybe.push_id(*id) }); if maybe.len_ranges() > 0 && (maybe.len() / maybe.len_ranges()) >= AVG_RANGE_COMP_REQ { - let IDLBitRange { state } = maybe; + let IDLBitRange { mut state } = maybe; + state.shrink_to_fit(); Some(state) } else { None @@ -510,6 +521,9 @@ impl IDLBitRange { } } } + + nlist.shrink_to_fit(); + IDLState::Sparse(nlist) }; @@ -543,12 +557,14 @@ impl IDLBitRange { } }); + nlist.shrink_to_fit(); + IDLBitRange { state: IDLState::Sparse(nlist), } } (IDLState::Compressed(list1), IDLState::Compressed(list2)) => { - let mut nlist = Vec::new(); + let mut nlist = Vec::with_capacity(0); let mut liter = list1.iter(); let mut riter = list2.iter(); @@ -580,6 +596,7 @@ impl IDLBitRange { if nlist.is_empty() { IDLBitRange::new() } else { + nlist.shrink_to_fit(); IDLBitRange { state: IDLState::Compressed(nlist), } @@ -639,6 +656,8 @@ impl IDLBitRange { rnext = riter.next(); } + nlist.shrink_to_fit(); + IDLState::Sparse(nlist) }; @@ -675,7 +694,7 @@ impl IDLBitRange { Ok(idx) => { debug_assert!(Ok(idx + idx_min) == list.binary_search(&candidate)); let idx = idx + idx_min; - let mut existing = list.get_mut(idx).unwrap(); + let existing = list.get_mut(idx).unwrap(); existing.mask |= candidate.mask; debug_assert!(idx >= idx_min); idx_min = idx; @@ -692,6 +711,8 @@ impl IDLBitRange { }; }); + list.shrink_to_fit(); + IDLBitRange { state: IDLState::Compressed(list), } @@ -746,6 +767,9 @@ impl IDLBitRange { nlist.push(newrange); rnextrange = riter.next(); } + + nlist.shrink_to_fit(); + IDLBitRange { state: IDLState::Compressed(nlist), } @@ -791,6 +815,8 @@ impl IDLBitRange { lnext = liter.next(); } + nlist.shrink_to_fit(); + IDLBitRange { state: IDLState::Sparse(nlist), } @@ -835,6 +861,8 @@ impl IDLBitRange { } }); + nlist.shrink_to_fit(); + IDLBitRange { state: IDLState::Sparse(nlist), } @@ -853,7 +881,7 @@ impl IDLBitRange { if let Ok(idx) = partition.binary_search(&candidate) { debug_assert!(Ok(idx + idx_min) == nlist.binary_search(&candidate)); let idx = idx + idx_min; - let mut existing = nlist.get_mut(idx).unwrap(); + let existing = nlist.get_mut(idx).unwrap(); existing.mask &= !candidate.mask; if existing.mask == 0 { nlist.remove(idx); @@ -862,6 +890,9 @@ impl IDLBitRange { idx_min = idx; } }); + + nlist.shrink_to_fit(); + IDLBitRange { state: IDLState::Compressed(nlist), } @@ -911,6 +942,8 @@ impl IDLBitRange { lnextrange = liter.next(); } + nlist.shrink_to_fit(); + IDLBitRange { state: IDLState::Compressed(nlist), } @@ -946,7 +979,10 @@ impl FromIterator for IDLBitRange { } }); - new_sparse.maybe_compress(); + if !new_sparse.maybe_compress() { + // If the compression didn't occur, trim the vec anyway. + new_sparse.state.shrink_to_fit(); + } new_sparse } }