cranelift-entity: more efficient EntitySet implementation (#5978)

* Use usize intead of u8

* Rename 'byte's to appropriate words
This commit is contained in:
uint256_t
2023-03-14 03:43:34 +09:00
committed by GitHub
parent ad0bce3a36
commit b50cf9bb57

View File

@@ -5,6 +5,9 @@ use crate::EntityRef;
use alloc::vec::Vec;
use core::marker::PhantomData;
// How many bits are used to represent a single element in `EntitySet`.
const BITS: usize = core::mem::size_of::<usize>() * 8;
/// A set of `K` for densely indexed entity references.
///
/// The `EntitySet` data structure uses the dense index space to implement a set with a bitvector.
@@ -14,7 +17,7 @@ pub struct EntitySet<K>
where
K: EntityRef,
{
elems: Vec<u8>,
elems: Vec<usize>,
len: usize,
unused: PhantomData<K>,
}
@@ -42,7 +45,7 @@ where
/// Creates a new empty set with the specified capacity.
pub fn with_capacity(capacity: usize) -> Self {
Self {
elems: Vec::with_capacity((capacity + 7) / 8),
elems: Vec::with_capacity((capacity + (BITS - 1)) / BITS),
..Self::new()
}
}
@@ -51,7 +54,7 @@ where
pub fn contains(&self, k: K) -> bool {
let index = k.index();
if index < self.len {
(self.elems[index / 8] & (1 << (index % 8))) != 0
(self.elems[index / BITS] & (1 << (index % BITS))) != 0
} else {
false
}
@@ -71,11 +74,11 @@ where
/// `clear`ed or created with `new`.
pub fn cardinality(&self) -> usize {
let mut n: usize = 0;
for byte_ix in 0..self.len / 8 {
n += self.elems[byte_ix].count_ones() as usize;
for idx in 0..self.len / BITS {
n += self.elems[idx].count_ones() as usize;
}
for bit_ix in (self.len / 8) * 8..self.len {
if (self.elems[bit_ix / 8] & (1 << (bit_ix % 8))) != 0 {
for bit_ix in (self.len / BITS) * BITS..self.len {
if (self.elems[bit_ix / BITS] & (1 << (bit_ix % BITS))) != 0 {
n += 1;
}
}
@@ -95,7 +98,7 @@ where
/// Resize the set to have `n` entries by adding default entries as needed.
pub fn resize(&mut self, n: usize) {
self.elems.resize((n + 7) / 8, 0);
self.elems.resize((n + (BITS - 1)) / BITS, 0);
self.len = n
}
@@ -106,7 +109,7 @@ where
self.resize(index + 1)
}
let result = !self.contains(k);
self.elems[index / 8] |= 1 << (index % 8);
self.elems[index / BITS] |= 1 << (index % BITS);
result
}
@@ -118,7 +121,7 @@ where
// Clear the last known entity in the list.
let last_index = self.len - 1;
self.elems[last_index / 8] &= !(1 << (last_index % 8));
self.elems[last_index / BITS] &= !(1 << (last_index % BITS));
// Set the length to the next last stored entity or zero if we pop'ed
// the last entity.
@@ -127,12 +130,14 @@ where
.iter()
.enumerate()
.rev()
.find(|(_, &byte)| byte != 0)
// Map `i` from byte index to bit level index.
// `(i + 1) * 8` = Last bit in byte.
// `last - byte.leading_zeros()` = last set bit in byte.
.find(|(_, &elem)| elem != 0)
// Map `i` from `elem` index to bit level index.
// `(i + 1) * BITS` = Last bit in `elem`.
// `last - elem.leading_zeros()` = last set bit in `elem`.
// `as usize` won't ever truncate as the potential range is `0..=8`.
.map_or(0, |(i, byte)| ((i + 1) * 8) - byte.leading_zeros() as usize);
.map_or(0, |(i, elem)| {
((i + 1) * BITS) - elem.leading_zeros() as usize
});
Some(K::new(last_index))
}