included previous patterns for in place testing

This commit is contained in:
Todd Gruben 2020-01-21 16:56:33 -06:00
parent 0459285101
commit f40ced47fa
3 changed files with 618 additions and 2 deletions

View file

@ -25,6 +25,7 @@ import (
"sort"
"unsafe"
"github.com/pilosa/pilosa"
"github.com/pkg/errors"
)
@ -995,8 +996,6 @@ func (b *Bitmap) unionInPlace(others ...*Bitmap) {
// Difference returns the difference of b and other.
func (b *Bitmap) Difference(other *Bitmap) *Bitmap {
output := NewBitmap()
output.Source = b.Source
iiter, _ := b.Containers.Iterator(0)
jiter, _ := other.Containers.Iterator(0)
i, j := iiter.Next(), jiter.Next()
@ -2668,6 +2667,7 @@ func (c *Container) arrayRemove(v uint16) (*Container, bool) {
}
// removing the last item? we can just return the empty container.
if c.N() == 1 {
c.n = 0
return nil, true
}
c = c.Thaw()
@ -2684,6 +2684,7 @@ func (c *Container) bitmapRemove(v uint16) (*Container, bool) {
}
// removing the last item? we can just return the empty container.
if c.N() == 1 {
c.n = 0
return nil, true
}
c = c.Thaw()
@ -2709,6 +2710,7 @@ func (c *Container) runRemove(v uint16) (*Container, bool) {
}
// removing the last item? we can just return the empty container.
if c.N() == 1 {
c.n = 0
return nil, true
}
c = c.Thaw()
@ -5451,3 +5453,436 @@ type containerUnionSummaryStats struct {
// extremely dense containers.
hasMaxRange bool
}
func (b *Bitmap) DifferenceInPlace(others ...*Bitmap) {
var (
requiredSliceSize = len(others)
// To avoid having to allocate a slice everytime, if the number of bitmaps
// being unioned is small enough we can just use this stack-allocated array.
staticHandledIters = [20]handledIter{}
bitmapIters handledIters
target = b
removeContinerKeys = [pilosa.ShardWidth / (1 << 16)]uint64{} //i need a better guess for this
)
if requiredSliceSize <= 20 {
bitmapIters = staticHandledIters[:0]
} else {
bitmapIters = make(handledIters, 0, requiredSliceSize)
}
for _, other := range others {
otherIter, _ := other.Containers.Iterator(0)
if otherIter.Next() {
bitmapIters = append(bitmapIters, handledIter{
iter: otherIter,
hasNext: true,
})
}
}
targetItr, _ := target.Containers.Iterator(0)
// Go through all the containers and remove the other bits
n := 0
for targetItr.Next() {
targetKey, curContainer := targetItr.Value()
// Loop until every iters current value has been handled.
for _, iIter := range bitmapIters {
if !iIter.hasNext {
continue
}
iKey, iContainer := iIter.iter.Value()
if targetKey == iKey {
curContainer.differenceInPlaceO(iContainer)
if curContainer.N() == 0 { //according to comments N = 1-count, so N should == 1 if 0 elements
removeContinerKeys[n] = iKey
n++
break
}
iIter.hasNext = iIter.iter.Next()
} else if targetKey > iKey {
iIter.hasNext = iIter.iter.Next()
}
}
}
for i := 0; i < n; i++ {
b.Containers.Remove(removeContinerKeys[i])
}
target.Containers.Repair()
}
func (c *Container) differenceInPlace(other *Container) {
if other.isArray() {
for _, v := range other.array() {
c.remove(v)
}
} else if other.isBitmap() {
for i, word := range other.bitmap() {
for word != 0 {
t := word & -word
c.remove(uint16((i*64 + int(popcount(t-1)))))
word ^= t
}
}
} else if other.isRun() {
for _, r := range other.runs() {
for v := int(r.start); v <= int(r.last); v++ {
c.remove(uint16(v))
}
}
}
}
func (c *Container) differenceInPlaceO(other *Container) {
if other.isArray() {
if c.isArray() {
differenceArrayArrayInPlace(c, other)
} else if c.isBitmap() {
differenceBitmapArrayInPlace(c, other)
} else if c.isRun() {
differenceRunArrayInPlace(c, other)
}
} else if other.isBitmap() {
if c.isArray() {
differenceArrayBitmapInPlace(c, other)
} else if c.isBitmap() {
differenceBitmapBitmapInPlace(c, other)
} else if c.isRun() {
differenceRunBitmapInPlace(c, other)
}
} else if other.isRun() {
if c.isArray() {
differenceArrayRunInPlace(c, other)
} else if c.isBitmap() {
differenceBitmapRunInPlace(c, other)
} else if c.isRun() {
differenceRunRunInPlace(c, other)
}
}
}
func differenceArrayArrayInPlace(output, other *Container) {
statsHit("differenceInPlace/ArrayArray")
aa, ab := output.array(), other.array()
na, nb := len(aa), len(ab)
n := 0
for i, j := 0, 0; i < na; {
va := aa[i]
if j >= nb {
aa[n] = va
n++
i++
continue
}
vb := ab[j]
if va < vb {
aa[n] = va
n++
i++
} else if va > vb {
j++
} else {
i, j = i+1, j+1
}
}
aa = aa[:n]
output.setArray(aa)
}
func differenceArrayBitmapInPlace(c, other *Container) {
statsHit("differenceInPlace/ArrayBitmap")
aa := c.array()
n := 0
bitmap := other.bitmap()
for _, va := range aa {
bmidx := va / 64
bidx := va % 64
mask := uint64(1) << bidx
b := bitmap[bmidx]
if mask&^b > 0 {
aa[n] = va
n++
}
}
aa = aa[:n]
c.setArray(aa)
}
func differenceArrayRunInPlace(c, other *Container) {
statsHit("differenceInPlace/ArrayRun")
i := 0 // array index
j := 0 // run index
aa, rb := c.array(), other.runs()
if len(aa) == 0 || len(rb) == 0 {
return
}
n := 0
// handle overlap
for i < len(aa) {
// keep all array elements before beginning of runs
if aa[i] < rb[j].start {
aa[n] = aa[i]
n++
i++
continue
}
// if array element in run, skip it
if aa[i] >= rb[j].start && aa[i] <= rb[j].last {
i++
continue
}
// if array element larger than current run, check next run
if aa[i] > rb[j].last {
j++
if j == len(rb) {
break
}
}
}
for ; i < len(aa); i++ {
aa[n] = aa[i]
n++
}
aa = aa[:n]
c.setArray(aa)
}
func differenceBitmapArrayInPlace(c, other *Container) {
statsHit("differenceInPlace/BitmapArray")
bitmap := c.bitmap()
n := c.N()
for _, v := range other.array() {
if c.bitmapContains(v) {
bitmap[v/64] &^= (uint64(1) << uint(v%64))
n--
}
}
c.setN(n)
if n < ArrayMaxSize {
c.bitmapToArray() // With This Work
}
}
func differenceBitmapBitmapInPlace(c, other *Container) {
statsHit("differenceInPlace/BitmapBitmap")
// local variables added to prevent BCE checks in loop
// see https://go101.org/article/bounds-check-elimination.html
var (
ab = c.bitmap()[:bitmapN]
bb = other.bitmap()[:bitmapN]
n int32
)
for i := 0; i < bitmapN; i++ {
ab[i] = ab[i] & (^bb[i])
n += int32(popcount(ab[i]))
}
c.setN(n)
if n < ArrayMaxSize {
c.bitmapToArray() // Will this work?
}
}
func differenceBitmapRunInPlace(c, other *Container) {
statsHit("differenceInPlace/BitmapRun")
for _, run := range other.runs() {
c.bitmapZeroRange(uint64(run.start), uint64(run.last)+1)
}
}
func differenceRunArrayInPlace(c, other *Container) {
statsHit("differenceInPlace/RunArray")
ra, ab := c.runs(), other.array()
if len(ab) == 0 {
return
}
runs := make([]interval16, 0, len(ra))
bidx := 0
vb := ab[bidx]
RUNLOOP:
for _, run := range ra {
start := run.start
for vb < run.start {
bidx++
if bidx >= len(ab) {
break
}
vb = ab[bidx]
}
for vb >= run.start && vb <= run.last {
if vb == start {
if vb == 65535 { // overflow
break RUNLOOP
}
start++
bidx++
if bidx >= len(ab) {
break
}
vb = ab[bidx]
continue
}
runs = append(runs, interval16{start: start, last: vb - 1})
if vb == 65535 { // overflow
break RUNLOOP
}
start = vb + 1
bidx++
if bidx >= len(ab) {
break
}
vb = ab[bidx]
}
if start <= run.last {
runs = append(runs, interval16{start: start, last: run.last})
}
}
c.setRuns(runs)
c.n = 0
for _, run := range runs {
c.n += int32(run.last-run.start) + 1
}
c.optimize()
}
func differenceRunBitmapInPlace(c, other *Container) {
statsHit("differenceInPlace/RunBitmap")
ra := c.runs()
// If a is full, difference is the flip of b.
if len(ra) > 0 && ra[0].start == 0 && ra[0].last == 65535 {
clone := other.Clone()
bitmap := clone.bitmap()
for i, word := range other.bitmap() {
bitmap[i] = ^word
}
c.setTyp(containerBitmap)
c.setMapped(false)
c.setBitmap(bitmap)
c.setN(c.count())
return
}
runs := make([]interval16, 0, len(ra))
for _, inputRun := range ra {
run := inputRun
add := true
for bit := inputRun.start; bit <= inputRun.last; bit++ {
if other.bitmapContains(bit) {
if run.start == bit {
if bit == 65535 { //overflow
add = false
}
run.start++
} else if bit == run.last {
run.last--
} else {
run.last = bit - 1
if run.last >= run.start {
runs = append(runs, run)
}
run.start = bit + 1
run.last = inputRun.last
}
if run.start > run.last {
break
}
}
if bit == 65535 { //overflow
break
}
}
if run.start <= run.last {
if add {
runs = append(runs, run)
}
}
}
c.setRuns(runs)
c.n = 0
for _, run := range runs {
c.n += int32(run.last-run.start) + 1
}
if c.N() < ArrayMaxSize && int32(len(runs)) > c.N()/2 {
c.runToArray()
} else if len(runs) > runMaxSize {
c.runToBitmap()
}
}
func differenceRunRunInPlace(c, other *Container) {
statsHit("differenceInPlace/RunRun")
ra, rb := c.runs(), other.runs()
if len(ra) == 0 || len(rb) == 0 {
return
}
apos := 0 // current a-run index
bpos := 0 // current b-run index
astart := ra[apos].start
alast := ra[apos].last
bstart := rb[bpos].start
blast := rb[bpos].last
alen := len(ra)
blen := len(rb)
runs := make([]interval16, 0, alen+blen) // TODO allocate max then truncate? or something else
// cardinality upper bound: sum of number of runs
// each B-run could split an A-run in two, up to len(b.runs) times
for apos < alen && bpos < blen {
switch {
case alast < bstart:
// current A-run entirely precedes current B-run: keep full A-run, advance to next A-run
runs = append(runs, interval16{start: astart, last: alast})
apos++
if apos < alen {
astart = ra[apos].start
alast = ra[apos].last
}
case blast < astart:
// current B-run entirely precedes current A-run: advance to next B-run
bpos++
if bpos < blen {
bstart = rb[bpos].start
blast = rb[bpos].last
}
default:
// overlap
if astart < bstart {
runs = append(runs, interval16{start: astart, last: bstart - 1})
}
if alast > blast {
astart = blast + 1
} else {
apos++
if apos < alen {
astart = ra[apos].start
alast = ra[apos].last
}
}
}
}
if apos < alen {
runs = append(runs, interval16{start: astart, last: alast})
apos++
if apos < alen {
runs = append(runs, ra[apos:]...)
}
}
c.setRuns(runs)
c.n = 0
for _, run := range runs {
c.n += int32(run.last-run.start) + 1
}
}

View file

@ -2709,6 +2709,14 @@ func unionInPlaceWrapper(a, b *Container) *Container {
out.UnionInPlace(B)
return out.Containers.Get(0)
}
func differenceInPlaceWrapper(a, b *Container) *Container {
out := NewBitmap()
out.Containers.Put(0, a.Clone())
B := NewBitmap()
B.Containers.Put(0, b)
out.DifferenceInPlace(B)
return out.Containers.Get(0)
}
func TestContainerCombinations(t *testing.T) {
@ -3283,6 +3291,120 @@ func TestContainerCombinations(t *testing.T) {
{flip, "outerBitsSet", "", "innerBitsSet"},
{flip, "oddBitsSet", "", "evenBitsSet"},
{flip, "evenBitsSet", "", "oddBitsSet"},
// differenceInPlace
{differenceInPlaceWrapper, "empty", "empty", "empty"},
{differenceInPlaceWrapper, "empty", "full", "empty"},
{differenceInPlaceWrapper, "empty", "firstBitSet", "empty"},
{differenceInPlaceWrapper, "empty", "lastBitSet", "empty"},
{differenceInPlaceWrapper, "empty", "firstBitUnset", "empty"},
{differenceInPlaceWrapper, "empty", "lastBitUnset", "empty"},
{differenceInPlaceWrapper, "empty", "innerBitsSet", "empty"},
{differenceInPlaceWrapper, "empty", "outerBitsSet", "empty"},
{differenceInPlaceWrapper, "empty", "oddBitsSet", "empty"},
{differenceInPlaceWrapper, "empty", "evenBitsSet", "empty"},
//
{differenceInPlaceWrapper, "full", "empty", "full"},
{differenceInPlaceWrapper, "full", "full", "empty"},
{differenceInPlaceWrapper, "full", "firstBitSet", "firstBitUnset"},
{differenceInPlaceWrapper, "full", "lastBitSet", "lastBitUnset"},
{differenceInPlaceWrapper, "full", "firstBitUnset", "firstBitSet"},
{differenceInPlaceWrapper, "full", "lastBitUnset", "lastBitSet"},
{differenceInPlaceWrapper, "full", "innerBitsSet", "outerBitsSet"},
{differenceInPlaceWrapper, "full", "outerBitsSet", "innerBitsSet"},
{differenceInPlaceWrapper, "full", "oddBitsSet", "evenBitsSet"},
{differenceInPlaceWrapper, "full", "evenBitsSet", "oddBitsSet"},
//
{differenceInPlaceWrapper, "firstBitSet", "empty", "firstBitSet"},
{differenceInPlaceWrapper, "firstBitSet", "full", "empty"},
{differenceInPlaceWrapper, "firstBitSet", "firstBitSet", "empty"},
{differenceInPlaceWrapper, "firstBitSet", "lastBitSet", "firstBitSet"},
{differenceInPlaceWrapper, "firstBitSet", "firstBitUnset", "firstBitSet"},
{differenceInPlaceWrapper, "firstBitSet", "lastBitUnset", "empty"},
{differenceInPlaceWrapper, "firstBitSet", "innerBitsSet", "firstBitSet"},
{differenceInPlaceWrapper, "firstBitSet", "outerBitsSet", "empty"},
{differenceInPlaceWrapper, "firstBitSet", "oddBitsSet", "firstBitSet"},
{differenceInPlaceWrapper, "firstBitSet", "evenBitsSet", "empty"},
//
{differenceInPlaceWrapper, "lastBitSet", "empty", "lastBitSet"},
{differenceInPlaceWrapper, "lastBitSet", "full", "empty"},
{differenceInPlaceWrapper, "lastBitSet", "firstBitSet", "lastBitSet"},
{differenceInPlaceWrapper, "lastBitSet", "lastBitSet", "empty"},
{differenceInPlaceWrapper, "lastBitSet", "firstBitUnset", "empty"},
{differenceInPlaceWrapper, "lastBitSet", "lastBitUnset", "lastBitSet"},
{differenceInPlaceWrapper, "lastBitSet", "innerBitsSet", "lastBitSet"},
{differenceInPlaceWrapper, "lastBitSet", "outerBitsSet", "empty"},
{differenceInPlaceWrapper, "lastBitSet", "oddBitsSet", "empty"},
{differenceInPlaceWrapper, "lastBitSet", "evenBitsSet", "lastBitSet"},
//
{differenceInPlaceWrapper, "firstBitUnset", "empty", "firstBitUnset"},
{differenceInPlaceWrapper, "firstBitUnset", "full", "empty"},
{differenceInPlaceWrapper, "firstBitUnset", "firstBitSet", "firstBitUnset"},
{differenceInPlaceWrapper, "firstBitUnset", "lastBitSet", "innerBitsSet"},
{differenceInPlaceWrapper, "firstBitUnset", "firstBitUnset", "empty"},
{differenceInPlaceWrapper, "firstBitUnset", "lastBitUnset", "lastBitSet"},
{differenceInPlaceWrapper, "firstBitUnset", "innerBitsSet", "lastBitSet"},
{differenceInPlaceWrapper, "firstBitUnset", "outerBitsSet", "innerBitsSet"},
//{differenceInPlaceWrapper, "firstBitUnset", "oddBitsSet", ""},
{differenceInPlaceWrapper, "firstBitUnset", "evenBitsSet", "oddBitsSet"},
//
{differenceInPlaceWrapper, "lastBitUnset", "empty", "lastBitUnset"},
{differenceInPlaceWrapper, "lastBitUnset", "full", "empty"},
{differenceInPlaceWrapper, "lastBitUnset", "firstBitSet", "innerBitsSet"},
{differenceInPlaceWrapper, "lastBitUnset", "lastBitSet", "lastBitUnset"},
{differenceInPlaceWrapper, "lastBitUnset", "firstBitUnset", "firstBitSet"},
{differenceInPlaceWrapper, "lastBitUnset", "lastBitUnset", "empty"},
{differenceInPlaceWrapper, "lastBitUnset", "innerBitsSet", "firstBitSet"},
{differenceInPlaceWrapper, "lastBitUnset", "outerBitsSet", "innerBitsSet"},
{differenceInPlaceWrapper, "lastBitUnset", "oddBitsSet", "evenBitsSet"},
//{differenceInPlaceWrapper, "lastBitUnset", "evenBitsSet", ""},
//
{differenceInPlaceWrapper, "innerBitsSet", "empty", "innerBitsSet"},
{differenceInPlaceWrapper, "innerBitsSet", "full", "empty"},
{differenceInPlaceWrapper, "innerBitsSet", "firstBitSet", "innerBitsSet"},
{differenceInPlaceWrapper, "innerBitsSet", "lastBitSet", "innerBitsSet"},
{differenceInPlaceWrapper, "innerBitsSet", "firstBitUnset", "empty"},
{differenceInPlaceWrapper, "innerBitsSet", "lastBitUnset", "empty"},
{differenceInPlaceWrapper, "innerBitsSet", "innerBitsSet", "empty"},
{differenceInPlaceWrapper, "innerBitsSet", "outerBitsSet", "innerBitsSet"},
//{differenceInPlaceWrapper, "innerBitsSet", "oddBitsSet", ""},
//{differenceInPlaceWrapper, "innerBitsSet", "evenBitsSet", ""},
//
{differenceInPlaceWrapper, "outerBitsSet", "empty", "outerBitsSet"},
{differenceInPlaceWrapper, "outerBitsSet", "full", "empty"},
{differenceInPlaceWrapper, "outerBitsSet", "firstBitSet", "lastBitSet"},
{differenceInPlaceWrapper, "outerBitsSet", "lastBitSet", "firstBitSet"},
{differenceInPlaceWrapper, "outerBitsSet", "firstBitUnset", "firstBitSet"},
{differenceInPlaceWrapper, "outerBitsSet", "lastBitUnset", "lastBitSet"},
{differenceInPlaceWrapper, "outerBitsSet", "innerBitsSet", "outerBitsSet"},
{differenceInPlaceWrapper, "outerBitsSet", "outerBitsSet", "empty"},
{differenceInPlaceWrapper, "outerBitsSet", "oddBitsSet", "firstBitSet"},
{differenceInPlaceWrapper, "outerBitsSet", "evenBitsSet", "lastBitSet"},
//
{differenceInPlaceWrapper, "oddBitsSet", "empty", "oddBitsSet"},
{differenceInPlaceWrapper, "oddBitsSet", "full", "empty"},
{differenceInPlaceWrapper, "oddBitsSet", "firstBitSet", "oddBitsSet"},
//{differenceInPlaceWrapper, "oddBitsSet", "lastBitSet", ""},
{differenceInPlaceWrapper, "oddBitsSet", "firstBitUnset", "empty"},
{differenceInPlaceWrapper, "oddBitsSet", "lastBitUnset", "lastBitSet"},
{differenceInPlaceWrapper, "oddBitsSet", "innerBitsSet", "lastBitSet"},
//{difference, "oddBitsSet", "outerBitsSet", ""},
{differenceInPlaceWrapper, "oddBitsSet", "oddBitsSet", "empty"},
{differenceInPlaceWrapper, "oddBitsSet", "evenBitsSet", "oddBitsSet"},
//
{differenceInPlaceWrapper, "evenBitsSet", "empty", "evenBitsSet"},
{differenceInPlaceWrapper, "evenBitsSet", "full", "empty"},
//{difference, "evenBitsSet", "firstBitSet", ""},
{differenceInPlaceWrapper, "evenBitsSet", "lastBitSet", "evenBitsSet"},
{differenceInPlaceWrapper, "evenBitsSet", "firstBitUnset", "firstBitSet"},
{differenceInPlaceWrapper, "evenBitsSet", "lastBitUnset", "empty"},
{differenceInPlaceWrapper, "evenBitsSet", "innerBitsSet", "firstBitSet"},
//{difference, "evenBitsSet", "outerBitsSet", ""},
{differenceInPlaceWrapper, "evenBitsSet", "oddBitsSet", "evenBitsSet"},
{differenceInPlaceWrapper, "evenBitsSet", "evenBitsSet", "empty"},
/*
*/
}
for _, testOp := range testOps {
for _, x := range containerTypes {
@ -3896,3 +4018,14 @@ func TestBitmapAny(t *testing.T) {
t.Error("shouldn't be any left")
}
}
func TestStuff(t *testing.T) {
a := doContainer(containerRun, runFull())
b := doContainer(containerBitmap, bitmapFull())
// b := doContainer(containerBitmap, bitmapLastBitSet())
r := differenceInPlaceWrapper(a, b)
if r.N() != 0 {
t.Error("fail")
}
}

View file

@ -1739,3 +1739,51 @@ func BenchmarkUnionBulk(b *testing.B) {
UnionInPlace(data.a1, data.a2, data.b, data.r1, data.r2)
}
}
func TestBitmap_differenceInPlace(t *testing.T) {
bm := testBM()
//the array
arraybm := roaring.NewSliceBitmap()
bitmapbm := roaring.NewSliceBitmap()
smallrunbm := roaring.NewSliceBitmap()
largerunbm := roaring.NewSliceBitmap()
for i := uint64(0); i < 1024; i += 4 {
_, _ = arraybm.Add((1 << 16) + i)
}
arraybm.Optimize()
for i := uint64(0); i < 16384; i += 2 {
_, _ = bitmapbm.Add((2 << 16) + i)
}
bitmapbm.Optimize()
//small run
for i := uint64(0); i < 1024; i++ {
_, _ = smallrunbm.Add((3 << 16) + i)
}
smallrunbm.Optimize()
for i := uint64(0); i < 65535; i++ {
_, _ = largerunbm.Add((4 << 16) + i)
}
smallrunbm.Optimize()
bm.DifferenceInPlace(arraybm, bitmapbm, smallrunbm, largerunbm)
if bm.Count() != 0 {
t.Fatalf("expected bitmap to be empty, it wasn't. %d", bm.Count())
}
bm = testBM()
bm.DifferenceInPlace(bitmapbm, smallrunbm, largerunbm)
if bm.Count() != 256 {
t.Fatalf("expected bitmap to have 256, it wasn't. %d", bm.Count())
}
}
func BenchmarkDifferencInPlace(b *testing.B) {
data := getBenchData(b)
for n := 0; n < b.N; n++ {
bm := roaring.NewBitmap()
bm.
UnionInPlace(data.a1, data.a2, data.b, data.r1, data.r2)
bm.
DifferenceInPlace(data.a1, data.r2, data.b, data.r1)
}
}