Skip to content

Commit 4daa62a

Browse files
committed
Auto merge of #25230 - rayglover:patch-bitset, r=Gankro
Some modest running-time improvements to `std::collections::BitSet` on bit-sets of varying set-membership densities. This is work originally from [here](https://github.com/rayglover/alt_collections). (Benchmarks copied below) ``` std::collections::BitSet / alt_collections::BitSet copy_dense ... 3.08x copy_sparse ... 4.22x count_dense ... 11.01x count_sparse ... 8.11x from_bytes ... 1.47x intersect_dense ... 6.54x intersect_sparse ... 4.37x union_dense ... 5.53x union_sparse ... 5.60x ``` The exception is `from_bytes`, which I've left unaltered since the optimization is rather obscure. Compiling with the cpu feature `popcnt` gave a further ~10% improvement on my machine, but this wasn't factored in to the benchmarks above. Similar improvements could be made to `BitVec`, although that would probably require more substantial changes. criticism welcome!
2 parents 2dd5ad0 + 307fab1 commit 4daa62a

File tree

1 file changed

+83
-74
lines changed

1 file changed

+83
-74
lines changed

src/libcollections/bit.rs

+83-74
Original file line numberDiff line numberDiff line change
@@ -1555,7 +1555,7 @@ impl BitSet {
15551555
#[inline]
15561556
#[stable(feature = "rust1", since = "1.0.0")]
15571557
pub fn iter(&self) -> bit_set::Iter {
1558-
SetIter {set: self, next_idx: 0}
1558+
SetIter(BlockIter::from_blocks(self.bit_vec.blocks()))
15591559
}
15601560

15611561
/// Iterator over each usize stored in `self` union `other`.
@@ -1580,13 +1580,11 @@ impl BitSet {
15801580
pub fn union<'a>(&'a self, other: &'a BitSet) -> Union<'a> {
15811581
fn or(w1: u32, w2: u32) -> u32 { w1 | w2 }
15821582

1583-
Union(TwoBitPositions {
1584-
set: self,
1585-
other: other,
1583+
Union(BlockIter::from_blocks(TwoBitPositions {
1584+
set: self.bit_vec.blocks(),
1585+
other: other.bit_vec.blocks(),
15861586
merge: or,
1587-
current_word: 0,
1588-
next_idx: 0
1589-
})
1587+
}))
15901588
}
15911589

15921590
/// Iterator over each usize stored in `self` intersect `other`.
@@ -1611,13 +1609,12 @@ impl BitSet {
16111609
pub fn intersection<'a>(&'a self, other: &'a BitSet) -> Intersection<'a> {
16121610
fn bitand(w1: u32, w2: u32) -> u32 { w1 & w2 }
16131611
let min = cmp::min(self.bit_vec.len(), other.bit_vec.len());
1614-
Intersection(TwoBitPositions {
1615-
set: self,
1616-
other: other,
1612+
1613+
Intersection(BlockIter::from_blocks(TwoBitPositions {
1614+
set: self.bit_vec.blocks(),
1615+
other: other.bit_vec.blocks(),
16171616
merge: bitand,
1618-
current_word: 0,
1619-
next_idx: 0
1620-
}.take(min))
1617+
}).take(min))
16211618
}
16221619

16231620
/// Iterator over each usize stored in the `self` setminus `other`.
@@ -1649,13 +1646,11 @@ impl BitSet {
16491646
pub fn difference<'a>(&'a self, other: &'a BitSet) -> Difference<'a> {
16501647
fn diff(w1: u32, w2: u32) -> u32 { w1 & !w2 }
16511648

1652-
Difference(TwoBitPositions {
1653-
set: self,
1654-
other: other,
1649+
Difference(BlockIter::from_blocks(TwoBitPositions {
1650+
set: self.bit_vec.blocks(),
1651+
other: other.bit_vec.blocks(),
16551652
merge: diff,
1656-
current_word: 0,
1657-
next_idx: 0
1658-
})
1653+
}))
16591654
}
16601655

16611656
/// Iterator over each usize stored in the symmetric difference of `self` and `other`.
@@ -1681,13 +1676,11 @@ impl BitSet {
16811676
pub fn symmetric_difference<'a>(&'a self, other: &'a BitSet) -> SymmetricDifference<'a> {
16821677
fn bitxor(w1: u32, w2: u32) -> u32 { w1 ^ w2 }
16831678

1684-
SymmetricDifference(TwoBitPositions {
1685-
set: self,
1686-
other: other,
1679+
SymmetricDifference(BlockIter::from_blocks(TwoBitPositions {
1680+
set: self.bit_vec.blocks(),
1681+
other: other.bit_vec.blocks(),
16871682
merge: bitxor,
1688-
current_word: 0,
1689-
next_idx: 0
1690-
})
1683+
}))
16911684
}
16921685

16931686
/// Unions in-place with the specified other bit vector.
@@ -1994,98 +1987,114 @@ impl hash::Hash for BitSet {
19941987
}
19951988
}
19961989

1997-
/// An iterator for `BitSet`.
19981990
#[derive(Clone)]
19991991
#[stable(feature = "rust1", since = "1.0.0")]
2000-
pub struct SetIter<'a> {
2001-
set: &'a BitSet,
2002-
next_idx: usize
1992+
struct BlockIter<T> where T: Iterator<Item=u32> {
1993+
head: u32,
1994+
head_offset: usize,
1995+
tail: T,
1996+
}
1997+
1998+
impl<'a, T> BlockIter<T> where T: Iterator<Item=u32> {
1999+
fn from_blocks(mut blocks: T) -> BlockIter<T> {
2000+
let h = blocks.next().unwrap_or(0);
2001+
BlockIter {tail: blocks, head: h, head_offset: 0}
2002+
}
20032003
}
20042004

20052005
/// An iterator combining two `BitSet` iterators.
20062006
#[derive(Clone)]
20072007
struct TwoBitPositions<'a> {
2008-
set: &'a BitSet,
2009-
other: &'a BitSet,
2008+
set: Blocks<'a>,
2009+
other: Blocks<'a>,
20102010
merge: fn(u32, u32) -> u32,
2011-
current_word: u32,
2012-
next_idx: usize
20132011
}
20142012

2013+
/// An iterator for `BitSet`.
2014+
#[derive(Clone)]
2015+
#[stable(feature = "rust1", since = "1.0.0")]
2016+
pub struct SetIter<'a>(BlockIter<Blocks<'a>>);
20152017
#[derive(Clone)]
20162018
#[stable(feature = "rust1", since = "1.0.0")]
2017-
pub struct Union<'a>(TwoBitPositions<'a>);
2019+
pub struct Union<'a>(BlockIter<TwoBitPositions<'a>>);
20182020
#[derive(Clone)]
20192021
#[stable(feature = "rust1", since = "1.0.0")]
2020-
pub struct Intersection<'a>(Take<TwoBitPositions<'a>>);
2022+
pub struct Intersection<'a>(Take<BlockIter<TwoBitPositions<'a>>>);
20212023
#[derive(Clone)]
20222024
#[stable(feature = "rust1", since = "1.0.0")]
2023-
pub struct Difference<'a>(TwoBitPositions<'a>);
2025+
pub struct Difference<'a>(BlockIter<TwoBitPositions<'a>>);
20242026
#[derive(Clone)]
20252027
#[stable(feature = "rust1", since = "1.0.0")]
2026-
pub struct SymmetricDifference<'a>(TwoBitPositions<'a>);
2028+
pub struct SymmetricDifference<'a>(BlockIter<TwoBitPositions<'a>>);
20272029

20282030
#[stable(feature = "rust1", since = "1.0.0")]
2029-
impl<'a> Iterator for SetIter<'a> {
2031+
impl<'a, T> Iterator for BlockIter<T> where T: Iterator<Item=u32> {
20302032
type Item = usize;
20312033

20322034
fn next(&mut self) -> Option<usize> {
2033-
while self.next_idx < self.set.bit_vec.len() {
2034-
let idx = self.next_idx;
2035-
self.next_idx += 1;
2036-
2037-
if self.set.contains(&idx) {
2038-
return Some(idx);
2035+
while self.head == 0 {
2036+
match self.tail.next() {
2037+
Some(w) => self.head = w,
2038+
None => return None
20392039
}
2040+
self.head_offset += u32::BITS;
20402041
}
20412042

2042-
return None;
2043+
// from the current block, isolate the
2044+
// LSB and subtract 1, producing k:
2045+
// a block with a number of set bits
2046+
// equal to the index of the LSB
2047+
let k = (self.head & (!self.head + 1)) - 1;
2048+
// update block, removing the LSB
2049+
self.head &= self.head - 1;
2050+
// return offset + (index of LSB)
2051+
Some(self.head_offset + (u32::count_ones(k) as usize))
20432052
}
20442053

20452054
#[inline]
20462055
fn size_hint(&self) -> (usize, Option<usize>) {
2047-
(0, Some(self.set.bit_vec.len() - self.next_idx))
2056+
match self.tail.size_hint() {
2057+
(_, Some(h)) => (0, Some(1 + h * (u32::BITS as usize))),
2058+
_ => (0, None)
2059+
}
20482060
}
20492061
}
20502062

20512063
#[stable(feature = "rust1", since = "1.0.0")]
20522064
impl<'a> Iterator for TwoBitPositions<'a> {
2053-
type Item = usize;
2054-
2055-
fn next(&mut self) -> Option<usize> {
2056-
while self.next_idx < self.set.bit_vec.len() ||
2057-
self.next_idx < self.other.bit_vec.len() {
2058-
let bit_idx = self.next_idx % u32::BITS;
2059-
if bit_idx == 0 {
2060-
let s_bit_vec = &self.set.bit_vec;
2061-
let o_bit_vec = &self.other.bit_vec;
2062-
// Merging the two words is a bit of an awkward dance since
2063-
// one BitVec might be longer than the other
2064-
let word_idx = self.next_idx / u32::BITS;
2065-
let w1 = if word_idx < s_bit_vec.storage.len() {
2066-
s_bit_vec.storage[word_idx]
2067-
} else { 0 };
2068-
let w2 = if word_idx < o_bit_vec.storage.len() {
2069-
o_bit_vec.storage[word_idx]
2070-
} else { 0 };
2071-
self.current_word = (self.merge)(w1, w2);
2072-
}
2073-
2074-
self.next_idx += 1;
2075-
if self.current_word & (1 << bit_idx) != 0 {
2076-
return Some(self.next_idx - 1);
2077-
}
2065+
type Item = u32;
2066+
2067+
fn next(&mut self) -> Option<u32> {
2068+
match (self.set.next(), self.other.next()) {
2069+
(Some(a), Some(b)) => Some((self.merge)(a, b)),
2070+
(Some(a), None) => Some((self.merge)(a, 0)),
2071+
(None, Some(b)) => Some((self.merge)(0, b)),
2072+
_ => return None
20782073
}
2079-
return None;
20802074
}
20812075

20822076
#[inline]
20832077
fn size_hint(&self) -> (usize, Option<usize>) {
2084-
let cap = cmp::max(self.set.bit_vec.len(), self.other.bit_vec.len());
2085-
(0, Some(cap - self.next_idx))
2078+
let (a, au) = self.set.size_hint();
2079+
let (b, bu) = self.other.size_hint();
2080+
2081+
let upper = match (au, bu) {
2082+
(Some(au), Some(bu)) => Some(cmp::max(au, bu)),
2083+
_ => None
2084+
};
2085+
2086+
(cmp::max(a, b), upper)
20862087
}
20872088
}
20882089

2090+
#[stable(feature = "rust1", since = "1.0.0")]
2091+
impl<'a> Iterator for SetIter<'a> {
2092+
type Item = usize;
2093+
2094+
#[inline] fn next(&mut self) -> Option<usize> { self.0.next() }
2095+
#[inline] fn size_hint(&self) -> (usize, Option<usize>) { self.0.size_hint() }
2096+
}
2097+
20892098
#[stable(feature = "rust1", since = "1.0.0")]
20902099
impl<'a> Iterator for Union<'a> {
20912100
type Item = usize;

0 commit comments

Comments
 (0)