improve union-related benchmarking

Add a benchmark to test a specific case where UnionInPlace is
underperforming the naive union operation badly.

Also, the UnionBulk test was reusing a bitmap, meaning that it ended
up doing a lot of unions into a bitmap that already had all the
bits it was supposed to have. This broke a couple of other tests
in unexpected ways.

We also now use UnionInPlace in importRoaring, and test it
in the container combinations tests via a wrapper.
This commit is contained in:
Seebs 2019-03-12 15:51:22 -05:00
parent a28141c466
commit 054cb206d5
3 changed files with 177 additions and 9 deletions

View file

@ -1763,8 +1763,11 @@ func (f *fragment) importRoaring(data []byte, clear bool) error {
if clear {
bm = f.storage.Difference(bm)
} else if f.storage.Any() {
bm = f.storage.Union(bm)
} else if f.storage.Containers.Size() >= bm.Containers.Size() {
f.storage.UnionInPlace(bm)
bm = f.storage
} else {
bm.UnionInPlace(f.storage)
}
for rowID := range rowSet {

View file

@ -2699,6 +2699,18 @@ func getFunctionName(i interface{}) string {
return y[0]
}
// UnionInPlace is defined at the Bitmap level, but this wrapper lets us insert
// it into our ContainerCombinations tests so that it gets exercised on a wide
// variety of container data.
func unionInPlaceWrapper(a, b *Container) *Container {
out := NewBitmap()
out.Containers.Put(0, a.Clone())
B := NewBitmap()
B.Containers.Put(0, b)
out.UnionInPlace(B)
return out.Containers.Get(0)
}
func TestContainerCombinations(t *testing.T) {
cts := setupContainerTests()
@ -2935,6 +2947,117 @@ func TestContainerCombinations(t *testing.T) {
{union, "evenBitsSet", "oddBitsSet", "full"},
{union, "evenBitsSet", "evenBitsSet", "evenBitsSet"},
// unionInPlaceWrapper
{unionInPlaceWrapper, "empty", "empty", "empty"},
{unionInPlaceWrapper, "empty", "full", "full"},
{unionInPlaceWrapper, "empty", "firstBitSet", "firstBitSet"},
{unionInPlaceWrapper, "empty", "lastBitSet", "lastBitSet"},
{unionInPlaceWrapper, "empty", "firstBitUnset", "firstBitUnset"},
{unionInPlaceWrapper, "empty", "lastBitUnset", "lastBitUnset"},
{unionInPlaceWrapper, "empty", "innerBitsSet", "innerBitsSet"},
{unionInPlaceWrapper, "empty", "outerBitsSet", "outerBitsSet"},
{unionInPlaceWrapper, "empty", "oddBitsSet", "oddBitsSet"},
{unionInPlaceWrapper, "empty", "evenBitsSet", "evenBitsSet"},
//
{unionInPlaceWrapper, "full", "empty", "full"},
{unionInPlaceWrapper, "full", "full", "full"},
{unionInPlaceWrapper, "full", "firstBitSet", "full"},
{unionInPlaceWrapper, "full", "lastBitSet", "full"},
{unionInPlaceWrapper, "full", "firstBitUnset", "full"},
{unionInPlaceWrapper, "full", "lastBitUnset", "full"},
{unionInPlaceWrapper, "full", "innerBitsSet", "full"},
{unionInPlaceWrapper, "full", "outerBitsSet", "full"},
{unionInPlaceWrapper, "full", "oddBitsSet", "full"},
{unionInPlaceWrapper, "full", "evenBitsSet", "full"},
//
{unionInPlaceWrapper, "firstBitSet", "empty", "firstBitSet"},
{unionInPlaceWrapper, "firstBitSet", "full", "full"},
{unionInPlaceWrapper, "firstBitSet", "firstBitSet", "firstBitSet"},
{unionInPlaceWrapper, "firstBitSet", "lastBitSet", "outerBitsSet"},
{unionInPlaceWrapper, "firstBitSet", "firstBitUnset", "full"},
{unionInPlaceWrapper, "firstBitSet", "lastBitUnset", "lastBitUnset"},
{unionInPlaceWrapper, "firstBitSet", "innerBitsSet", "lastBitUnset"},
{unionInPlaceWrapper, "firstBitSet", "outerBitsSet", "outerBitsSet"},
//{unionInPlaceWrapper, "firstBitSet", "oddBitsSet", ""},
{unionInPlaceWrapper, "firstBitSet", "evenBitsSet", "evenBitsSet"},
//
{unionInPlaceWrapper, "lastBitSet", "empty", "lastBitSet"},
{unionInPlaceWrapper, "lastBitSet", "full", "full"},
{unionInPlaceWrapper, "lastBitSet", "firstBitSet", "outerBitsSet"},
{unionInPlaceWrapper, "lastBitSet", "lastBitSet", "lastBitSet"},
{unionInPlaceWrapper, "lastBitSet", "firstBitUnset", "firstBitUnset"},
{unionInPlaceWrapper, "lastBitSet", "lastBitUnset", "full"},
{unionInPlaceWrapper, "lastBitSet", "innerBitsSet", "firstBitUnset"},
{unionInPlaceWrapper, "lastBitSet", "outerBitsSet", "outerBitsSet"},
{unionInPlaceWrapper, "lastBitSet", "oddBitsSet", "oddBitsSet"},
//{unionInPlaceWrapper, "lastBitSet", "evenBitsSet", ""},
//
{unionInPlaceWrapper, "firstBitUnset", "empty", "firstBitUnset"},
{unionInPlaceWrapper, "firstBitUnset", "full", "full"},
{unionInPlaceWrapper, "firstBitUnset", "firstBitSet", "full"},
{unionInPlaceWrapper, "firstBitUnset", "lastBitSet", "firstBitUnset"},
{unionInPlaceWrapper, "firstBitUnset", "firstBitUnset", "firstBitUnset"},
{unionInPlaceWrapper, "firstBitUnset", "lastBitUnset", "full"},
{unionInPlaceWrapper, "firstBitUnset", "innerBitsSet", "firstBitUnset"},
{unionInPlaceWrapper, "firstBitUnset", "outerBitsSet", "full"},
{unionInPlaceWrapper, "firstBitUnset", "oddBitsSet", "firstBitUnset"},
{unionInPlaceWrapper, "firstBitUnset", "evenBitsSet", "full"},
//
{unionInPlaceWrapper, "lastBitUnset", "empty", "lastBitUnset"},
{unionInPlaceWrapper, "lastBitUnset", "full", "full"},
{unionInPlaceWrapper, "lastBitUnset", "firstBitSet", "lastBitUnset"},
{unionInPlaceWrapper, "lastBitUnset", "lastBitSet", "full"},
{unionInPlaceWrapper, "lastBitUnset", "firstBitUnset", "full"},
{unionInPlaceWrapper, "lastBitUnset", "lastBitUnset", "lastBitUnset"},
{unionInPlaceWrapper, "lastBitUnset", "innerBitsSet", "lastBitUnset"},
{unionInPlaceWrapper, "lastBitUnset", "outerBitsSet", "full"},
{unionInPlaceWrapper, "lastBitUnset", "oddBitsSet", "full"},
{unionInPlaceWrapper, "lastBitUnset", "evenBitsSet", "lastBitUnset"},
//
{unionInPlaceWrapper, "innerBitsSet", "empty", "innerBitsSet"},
{unionInPlaceWrapper, "innerBitsSet", "full", "full"},
{unionInPlaceWrapper, "innerBitsSet", "firstBitSet", "lastBitUnset"},
{unionInPlaceWrapper, "innerBitsSet", "lastBitSet", "firstBitUnset"},
{unionInPlaceWrapper, "innerBitsSet", "firstBitUnset", "firstBitUnset"},
{unionInPlaceWrapper, "innerBitsSet", "lastBitUnset", "lastBitUnset"},
{unionInPlaceWrapper, "innerBitsSet", "innerBitsSet", "innerBitsSet"},
{unionInPlaceWrapper, "innerBitsSet", "outerBitsSet", "full"},
{unionInPlaceWrapper, "innerBitsSet", "oddBitsSet", "firstBitUnset"},
{unionInPlaceWrapper, "innerBitsSet", "evenBitsSet", "lastBitUnset"},
//
{unionInPlaceWrapper, "outerBitsSet", "empty", "outerBitsSet"},
{unionInPlaceWrapper, "outerBitsSet", "full", "full"},
{unionInPlaceWrapper, "outerBitsSet", "firstBitSet", "outerBitsSet"},
{unionInPlaceWrapper, "outerBitsSet", "lastBitSet", "outerBitsSet"},
{unionInPlaceWrapper, "outerBitsSet", "firstBitUnset", "full"},
{unionInPlaceWrapper, "outerBitsSet", "lastBitUnset", "full"},
{unionInPlaceWrapper, "outerBitsSet", "innerBitsSet", "full"},
{unionInPlaceWrapper, "outerBitsSet", "outerBitsSet", "outerBitsSet"},
//{unionInPlaceWrapper, "outerBitsSet", "oddBitsSet", ""},
//{unionInPlaceWrapper, "outerBitsSet", "evenBitsSet", ""},
//
{unionInPlaceWrapper, "oddBitsSet", "empty", "oddBitsSet"},
{unionInPlaceWrapper, "oddBitsSet", "full", "full"},
//{unionInPlaceWrapper, "oddBitsSet", "firstBitSet", ""},
{unionInPlaceWrapper, "oddBitsSet", "lastBitSet", "oddBitsSet"},
{unionInPlaceWrapper, "oddBitsSet", "firstBitUnset", "firstBitUnset"},
{unionInPlaceWrapper, "oddBitsSet", "lastBitUnset", "full"},
{unionInPlaceWrapper, "oddBitsSet", "innerBitsSet", "firstBitUnset"},
//{unionInPlaceWrapper, "oddBitsSet", "outerBitsSet", ""},
{unionInPlaceWrapper, "oddBitsSet", "oddBitsSet", "oddBitsSet"},
{unionInPlaceWrapper, "oddBitsSet", "evenBitsSet", "full"},
//
{unionInPlaceWrapper, "evenBitsSet", "empty", "evenBitsSet"},
{unionInPlaceWrapper, "evenBitsSet", "full", "full"},
{unionInPlaceWrapper, "evenBitsSet", "firstBitSet", "evenBitsSet"},
//{unionInPlaceWrapper, "evenBitsSet", "lastBitSet", ""},
{unionInPlaceWrapper, "evenBitsSet", "firstBitUnset", "full"},
{unionInPlaceWrapper, "evenBitsSet", "lastBitUnset", "lastBitUnset"},
{unionInPlaceWrapper, "evenBitsSet", "innerBitsSet", "lastBitUnset"},
//{unionInPlaceWrapper, "evenBitsSet", "outerBitsSet", ""},
{unionInPlaceWrapper, "evenBitsSet", "oddBitsSet", "full"},
{unionInPlaceWrapper, "evenBitsSet", "evenBitsSet", "evenBitsSet"},
// difference
{difference, "empty", "empty", "empty"},
{difference, "empty", "full", "empty"},
@ -3653,3 +3776,45 @@ func TestDirectAddNVsAdd(t *testing.T) {
}
}
func BenchmarkUnionInPlaceRegression(b *testing.B) {
initial := make([]uint64, 0, 10100)
a1 := make([]uint64, 0, 10000)
a2 := make([]uint64, 0, 10000)
for i := uint64(0); i < 1<<30; i += 100000 {
initial = append(initial, i)
a1 = append(a1, i+67000)
a2 = append(a2, i/2)
}
a1BM := NewBTreeBitmap(a1...)
a2BM := NewBTreeBitmap(a2...)
b.Run("Union1", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
bm := NewBTreeBitmap(initial...)
_ = bm.Union(a1BM)
}
})
b.Run("UnionInPlace1", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
bm := NewBTreeBitmap(initial...)
bm.UnionInPlace(a1BM)
}
})
b.Run("Union2", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
bm := NewBTreeBitmap(initial...)
_ = bm.Union(a1BM, a2BM)
}
})
b.Run("UnionInPlace2", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
bm := NewBTreeBitmap(initial...)
bm.UnionInPlace(a1BM, a2BM)
}
})
}

View file

@ -1275,7 +1275,7 @@ func isAllType(b *roaring.Bitmap, typ string) bool {
return true
}
func getBenchData(b *testing.B) *benchmarkSampleData {
func getBenchData(tb testing.TB) *benchmarkSampleData {
data := &sampleData
if data.a1 == nil {
const max = (1 << 24) / 64
@ -1321,19 +1321,19 @@ func getBenchData(b *testing.B) *benchmarkSampleData {
}
if !isAllType(data.a1, "array") {
b.Fatalf("expected data.a1 to be an array, it wasn't.")
tb.Fatalf("expected data.a1 to be an array, it wasn't.")
}
if !isAllType(data.a2, "array") {
b.Fatalf("expected data.a2 to be an array, it wasn't.")
tb.Fatalf("expected data.a2 to be an array, it wasn't.")
}
if !isAllType(data.b, "bitmap") {
b.Fatalf("expected data.b to be a bitmap, it wasn't.")
tb.Fatalf("expected data.b to be a bitmap, it wasn't.")
}
if !isAllType(data.r1, "run") {
b.Fatalf("expected data.r1 to be RLE, it wasn't.")
tb.Fatalf("expected data.r1 to be RLE, it wasn't.")
}
if !isAllType(data.r2, "run") {
b.Fatalf("expected data.r2 to be RLE, it wasn't.")
tb.Fatalf("expected data.r2 to be RLE, it wasn't.")
}
return data
}
@ -1607,8 +1607,8 @@ func BenchmarkUnion(b *testing.B) {
func BenchmarkUnionBulk(b *testing.B) {
data := getBenchData(b)
bm := roaring.NewBitmap()
for n := 0; n < b.N; n++ {
bm := roaring.NewBitmap()
bm.
UnionInPlace(data.a1, data.a2, data.b, data.r1, data.r2)
}