Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions arraycontainer.go
Original file line number Diff line number Diff line change
Expand Up @@ -628,6 +628,30 @@ func (ac *arrayContainer) xor(a container) container {
panic("unsupported container type")
}

func (ac *arrayContainer) ixor(a container) container {
switch x := a.(type) {
case *arrayContainer:
return ac.ixorArray(x)
case *bitmapContainer:
return ac.ixorBitmap(x)
case *runContainer16:
return ac.ixorRun16(x)
}
panic("unsupported container type")
}

func (ac *arrayContainer) ixorArray(value2 *arrayContainer) container {
return ac.xorArray(value2)
}

func (ac *arrayContainer) ixorBitmap(value2 *bitmapContainer) container {
return value2.ixor(ac)
}

func (ac *arrayContainer) ixorRun16(value2 *runContainer16) container {
return value2.ixor(ac)
}

func (ac *arrayContainer) xorArray(value2 *arrayContainer) container {
value1 := ac
totalCardinality := value1.getCardinality() + value2.getCardinality()
Expand Down
29 changes: 29 additions & 0 deletions benchmark_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1067,6 +1067,35 @@ func BenchmarkXorLopsided(b *testing.B) {
}
}

// BenchmarkXorDense benchmarks in-place Xor (ixor) on dense data
// where containers are bitmapContainers (>4096 values per 16-bit chunk).
// This is where in-place mutation of the bitmap[]uint64 slice should shine.
func BenchmarkXorDense(b *testing.B) {
b.StopTimer()
r := rand.New(rand.NewSource(0))
// 50 chunks, each with ~10000 values → bitmapContainers (threshold is 4096)
numChunks := 50
valsPerChunk := 10000
s := NewBitmap()
for chunk := 0; chunk < numChunks; chunk++ {
base := uint32(chunk) * 65536
for i := 0; i < valsPerChunk; i++ {
s.Add(base + uint32(r.Intn(65536)))
}
}
x2 := NewBitmap()
for chunk := 0; chunk < numChunks; chunk++ {
base := uint32(chunk) * 65536
for i := 0; i < valsPerChunk; i++ {
x2.Add(base + uint32(r.Intn(65536)))
}
}
b.StartTimer()
for j := 0; j < b.N; j++ {
s.Xor(x2)
}
}

func BenchmarkBitmapReuseWithoutClear(b *testing.B) {
for j := 0; j < b.N; j++ {
s := NewBitmap()
Expand Down
37 changes: 37 additions & 0 deletions bitmapcontainer.go
Original file line number Diff line number Diff line change
Expand Up @@ -915,6 +915,43 @@ func (bc *bitmapContainer) iandBitmap(value2 *bitmapContainer) container {
return bc
}

func (bc *bitmapContainer) ixor(a container) container {
switch x := a.(type) {
case *arrayContainer:
return bc.ixorArray(x)
case *bitmapContainer:
return bc.ixorBitmap(x)
case *runContainer16:
return bc.ixorRun16(x)
}
panic("unsupported container type")
}

func (bc *bitmapContainer) ixorArray(value2 *arrayContainer) container {
vbc := value2.toBitmapContainer()
return bc.ixorBitmap(vbc)
}

func (bc *bitmapContainer) ixorRun16(value2 *runContainer16) container {
rcb := value2.toBitmapContainer()
return bc.ixorBitmap(rcb)
}

func (bc *bitmapContainer) ixorBitmap(value2 *bitmapContainer) container {
newCardinality := int(popcntXorSlice(bc.bitmap, value2.bitmap))
if newCardinality > arrayDefaultMaxSize {
for k := 0; k < len(bc.bitmap); k++ {
bc.bitmap[k] = bc.bitmap[k] ^ value2.bitmap[k]
}
bc.cardinality = newCardinality
return bc
}
ac := newArrayContainerSize(newCardinality)
fillArrayXOR(ac.content, bc.bitmap, value2.bitmap)
ac.content = ac.content[:newCardinality]
return ac
}

func (bc *bitmapContainer) andNot(a container) container {
switch x := a.(type) {
case *arrayContainer:
Expand Down
3 changes: 1 addition & 2 deletions roaring.go
Original file line number Diff line number Diff line change
Expand Up @@ -1509,8 +1509,7 @@ func (rb *Bitmap) Xor(x2 *Bitmap) {
pos1++
pos2++
} else {
// TODO: couple be computed in-place for reduced memory usage
c := rb.highlowcontainer.getContainerAtIndex(pos1).xor(x2.highlowcontainer.getContainerAtIndex(pos2))
c := rb.highlowcontainer.getWritableContainerAtIndex(pos1).ixor(x2.highlowcontainer.getContainerAtIndex(pos2))
if !c.isEmpty() {
rb.highlowcontainer.setContainerAtIndex(pos1, c)
pos1++
Expand Down
1 change: 1 addition & 0 deletions roaringarray.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ type container interface {
not(start, final int) container // range is [firstOfRange,lastOfRange)
inot(firstOfRange, endx int) container // i stands for inplace, range is [firstOfRange,endx)
xor(r container) container
ixor(r container) container // i stands for inplace
getShortIterator() shortPeekable
getUnsetIterator() shortPeekable
iterate(cb func(x uint16) bool) bool
Expand Down
24 changes: 24 additions & 0 deletions runcontainer.go
Original file line number Diff line number Diff line change
Expand Up @@ -2416,6 +2416,30 @@ func (rc *runContainer16) xor(a container) container {
panic("unsupported container type")
}

func (rc *runContainer16) ixor(a container) container {
switch c := a.(type) {
case *arrayContainer:
return rc.ixorArray(c)
case *bitmapContainer:
return rc.ixorBitmap(c)
case *runContainer16:
return rc.ixorRunContainer16(c)
}
panic("unsupported container type")
}

func (rc *runContainer16) ixorArray(value2 *arrayContainer) container {
return rc.toBitmapContainer().ixor(value2)
}

func (rc *runContainer16) ixorBitmap(value2 *bitmapContainer) container {
return value2.ixor(rc)
}

func (rc *runContainer16) ixorRunContainer16(value2 *runContainer16) container {
return rc.toBitmapContainer().ixor(value2.toBitmapContainer())
}

func (rc *runContainer16) iandNot(a container) container {
switch c := a.(type) {
case *arrayContainer:
Expand Down
Loading