BitVec: remove zero words to avoid expanding when unnecessary.

This commit is contained in:
Chris Fallin
2021-08-12 14:40:18 -07:00
parent 82b7e6ba7b
commit eaf8647fdf

View File

@@ -43,15 +43,33 @@ impl AdaptiveMap {
fn expand(&mut self) { fn expand(&mut self) {
match self { match self {
&mut Self::Small { &mut Self::Small {
len, ref mut len,
ref keys, ref mut keys,
ref values, ref mut values,
} => { } => {
let mut map = FxHashMap::default(); // Note: we *may* remain as `Small` if there are any
for i in 0..len { // zero elements. Try removing them first, before we
map.insert(keys[i as usize], values[i as usize]); // commit to a memory allocation.
if values.iter().any(|v| *v == 0) {
let mut out = 0;
for i in 0..(*len as usize) {
if values[i] == 0 {
continue;
}
if out < i {
keys[out] = keys[i];
values[out] = values[i];
}
out += 1;
}
*len = out as u32;
} else {
let mut map = FxHashMap::default();
for i in 0..(*len as usize) {
map.insert(keys[i], values[i]);
}
*self = Self::Large(map);
} }
*self = Self::Large(map);
} }
_ => {} _ => {}
} }
@@ -256,6 +274,15 @@ impl BitVec {
set_bits(bits).map(move |i| BITS_PER_WORD * word_idx + i) set_bits(bits).map(move |i| BITS_PER_WORD * word_idx + i)
}) })
} }
/// Is the adaptive data structure in "small" mode? This is meant
/// for testing assertions only.
pub(crate) fn is_small(&self) -> bool {
match &self.elems {
&AdaptiveMap::Small { .. } => true,
_ => false,
}
}
} }
fn set_bits(bits: u64) -> impl Iterator<Item = usize> { fn set_bits(bits: u64) -> impl Iterator<Item = usize> {
@@ -309,4 +336,18 @@ mod test {
assert_eq!(sum, checksum); assert_eq!(sum, checksum);
} }
#[test]
fn test_expand_remove_zero_elems() {
let mut vec = BitVec::new();
// Set 12 different words (this is the max small-mode size).
for i in 0..12 {
vec.set(64 * i, true);
}
// Now clear a bit, and set a bit in a different word. We
// should still be in small mode.
vec.set(64 * 5, false);
vec.set(64 * 100, true);
assert!(vec.is_small());
}
} }