Skip to content

Commit 3fe77fa

Browse files
authored
Merge pull request #509 from DhruvilK7/master
In place XOR
2 parents 3def95f + 07bccb8 commit 3fe77fa

6 files changed

Lines changed: 116 additions & 2 deletions

File tree

arraycontainer.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,30 @@ func (ac *arrayContainer) xor(a container) container {
628628
panic("unsupported container type")
629629
}
630630

631+
func (ac *arrayContainer) ixor(a container) container {
632+
switch x := a.(type) {
633+
case *arrayContainer:
634+
return ac.ixorArray(x)
635+
case *bitmapContainer:
636+
return ac.ixorBitmap(x)
637+
case *runContainer16:
638+
return ac.ixorRun16(x)
639+
}
640+
panic("unsupported container type")
641+
}
642+
643+
func (ac *arrayContainer) ixorArray(value2 *arrayContainer) container {
644+
return ac.xorArray(value2)
645+
}
646+
647+
func (ac *arrayContainer) ixorBitmap(value2 *bitmapContainer) container {
648+
return value2.ixor(ac)
649+
}
650+
651+
func (ac *arrayContainer) ixorRun16(value2 *runContainer16) container {
652+
return value2.ixor(ac)
653+
}
654+
631655
func (ac *arrayContainer) xorArray(value2 *arrayContainer) container {
632656
value1 := ac
633657
totalCardinality := value1.getCardinality() + value2.getCardinality()

benchmark_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1067,6 +1067,35 @@ func BenchmarkXorLopsided(b *testing.B) {
10671067
}
10681068
}
10691069

1070+
// BenchmarkXorDense benchmarks in-place Xor (ixor) on dense data
1071+
// where containers are bitmapContainers (>4096 values per 16-bit chunk).
1072+
// This is where in-place mutation of the bitmap[]uint64 slice should shine.
1073+
func BenchmarkXorDense(b *testing.B) {
1074+
b.StopTimer()
1075+
r := rand.New(rand.NewSource(0))
1076+
// 50 chunks, each with ~10000 values → bitmapContainers (threshold is 4096)
1077+
numChunks := 50
1078+
valsPerChunk := 10000
1079+
s := NewBitmap()
1080+
for chunk := 0; chunk < numChunks; chunk++ {
1081+
base := uint32(chunk) * 65536
1082+
for i := 0; i < valsPerChunk; i++ {
1083+
s.Add(base + uint32(r.Intn(65536)))
1084+
}
1085+
}
1086+
x2 := NewBitmap()
1087+
for chunk := 0; chunk < numChunks; chunk++ {
1088+
base := uint32(chunk) * 65536
1089+
for i := 0; i < valsPerChunk; i++ {
1090+
x2.Add(base + uint32(r.Intn(65536)))
1091+
}
1092+
}
1093+
b.StartTimer()
1094+
for j := 0; j < b.N; j++ {
1095+
s.Xor(x2)
1096+
}
1097+
}
1098+
10701099
func BenchmarkBitmapReuseWithoutClear(b *testing.B) {
10711100
for j := 0; j < b.N; j++ {
10721101
s := NewBitmap()

bitmapcontainer.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -915,6 +915,43 @@ func (bc *bitmapContainer) iandBitmap(value2 *bitmapContainer) container {
915915
return bc
916916
}
917917

918+
func (bc *bitmapContainer) ixor(a container) container {
919+
switch x := a.(type) {
920+
case *arrayContainer:
921+
return bc.ixorArray(x)
922+
case *bitmapContainer:
923+
return bc.ixorBitmap(x)
924+
case *runContainer16:
925+
return bc.ixorRun16(x)
926+
}
927+
panic("unsupported container type")
928+
}
929+
930+
func (bc *bitmapContainer) ixorArray(value2 *arrayContainer) container {
931+
vbc := value2.toBitmapContainer()
932+
return bc.ixorBitmap(vbc)
933+
}
934+
935+
func (bc *bitmapContainer) ixorRun16(value2 *runContainer16) container {
936+
rcb := value2.toBitmapContainer()
937+
return bc.ixorBitmap(rcb)
938+
}
939+
940+
func (bc *bitmapContainer) ixorBitmap(value2 *bitmapContainer) container {
941+
newCardinality := int(popcntXorSlice(bc.bitmap, value2.bitmap))
942+
if newCardinality > arrayDefaultMaxSize {
943+
for k := 0; k < len(bc.bitmap); k++ {
944+
bc.bitmap[k] = bc.bitmap[k] ^ value2.bitmap[k]
945+
}
946+
bc.cardinality = newCardinality
947+
return bc
948+
}
949+
ac := newArrayContainerSize(newCardinality)
950+
fillArrayXOR(ac.content, bc.bitmap, value2.bitmap)
951+
ac.content = ac.content[:newCardinality]
952+
return ac
953+
}
954+
918955
func (bc *bitmapContainer) andNot(a container) container {
919956
switch x := a.(type) {
920957
case *arrayContainer:

roaring.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1509,8 +1509,7 @@ func (rb *Bitmap) Xor(x2 *Bitmap) {
15091509
pos1++
15101510
pos2++
15111511
} else {
1512-
// TODO: couple be computed in-place for reduced memory usage
1513-
c := rb.highlowcontainer.getContainerAtIndex(pos1).xor(x2.highlowcontainer.getContainerAtIndex(pos2))
1512+
c := rb.highlowcontainer.getWritableContainerAtIndex(pos1).ixor(x2.highlowcontainer.getContainerAtIndex(pos2))
15141513
if !c.isEmpty() {
15151514
rb.highlowcontainer.setContainerAtIndex(pos1, c)
15161515
pos1++

roaringarray.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ type container interface {
3939
not(start, final int) container // range is [firstOfRange,lastOfRange)
4040
inot(firstOfRange, endx int) container // i stands for inplace, range is [firstOfRange,endx)
4141
xor(r container) container
42+
ixor(r container) container // i stands for inplace
4243
getShortIterator() shortPeekable
4344
getUnsetIterator() shortPeekable
4445
iterate(cb func(x uint16) bool) bool

runcontainer.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2416,6 +2416,30 @@ func (rc *runContainer16) xor(a container) container {
24162416
panic("unsupported container type")
24172417
}
24182418

2419+
func (rc *runContainer16) ixor(a container) container {
2420+
switch c := a.(type) {
2421+
case *arrayContainer:
2422+
return rc.ixorArray(c)
2423+
case *bitmapContainer:
2424+
return rc.ixorBitmap(c)
2425+
case *runContainer16:
2426+
return rc.ixorRunContainer16(c)
2427+
}
2428+
panic("unsupported container type")
2429+
}
2430+
2431+
func (rc *runContainer16) ixorArray(value2 *arrayContainer) container {
2432+
return rc.toBitmapContainer().ixor(value2)
2433+
}
2434+
2435+
func (rc *runContainer16) ixorBitmap(value2 *bitmapContainer) container {
2436+
return value2.ixor(rc)
2437+
}
2438+
2439+
func (rc *runContainer16) ixorRunContainer16(value2 *runContainer16) container {
2440+
return rc.toBitmapContainer().ixor(value2.toBitmapContainer())
2441+
}
2442+
24192443
func (rc *runContainer16) iandNot(a container) container {
24202444
switch c := a.(type) {
24212445
case *arrayContainer:

0 commit comments

Comments
 (0)