Avoid allocating a vector of elements when hashing frozenset (#5408)

Adds a `try_fold_keys` method to Dict which allows performing common
operations on all elements without needing to create a Vec first.
This commit is contained in:
Ankit Goel
2024-09-21 15:18:26 +01:00
committed by GitHub
parent a8964f4108
commit 8cff0ed6c2
2 changed files with 15 additions and 4 deletions

View File

@@ -436,12 +436,12 @@ impl PySetInner {
((h ^ 89869747) ^ (h.wrapping_shl(16))).wrapping_mul(3644798167)
}
// Factor in the number of active entries
let mut hash: u64 = (self.elements().len() as u64 + 1).wrapping_mul(1927868237);
let mut hash: u64 = (self.len() as u64 + 1).wrapping_mul(1927868237);
// Xor-in shuffled bits from every entry's hash field because xor is
// commutative and a frozenset hash should be independent of order.
for element in self.elements().iter() {
hash ^= _shuffle_bits(element.hash(vm)? as u64);
}
hash = self.content.try_fold_keys(hash, |h, element| {
Ok(h ^ _shuffle_bits(element.hash(vm)? as u64))
})?;
// Disperse patterns arising in nested frozensets
hash ^= (hash >> 11) ^ (hash >> 25);
hash = hash.wrapping_mul(69069).wrapping_add(907133923);

View File

@@ -530,6 +530,17 @@ impl<T: Clone> Dict<T> {
.collect()
}
pub fn try_fold_keys<Acc, Fold>(&self, init: Acc, f: Fold) -> PyResult<Acc>
where
Fold: FnMut(Acc, &PyObject) -> PyResult<Acc>,
{
self.read()
.entries
.iter()
.filter_map(|v| v.as_ref().map(|v| v.key.as_object()))
.try_fold(init, f)
}
/// Lookup the index for the given key.
#[cfg_attr(feature = "flame-it", flame("Dict"))]
fn lookup<K: DictKey + ?Sized>(