@@ -3,6 +3,7 @@ package roaring
33import (
44 "bytes"
55 "fmt"
6+ "github.com/stretchr/testify/require"
67 "math/rand"
78 "runtime"
89 "testing"
@@ -235,6 +236,75 @@ func BenchmarkUnionRoaring(b *testing.B) {
235236 }
236237}
237238
239+ // BenchmarkUnionInPlaceCopyOnWrite tests the performance of bitmap.Or()
240+ // when the bitmap was generated via FromBuffer.
241+ // In this case all left containers need to be copied in order to be updated.
242+ // The nested for-loops test a number of different scenarios
243+ // with respect to the ranges and densities of bitmaps.
244+ func BenchmarkUnionInPlaceCopyOnWrite (b * testing.B ) {
245+ //uint32s to maintain 1.12 compatibility, which requires unsigned shifts.
246+ startingContainerPower := uint32 (4 )
247+ finalContainerPower := uint32 (10 )
248+ containerIncrement := uint32 (3 )
249+ startingItemsPower := uint32 (3 )
250+ finalItemsPower := uint32 (10 )
251+ itemsIncrement := uint32 (7 )
252+ for leftContainerPower := startingContainerPower ; leftContainerPower <= finalContainerPower ; leftContainerPower += containerIncrement {
253+ for rightContainerPower := startingContainerPower ; rightContainerPower <= finalContainerPower ; rightContainerPower += containerIncrement {
254+ for leftItemsPerContainerPower := startingItemsPower ; leftItemsPerContainerPower <= finalItemsPower ; leftItemsPerContainerPower += itemsIncrement {
255+ for rightItemsPerContainerPower := startingItemsPower ; rightItemsPerContainerPower <= finalItemsPower ; rightItemsPerContainerPower += itemsIncrement {
256+ b .Run (fmt .Sprintf ("%d-%d-%d-%d" , leftContainerPower , rightContainerPower , leftItemsPerContainerPower , rightItemsPerContainerPower ),
257+ func (b * testing.B ) {
258+ leftMax := (1 << 16 ) << leftContainerPower
259+ rightMax := (1 << 16 ) << rightContainerPower
260+ leftItems := 1 << (leftContainerPower + leftItemsPerContainerPower )
261+ rightItems := 1 << (rightContainerPower + rightItemsPerContainerPower )
262+ left := make ([][]byte , 10 )
263+ right := make ([]* Bitmap , 10 )
264+ for i := 0 ; i < 10 ; i ++ {
265+ right [i ] = NewBitmap ()
266+ left [i ] = generateRandomBitmap (b , leftMax , leftItems )
267+ _ , err := right [i ].FromBuffer (generateRandomBitmap (b , rightMax , rightItems ))
268+ require .NoError (b , err )
269+ }
270+ // This tests a destructive operation, Or() so have to have a fresh bitmap per test.
271+ targetLefts := make ([]* Bitmap , b .N )
272+ for i := 0 ; i < b .N ; i ++ {
273+ targetLefts [i ] = NewBitmap ()
274+ _ , err := targetLefts [i ].FromBuffer (left [i % 10 ])
275+ require .NoError (b , err )
276+ }
277+ runActualBenchmark (b , targetLefts , right )
278+ })
279+ }
280+ }
281+ }
282+ }
283+ }
284+
285+ // runActualBenchmark is broken out primarily so you can profile the tests,
286+ // as otherwise the generation overwhelms the actual test.
287+ func runActualBenchmark (b * testing.B , targetLefts []* Bitmap , right []* Bitmap ) uint64 {
288+ b .ResetTimer ()
289+ b .ReportAllocs ()
290+ total := uint64 (0 )
291+ for i := 0 ; i < b .N ; i ++ {
292+ targetLefts [i ].Or (right [i % 10 ])
293+ total += targetLefts [i ].GetCardinality ()
294+ }
295+ return total
296+ }
297+
298+ func generateRandomBitmap (b * testing.B , max , terms int ) []byte {
299+ bitmap := NewBitmap ()
300+ for i := 0 ; i < terms ; i ++ {
301+ bitmap .Add (uint32 (rand .Intn (max )))
302+ }
303+ result , err := bitmap .ToBytes ()
304+ require .NoError (b , err )
305+ return result
306+ }
307+
238308// go test -bench BenchmarkSize -run -
239309func BenchmarkSizeBitset (b * testing.B ) {
240310 b .StopTimer ()
0 commit comments