Skip to content

Commit f3ceb59

Browse files
authored
Merge pull request #527 from tamirms/master
fastaggregation: avoid runContainer16.lazyIOR slow path in FastOr
2 parents 8a91e8f + 482ea71 commit f3ceb59

2 files changed

Lines changed: 69 additions & 0 deletions

File tree

benchmark_test.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1356,3 +1356,65 @@ func BenchmarkCardinalityInRange(b *testing.B) {
13561356
}
13571357
}
13581358
}
1359+
1360+
// BenchmarkFastOrRunContainers measures FastOr over inputs containing
1361+
// runContainer16 slots (the shape AddRange and RunOptimize produce),
1362+
// exercising the runContainer16 -> bitmapContainer pre-promotion in
1363+
// (*Bitmap).lazyOR. See issue #81.
1364+
//
1365+
// go test -bench BenchmarkFastOrRunContainers -benchmem -run - -benchtime=2s
1366+
func BenchmarkFastOrRunContainers(b *testing.B) {
1367+
const numBitmaps = 15
1368+
const blocksPerBitmap = 40 // each block = 1<<16 bits
1369+
const runsPerBlock = 8
1370+
1371+
rng := rand.New(rand.NewSource(42))
1372+
bms := make([]*Bitmap, numBitmaps)
1373+
for i := 0; i < numBitmaps; i++ {
1374+
bm := NewBitmap()
1375+
for blk := 0; blk < blocksPerBitmap; blk++ {
1376+
base := uint64(blk) << 16
1377+
offset := uint64(rng.Intn(1000))
1378+
for r := 0; r < runsPerBlock; r++ {
1379+
start := base + offset + uint64(r)*8000 + uint64(i*37)
1380+
end := start + 6000
1381+
blockEnd := base + (1 << 16)
1382+
if end > blockEnd {
1383+
end = blockEnd
1384+
}
1385+
if start >= blockEnd {
1386+
break
1387+
}
1388+
bm.AddRange(start, end)
1389+
}
1390+
}
1391+
bm.RunOptimize()
1392+
bms[i] = bm
1393+
}
1394+
1395+
// Sanity-check: the workload must actually contain runContainer16,
1396+
// otherwise the bench wouldn't exercise the patched path.
1397+
hasRunContainer := false
1398+
for _, bm := range bms {
1399+
for _, c := range bm.highlowcontainer.containers {
1400+
if _, ok := c.(*runContainer16); ok {
1401+
hasRunContainer = true
1402+
break
1403+
}
1404+
}
1405+
if hasRunContainer {
1406+
break
1407+
}
1408+
}
1409+
if !hasRunContainer {
1410+
b.Fatalf("workload did not produce any runContainer16; bench would not exercise the patched path")
1411+
}
1412+
1413+
b.ResetTimer()
1414+
for n := 0; n < b.N; n++ {
1415+
res := FastOr(bms...)
1416+
if res.GetCardinality() == 0 {
1417+
b.Fatal("unexpected empty result")
1418+
}
1419+
}
1420+
}

fastaggregation.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,13 @@ main:
8181
s2 = x2.highlowcontainer.getKeyAtIndex(pos2)
8282
} else {
8383
c1 := x1.highlowcontainer.getWritableContainerAtIndex(pos1)
84+
// runContainer16.lazyIOR falls back to a slow ior path
85+
// (O(N log R) per merged element); promote to bitmapContainer
86+
// first, whose lazy union is O(1024) regardless of cardinality.
87+
// See https://github.com/RoaringBitmap/roaring/issues/81.
88+
if rc, ok := c1.(*runContainer16); ok && !rc.isFull() {
89+
c1 = rc.toBitmapContainer()
90+
}
8491
x1.highlowcontainer.containers[pos1] = c1.lazyIOR(x2.highlowcontainer.getContainerAtIndex(pos2))
8592
x1.highlowcontainer.needCopyOnWrite[pos1] = false
8693
pos1++

0 commit comments

Comments
 (0)