My program has a requirement for sets of integers that are usually small ( they are column numbers, and it would be unusual to have a table with more than 63 columns ). I was thinking a specialised set might avoid some heap allocations, and be faster for the usual case. As well as the elements being small integers, typically the set will only contain one or two elements, the main aim is for minimal overhead when creating the set ( they are used when selecting a database index ).
So I just made this SmallSet
struct ( code below ). I was just wondering whether to make the overflow an Option<BTreeSet>
, so the BTreeSet doesn't have to be initialised if there is no overflow ( the expected common case ). Anyway, is my thinking correct here, and could SmallSet be expected to outperform a plain BTreeSet in this situation? How might the struct be improved?
/// Set of usize, optimised for elements < 64.
pub struct SmallSet {
bitset: u64,
overflow: BTreeSet<usize>,
}
impl SmallSet {
pub fn new() -> Self {
Self {
bitset: 0,
overflow: BTreeSet::new(),
}
}
pub fn insert(&mut self, x: usize) {
if x < 64 {
self.bitset |= 1 << x;
} else {
self.overflow.insert(x);
}
}
pub fn contains(&self, x: usize) -> bool {
if x < 64 {
self.bitset & (1 << x) != 0
} else {
self.overflow.contains(&x)
}
}
pub fn remove(&mut self, x: usize) -> bool {
if x < 64 {
let bit: u64 = 1 << x;
let result = self.bitset & bit != 0;
self.bitset &= u64::MAX - bit;
result
} else {
self.overflow.remove(&x)
}
}
}
impl Default for SmallSet {
fn default() -> Self {
Self::new()
}
}