@@ -5,6 +5,10 @@ use std::iter;
55use std:: marker:: PhantomData ;
66use std:: mem;
77use std:: slice;
8+ #[ cfg( test) ]
9+ extern crate test;
10+ #[ cfg( test) ]
11+ use test:: Bencher ;
812
913pub type Word = u64 ;
1014pub const WORD_BYTES : usize = mem:: size_of :: < Word > ( ) ;
@@ -177,6 +181,45 @@ impl<T: Idx> BitSet<T> {
177181 // Note: we currently don't bother trying to make a Sparse set.
178182 HybridBitSet :: Dense ( self . to_owned ( ) )
179183 }
184+
185+ /// Set `self = self | other`. In contrast to `union` returns `true` if the set contains at
186+ /// least one bit that is not in `other` (i.e. `other` is not a superset of `self`).
187+ ///
188+ /// This is an optimization for union of a hybrid bitset.
189+ fn reverse_union_sparse ( & mut self , sparse : & SparseBitSet < T > ) -> bool {
190+ assert ! ( sparse. domain_size == self . domain_size) ;
191+ self . clear_excess_bits ( ) ;
192+
193+ let mut not_already = false ;
194+ // Index of the current word not yet merged.
195+ let mut current_index = 0 ;
196+ // Mask of bits that came from the sparse set in the current word.
197+ let mut new_bit_mask = 0 ;
198+ for ( word_index, mask) in sparse. iter ( ) . map ( |x| word_index_and_mask ( * x) ) {
199+ // Next bit is in a word not inspected yet.
200+ if word_index > current_index {
201+ self . words [ current_index] |= new_bit_mask;
202+ // Were there any bits in the old word that did not occur in the sparse set?
203+ not_already |= ( self . words [ current_index] ^ new_bit_mask) != 0 ;
204+ // Check all words we skipped for any set bit.
205+ not_already |= self . words [ current_index+1 ..word_index] . iter ( ) . any ( |& x| x != 0 ) ;
206+ // Update next word.
207+ current_index = word_index;
208+ // Reset bit mask, no bits have been merged yet.
209+ new_bit_mask = 0 ;
210+ }
211+ // Add bit and mark it as coming from the sparse set.
212+ // self.words[word_index] |= mask;
213+ new_bit_mask |= mask;
214+ }
215+ self . words [ current_index] |= new_bit_mask;
216+ // Any bits in the last inspected word that were not in the sparse set?
217+ not_already |= ( self . words [ current_index] ^ new_bit_mask) != 0 ;
218+ // Any bits in the tail? Note `clear_excess_bits` before.
219+ not_already |= self . words [ current_index+1 ..] . iter ( ) . any ( |& x| x != 0 ) ;
220+
221+ not_already
222+ }
180223}
181224
182225/// This is implemented by all the bitsets so that BitSet::union() can be
@@ -514,10 +557,22 @@ impl<T: Idx> HybridBitSet<T> {
514557 changed
515558 }
516559 HybridBitSet :: Dense ( other_dense) => {
517- // `self` is sparse and `other` is dense. Densify
518- // `self` and then do the bitwise union.
519- let mut new_dense = self_sparse. to_dense ( ) ;
520- let changed = new_dense. union ( other_dense) ;
560+ // `self` is sparse and `other` is dense. To
561+ // merge them, we have two available strategies:
562+ // * Densify `self` then merge other
563+ // * Clone other then integrate bits from `self`
564+ // The second strategy requires dedicated method
565+ // since the usual `union` returns the wrong
566+ // result. In the dedicated case the computation
567+ // is slightly faster if the bits of the sparse
568+ // bitset map to only few words of the dense
569+ // representation, i.e. indices are near each
570+ // other.
571+ //
572+ // Benchmarking seems to suggest that the second
573+ // option is worth it.
574+ let mut new_dense = other_dense. clone ( ) ;
575+ let changed = new_dense. reverse_union_sparse ( self_sparse) ;
521576 * self = HybridBitSet :: Dense ( new_dense) ;
522577 changed
523578 }
@@ -1214,3 +1269,87 @@ fn sparse_matrix_iter() {
12141269 }
12151270 assert ! ( iter. next( ) . is_none( ) ) ;
12161271}
1272+
1273+ /// Merge dense hybrid set into empty sparse hybrid set.
1274+ #[ bench]
1275+ fn union_hybrid_sparse_empty_to_dense ( b : & mut Bencher ) {
1276+ let mut pre_dense: HybridBitSet < usize > = HybridBitSet :: new_empty ( 256 ) ;
1277+ for i in 0 ..10 {
1278+ assert ! ( pre_dense. insert( i) ) ;
1279+ }
1280+ let pre_sparse: HybridBitSet < usize > = HybridBitSet :: new_empty ( 256 ) ;
1281+ b. iter ( || {
1282+ let dense = pre_dense. clone ( ) ;
1283+ let mut sparse = pre_sparse. clone ( ) ;
1284+ sparse. union ( & dense) ;
1285+ } )
1286+ }
1287+
1288+ /// Merge dense hybrid set into full hybrid set with same indices.
1289+ #[ bench]
1290+ fn union_hybrid_sparse_full_to_dense ( b : & mut Bencher ) {
1291+ let mut pre_dense: HybridBitSet < usize > = HybridBitSet :: new_empty ( 256 ) ;
1292+ for i in 0 ..10 {
1293+ assert ! ( pre_dense. insert( i) ) ;
1294+ }
1295+ let mut pre_sparse: HybridBitSet < usize > = HybridBitSet :: new_empty ( 256 ) ;
1296+ for i in 0 ..SPARSE_MAX {
1297+ assert ! ( pre_sparse. insert( i) ) ;
1298+ }
1299+ b. iter ( || {
1300+ let dense = pre_dense. clone ( ) ;
1301+ let mut sparse = pre_sparse. clone ( ) ;
1302+ sparse. union ( & dense) ;
1303+ } )
1304+ }
1305+
1306+ /// Merge dense hybrid set into full hybrid set with indices over the whole domain.
1307+ #[ bench]
1308+ fn union_hybrid_sparse_domain_to_dense ( b : & mut Bencher ) {
1309+ let mut pre_dense: HybridBitSet < usize > = HybridBitSet :: new_empty ( SPARSE_MAX * 64 ) ;
1310+ for i in 0 ..10 {
1311+ assert ! ( pre_dense. insert( i) ) ;
1312+ }
1313+ let mut pre_sparse: HybridBitSet < usize > = HybridBitSet :: new_empty ( SPARSE_MAX * 64 ) ;
1314+ for i in 0 ..SPARSE_MAX {
1315+ assert ! ( pre_sparse. insert( i* 64 ) ) ;
1316+ }
1317+ b. iter ( || {
1318+ let dense = pre_dense. clone ( ) ;
1319+ let mut sparse = pre_sparse. clone ( ) ;
1320+ sparse. union ( & dense) ;
1321+ } )
1322+ }
1323+
1324+ /// Merge dense hybrid set into empty hybrid set where the domain is very small.
1325+ #[ bench]
1326+ fn union_hybrid_sparse_empty_small_domain ( b : & mut Bencher ) {
1327+ let mut pre_dense: HybridBitSet < usize > = HybridBitSet :: new_empty ( SPARSE_MAX ) ;
1328+ for i in 0 ..SPARSE_MAX {
1329+ assert ! ( pre_dense. insert( i) ) ;
1330+ }
1331+ let pre_sparse: HybridBitSet < usize > = HybridBitSet :: new_empty ( SPARSE_MAX ) ;
1332+ b. iter ( || {
1333+ let dense = pre_dense. clone ( ) ;
1334+ let mut sparse = pre_sparse. clone ( ) ;
1335+ sparse. union ( & dense) ;
1336+ } )
1337+ }
1338+
1339+ /// Merge dense hybrid set into full hybrid set where the domain is very small.
1340+ #[ bench]
1341+ fn union_hybrid_sparse_full_small_domain ( b : & mut Bencher ) {
1342+ let mut pre_dense: HybridBitSet < usize > = HybridBitSet :: new_empty ( SPARSE_MAX ) ;
1343+ for i in 0 ..SPARSE_MAX {
1344+ assert ! ( pre_dense. insert( i) ) ;
1345+ }
1346+ let mut pre_sparse: HybridBitSet < usize > = HybridBitSet :: new_empty ( SPARSE_MAX ) ;
1347+ for i in 0 ..SPARSE_MAX {
1348+ assert ! ( pre_sparse. insert( i) ) ;
1349+ }
1350+ b. iter ( || {
1351+ let dense = pre_dense. clone ( ) ;
1352+ let mut sparse = pre_sparse. clone ( ) ;
1353+ sparse. union ( & dense) ;
1354+ } )
1355+ }
0 commit comments