@@ -5,6 +5,10 @@ use std::iter;
5
5
use std:: marker:: PhantomData ;
6
6
use std:: mem;
7
7
use std:: slice;
8
+ #[ cfg( test) ]
9
+ extern crate test;
10
+ #[ cfg( test) ]
11
+ use test:: Bencher ;
8
12
9
13
pub type Word = u64 ;
10
14
pub const WORD_BYTES : usize = mem:: size_of :: < Word > ( ) ;
@@ -177,6 +181,45 @@ impl<T: Idx> BitSet<T> {
177
181
// Note: we currently don't bother trying to make a Sparse set.
178
182
HybridBitSet :: Dense ( self . to_owned ( ) )
179
183
}
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
+ }
180
223
}
181
224
182
225
/// This is implemented by all the bitsets so that BitSet::union() can be
@@ -514,10 +557,22 @@ impl<T: Idx> HybridBitSet<T> {
514
557
changed
515
558
}
516
559
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) ;
521
576
* self = HybridBitSet :: Dense ( new_dense) ;
522
577
changed
523
578
}
@@ -1214,3 +1269,87 @@ fn sparse_matrix_iter() {
1214
1269
}
1215
1270
assert ! ( iter. next( ) . is_none( ) ) ;
1216
1271
}
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