Struct hi_sparse_bitset::SmallBitSet
source · pub struct SmallBitSet<Conf: SmallConfig>(/* private fields */);
Expand description
Same as BitSet, but sparsely populated hierarchy blocks 9 times smaller.
Which means that sparse sets virtually do not have indirection-related memory overhead!
§Memory
For _128bit each Level1 block consumes just 32 bytes, if less than 8 data blocks pointed from. 256+32 bytes otherwise.
§Performance
All operations still have O(1) complexity. But in terms of raw performance, (due to additional layer of indirection) this is x1.5 - x2 slower than BitSet. Which is still very fast.
§Implementation details
Level0 128bit SIMD
[u8;128]
┌ 128bit SIMD ┐ ╭─ ── ── ── ── ── ── ── ── ── ╮
│ ╭──────────────╮ │ Act as SBO: │
Level1 Vec│ │ [u16;7] │ │ │- Inline SparseBitMap, for
│ │ ──────────── │◁┼────┤small size. │
│ │Box<[u16;128]>│ │ - Boxed full-size array with │
└ ╰──────────────╯ ┘ │direct access for a big one.
┌ ┐ ╰ ── ── ── ── ── ── ── ── ── ─╯
Data Vec│ 128bit SIMD │
└ ┘
SparseBitMap - bit_block
acts as a sparse array:
0 1 2 3 ◁═ popcnt before element
(dense_array indices)
bit_block 0 1 1 0 0 0 1 1 0 0 ...
└───┬─┬───────┬─┬─────────┘
┌──┘┌┘ ┌─────┘ │
│ │ │ ┌────┘
▼ ▼ ▼ ▼
dense_array 1, 32, 4, 5 len = bit_block popcnt
As you can see, SparseBitMap has fast O(1) per-index access.
Insert and remove - are O(N) operations, because
dense_array
must keep its element order.
Why not just use SparseBitMap for everything?
Big-sized dense_array
should be placed somewhere outside of LevelBlock struct
to be memory efficient (e.g., in dynamic-sized heap). Hence - it
will be accessed through the pointer (which is yet another layer of indirection).
Due to this, and the O(N) insert/remove - SparseBitMap is used only for small-sized arrays.
We aim for a 16-element array - as with this size, data blocks pointed
from level1 block will have the same size in total, as a full-sized level1 block
indirection array.
Implementations§
source§impl<Conf> SmallBitSet<Conf>where
Conf: SmallConfig,
impl<Conf> SmallBitSet<Conf>where
Conf: SmallConfig,
pub fn new() -> Self
sourcepub const fn max_capacity() -> usize
pub const fn max_capacity() -> usize
Max usize, bitset with this Conf
can hold.
sourcepub fn remove(&mut self, index: usize) -> bool
pub fn remove(&mut self, index: usize) -> bool
Returns false if index is invalid/not in bitset.
sourcepub unsafe fn remove_unchecked(&mut self, index: usize)
pub unsafe fn remove_unchecked(&mut self, index: usize)
§Safety
index
MUST exists in HiSparseBitset!
source§impl<Conf> SmallBitSet<Conf>where
Conf: SmallConfig,
impl<Conf> SmallBitSet<Conf>where
Conf: SmallConfig,
Trait Implementations§
source§impl<Conf, Rhs> BitAnd<Rhs> for &SmallBitSet<Conf>
impl<Conf, Rhs> BitAnd<Rhs> for &SmallBitSet<Conf>
source§impl<Conf, Rhs> BitOr<Rhs> for &SmallBitSet<Conf>
impl<Conf, Rhs> BitOr<Rhs> for &SmallBitSet<Conf>
source§impl<Conf: SmallConfig> BitSetBase for SmallBitSet<Conf>
impl<Conf: SmallConfig> BitSetBase for SmallBitSet<Conf>
source§impl<Conf> BitSetInterface for &SmallBitSet<Conf>where
Conf: SmallConfig,
impl<Conf> BitSetInterface for &SmallBitSet<Conf>where
Conf: SmallConfig,
fn block_iter(&self) -> CachingBlockIter<&Self> ⓘ
fn iter(&self) -> CachingIndexIter<&Self> ⓘ
fn into_block_iter(self) -> CachingBlockIter<Self> ⓘ
fn contains(&self, index: usize) -> bool
source§fn is_empty(&self) -> bool
fn is_empty(&self) -> bool
source§impl<Conf, Rhs> BitXor<Rhs> for &SmallBitSet<Conf>
impl<Conf, Rhs> BitXor<Rhs> for &SmallBitSet<Conf>
source§impl<Conf> Clone for SmallBitSet<Conf>where
Conf: SmallConfig,
impl<Conf> Clone for SmallBitSet<Conf>where
Conf: SmallConfig,
source§impl<Conf> Debug for SmallBitSet<Conf>where
Conf: SmallConfig,
impl<Conf> Debug for SmallBitSet<Conf>where
Conf: SmallConfig,
source§impl<Conf> Default for SmallBitSet<Conf>where
Conf: SmallConfig,
impl<Conf> Default for SmallBitSet<Conf>where
Conf: SmallConfig,
source§impl<Conf, const N: usize> From<[usize; N]> for SmallBitSet<Conf>where
Conf: SmallConfig,
impl<Conf, const N: usize> From<[usize; N]> for SmallBitSet<Conf>where
Conf: SmallConfig,
source§impl<Conf> FromIterator<usize> for SmallBitSet<Conf>where
Conf: SmallConfig,
impl<Conf> FromIterator<usize> for SmallBitSet<Conf>where
Conf: SmallConfig,
source§impl<Conf> IntoIterator for &SmallBitSet<Conf>where
Conf: SmallConfig,
impl<Conf> IntoIterator for &SmallBitSet<Conf>where
Conf: SmallConfig,
source§impl<Conf> LevelMasks for SmallBitSet<Conf>where
Conf: SmallConfig,
impl<Conf> LevelMasks for SmallBitSet<Conf>where
Conf: SmallConfig,
fn level0_mask(&self) -> <Self::Conf as Config>::Level0BitBlock
source§unsafe fn level1_mask(
&self,
level0_index: usize
) -> <Self::Conf as Config>::Level1BitBlock
unsafe fn level1_mask( &self, level0_index: usize ) -> <Self::Conf as Config>::Level1BitBlock
source§impl<Conf> LevelMasksIterExt for SmallBitSet<Conf>where
Conf: SmallConfig,
impl<Conf> LevelMasksIterExt for SmallBitSet<Conf>where
Conf: SmallConfig,
§type IterState = <RawBitSet<Conf, Block<<Conf as Config>::Level0BitBlock, <Conf as Config>::Level0BlockIndices>, CompactBlock<<Conf as Config>::Level1BitBlock, <Conf as SmallConfig>::Level1MaskU64Populations, <Conf as Config>::Level1BlockIndices, <Conf as SmallConfig>::Level1SmallBlockIndices>, Block<<Conf as Config>::DataBitBlock, [usize; 0]>> as LevelMasksIterExt>::IterState
type IterState = <RawBitSet<Conf, Block<<Conf as Config>::Level0BitBlock, <Conf as Config>::Level0BlockIndices>, CompactBlock<<Conf as Config>::Level1BitBlock, <Conf as SmallConfig>::Level1MaskU64Populations, <Conf as Config>::Level1BlockIndices, <Conf as SmallConfig>::Level1SmallBlockIndices>, Block<<Conf as Config>::DataBitBlock, [usize; 0]>> as LevelMasksIterExt>::IterState
§type Level1BlockData = <RawBitSet<Conf, Block<<Conf as Config>::Level0BitBlock, <Conf as Config>::Level0BlockIndices>, CompactBlock<<Conf as Config>::Level1BitBlock, <Conf as SmallConfig>::Level1MaskU64Populations, <Conf as Config>::Level1BlockIndices, <Conf as SmallConfig>::Level1SmallBlockIndices>, Block<<Conf as Config>::DataBitBlock, [usize; 0]>> as LevelMasksIterExt>::Level1BlockData
type Level1BlockData = <RawBitSet<Conf, Block<<Conf as Config>::Level0BitBlock, <Conf as Config>::Level0BlockIndices>, CompactBlock<<Conf as Config>::Level1BitBlock, <Conf as SmallConfig>::Level1MaskU64Populations, <Conf as Config>::Level1BlockIndices, <Conf as SmallConfig>::Level1SmallBlockIndices>, Block<<Conf as Config>::DataBitBlock, [usize; 0]>> as LevelMasksIterExt>::Level1BlockData
fn make_iter_state(&self) -> Self::IterState
source§unsafe fn drop_iter_state(&self, state: &mut ManuallyDrop<Self::IterState>)
unsafe fn drop_iter_state(&self, state: &mut ManuallyDrop<Self::IterState>)
source§unsafe fn init_level1_block_data(
&self,
state: &mut Self::IterState,
level1_block_data: &mut MaybeUninit<Self::Level1BlockData>,
level0_index: usize
) -> (<Self::Conf as Config>::Level1BitBlock, bool)
unsafe fn init_level1_block_data( &self, state: &mut Self::IterState, level1_block_data: &mut MaybeUninit<Self::Level1BlockData>, level0_index: usize ) -> (<Self::Conf as Config>::Level1BitBlock, bool)
level1_block_data
and return (Level1Mask, is_not_empty). Read more