Skip to content

Commit 35d7182

Browse files
authored
Merge pull request #419 from neena/neena/faster-iandnot-bm-arr
faster iandnot between bitmap and array containers
2 parents 2bf931c + 0f133eb commit 35d7182

File tree

2 files changed

+38
-3
lines changed

2 files changed

+38
-3
lines changed

benchmark_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ func BenchmarkUnionRoaring(b *testing.B) {
322322
// The nested for-loops test a number of different scenarios
323323
// with respect to the ranges and densities of bitmaps.
324324
func BenchmarkUnionInPlaceCopyOnWrite(b *testing.B) {
325-
//uint32s to maintain 1.12 compatibility, which requires unsigned shifts.
325+
// uint32s to maintain 1.12 compatibility, which requires unsigned shifts.
326326
startingContainerPower := uint32(4)
327327
finalContainerPower := uint32(10)
328328
containerIncrement := uint32(3)
@@ -1181,6 +1181,7 @@ func BenchmarkAndNot(b *testing.B) {
11811181
b.Run(fmt.Sprintf("left=%s", leftGen.name), func(b *testing.B) {
11821182
for _, rightGen := range []generator{makeRunContainer, makeArrayContainer, makeBitmapContainer} {
11831183
b.Run(fmt.Sprintf("right=%s", rightGen.name), func(b *testing.B) {
1184+
b.ReportAllocs()
11841185
b.StopTimer()
11851186
serializedLefts := make([][]byte, 1000)
11861187
for i := range serializedLefts {

bitmapcontainer.go

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -888,8 +888,42 @@ func (bc *bitmapContainer) iandNot(a container) container {
888888
}
889889

890890
func (bc *bitmapContainer) iandNotArray(ac *arrayContainer) container {
891-
acb := ac.toBitmapContainer()
892-
return bc.iandNotBitmapSurely(acb)
891+
if ac.isEmpty() || bc.isEmpty() {
892+
// Nothing to do.
893+
return bc
894+
}
895+
896+
// Word by word, we remove the elements in ac from bc. The approach is to build
897+
// a mask of the elements to remove, and then apply it to the bitmap.
898+
wordIdx := uint16(0)
899+
mask := uint64(0)
900+
for i, v := range ac.content {
901+
if v/64 != wordIdx {
902+
// Flush the current word.
903+
if i != 0 {
904+
// We're removing bits that are set in the mask and in the current word.
905+
// To figure out the cardinality change, we count the number of bits that
906+
// are set in the mask and in the current word.
907+
mask &= bc.bitmap[wordIdx]
908+
bc.bitmap[wordIdx] &= ^mask
909+
bc.cardinality -= int(popcount(mask))
910+
}
911+
912+
wordIdx = v / 64
913+
mask = 0
914+
}
915+
mask |= 1 << (v % 64)
916+
}
917+
918+
// Flush the last word.
919+
mask &= bc.bitmap[wordIdx]
920+
bc.bitmap[wordIdx] &= ^mask
921+
bc.cardinality -= int(popcount(mask))
922+
923+
if bc.getCardinality() <= arrayDefaultMaxSize {
924+
return bc.toArrayContainer()
925+
}
926+
return bc
893927
}
894928

895929
func (bc *bitmapContainer) iandNotRun16(rc *runContainer16) container {

0 commit comments

Comments
 (0)