featurebase/roaring/roaring_container_test.go
Seebs a2151358ba Partial implementation: Distinct() supporting set fields
Add an exported IntersectionAny() from roaring to let us quickly
check whether two containers have overlap, so we can avoid performing
intersections we don't need to when evaluating containers within
the same row as a previous match. (IntersectionCount on the whole
bitmap would imply doing up to 16 intersections even if we find a bit
right away.)

We also allow ForeignIndex to be set on set, mutex, and time fields,
since all of those could now be reasonable operands for Distinct
ops.

Not yet present: Handling time quantums, but that seems really
desireable.
2020-10-23 13:36:40 -05:00

99 lines
3.2 KiB
Go

// Copyright 2019 Pilosa Corp.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package roaring
import (
"fmt"
"testing"
)
type containerOp struct {
name string
fn func(a, b *Container)
}
var containerOps = []containerOp{
{"intersect", func(a, b *Container) { _ = intersect(a, b) }},
{"union", func(a, b *Container) { _ = union(a, b) }},
{"difference", func(a, b *Container) { _ = difference(a, b) }},
{"xor", func(a, b *Container) { _ = xor(a, b) }},
{"intersectionCount", func(a, b *Container) { _ = intersectionCount(a, b) }},
}
// Run each container type against each other container type. In an earlier
// implementation, this had a subtle bug; we generated two sets of archetypal
// containers, so the run of Array1 vs. Array4096 used list1's Array1, and
// list2's Array4096, and the run of Arary4096 vs Array1 used list1's Array4096
// and list2's Array1. This created a subtle performance glitch, because
// list1 happened to have an Array1 containing 53,127 and list2 happened to
// have an Array1 containing 3,917, which meant that the second item being
// Array1 often looked dramatically faster than the first item being Array1.
// To reduce the impact of such things, we generate 8 of each container, and
// do each test on the whole 8x8 matrix. This does mean each operation is
// being run with a container compared with itself 1/8 of the time.
func BenchmarkCtOps(b *testing.B) {
ca, err := InitContainerArchetypes()
if err != nil {
b.Fatalf("creating container archetypes: %v", err)
}
for idx1, n1 := range ContainerArchetypeNames {
ca1 := ca[idx1]
for idx2, n2 := range ContainerArchetypeNames {
base := fmt.Sprintf("%s/%s", n1, n2)
ca2 := ca[idx2]
b.Run(base, func(b *testing.B) {
for _, op := range containerOps {
b.Run(op.name, func(b *testing.B) {
for i := 0; i < b.N; i++ {
for _, c1 := range ca1 {
for _, c2 := range ca2 {
op.fn(c1, c2)
}
}
}
})
}
})
}
}
}
func TestIntersectVariants(t *testing.T) {
ca, err := InitContainerArchetypes()
if err != nil {
t.Fatalf("creating container archetypes: %v", err)
}
for idx1, n1 := range ContainerArchetypeNames {
ca1 := ca[idx1]
for idx2, n2 := range ContainerArchetypeNames {
ca2 := ca[idx2]
for i1, c1 := range ca1 {
for i2, c2 := range ca2 {
full := intersect(c1, c2)
count := intersectionCount(c1, c2)
if full.N() != count {
t.Errorf("intersecting %s[%d] and %s[%d]: container has N %d, count was %d",
n1, i1, n2, i2, full.N(), count)
}
any := intersectionAny(c1, c2)
if any != (count != 0) {
t.Errorf("intersecting %s[%d] and %s[%d]: any %t, count was %d",
n1, i1, n2, i2, any, count)
}
}
}
}
}
}