Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
164 changes: 164 additions & 0 deletions benchmark123_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
//go:build go1.23
// +build go1.23

package roaring

import (
"math/rand"
"testing"
)

func BenchmarkIterator123(b *testing.B) {
bm := NewBitmap()
domain := 100000000
count := 10000
for j := 0; j < count; j++ {
v := uint32(rand.Intn(domain))
bm.Add(v)
}
i := IntIterator{}
expectedCardinality := bm.GetCardinality()
counter := uint64(0)

b.Run("simple iteration with alloc", func(b *testing.B) {
for n := 0; n < b.N; n++ {
counter = 0
i := bm.Iterator()
for i.HasNext() {
i.Next()
counter++
}
}
b.StopTimer()
})
if counter != expectedCardinality {
b.Fatalf("Cardinalities don't match: %d, %d", counter, expectedCardinality)
}
b.Run("simple iteration", func(b *testing.B) {
for n := 0; n < b.N; n++ {
counter = 0
i.Initialize(bm)
for i.HasNext() {
i.Next()
counter++
}
}
b.StopTimer()
})
if counter != expectedCardinality {
b.Fatalf("Cardinalities don't match: %d, %d", counter, expectedCardinality)
}
b.Run("values iteration", func(b *testing.B) {
for n := 0; n < b.N; n++ {
counter = 0
Values(bm)(func(_ uint32) bool {
counter++
return true
})
}
b.StopTimer()
})
if counter != expectedCardinality {
b.Fatalf("Cardinalities don't match: %d, %d", counter, expectedCardinality)
}
b.Run("reverse iteration with alloc", func(b *testing.B) {
for n := 0; n < b.N; n++ {
counter = 0
ir := bm.ReverseIterator()
for ir.HasNext() {
ir.Next()
counter++
}
}
b.StopTimer()
})
if counter != expectedCardinality {
b.Fatalf("Cardinalities don't match: %d, %d", counter, expectedCardinality)
}
ir := IntReverseIterator{}

b.Run("reverse iteration", func(b *testing.B) {
for n := 0; n < b.N; n++ {
counter = 0
ir.Initialize(bm)
for ir.HasNext() {
ir.Next()
counter++
}
}
b.StopTimer()
})
if counter != expectedCardinality {
b.Fatalf("Cardinalities don't match: %d, %d", counter, expectedCardinality)
}
b.Run("backward iteration", func(b *testing.B) {
for n := 0; n < b.N; n++ {
counter = 0
Backward(bm)(func(_ uint32) bool {
counter++
return true
})
}
b.StopTimer()
})
if counter != expectedCardinality {
b.Fatalf("Cardinalities don't match: %d, %d", counter, expectedCardinality)
}

b.Run("many iteration with alloc", func(b *testing.B) {
for n := 0; n < b.N; n++ {
counter = 0
buf := make([]uint32, 1024)
im := bm.ManyIterator()
for n := im.NextMany(buf); n != 0; n = im.NextMany(buf) {
counter += uint64(n)
}
}
b.StopTimer()
})
if counter != expectedCardinality {
b.Fatalf("Cardinalities don't match: %d, %d", counter, expectedCardinality)
}
im := ManyIntIterator{}
buf := make([]uint32, 1024)

b.Run("many iteration", func(b *testing.B) {
for n := 0; n < b.N; n++ {
counter = 0
im.Initialize(bm)
for n := im.NextMany(buf); n != 0; n = im.NextMany(buf) {
counter += uint64(n)
}
}
b.StopTimer()
})
if counter != expectedCardinality {
b.Fatalf("Cardinalities don't match: %d, %d", counter, expectedCardinality)
}

b.Run("values iteration 1.23", func(b *testing.B) {
for n := 0; n < b.N; n++ {
counter = 0
for range Values(bm) {
counter++
}
}
b.StopTimer()
})
if counter != expectedCardinality {
b.Fatalf("Cardinalities don't match: %d, %d", counter, expectedCardinality)
}

b.Run("backward iteration 1.23", func(b *testing.B) {
for n := 0; n < b.N; n++ {
counter = 0
for range Backward(bm) {
counter++
}
}
b.StopTimer()
})
if counter != expectedCardinality {
b.Fatalf("Cardinalities don't match: %d, %d", counter, expectedCardinality)
}
}
26 changes: 26 additions & 0 deletions benchmark_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,19 @@ func BenchmarkIteratorAlloc(b *testing.B) {
if counter != expectedCardinality {
b.Fatalf("Cardinalities don't match: %d, %d", counter, expectedCardinality)
}
b.Run("values iteration", func(b *testing.B) {
for n := 0; n < b.N; n++ {
counter = 0
Values(bm)(func(_ uint32) bool {
counter++
return true
})
}
b.StopTimer()
})
if counter != expectedCardinality {
b.Fatalf("Cardinalities don't match: %d, %d", counter, expectedCardinality)
}
b.Run("reverse iteration with alloc", func(b *testing.B) {
for n := 0; n < b.N; n++ {
counter = 0
Expand Down Expand Up @@ -84,6 +97,19 @@ func BenchmarkIteratorAlloc(b *testing.B) {
if counter != expectedCardinality {
b.Fatalf("Cardinalities don't match: %d, %d", counter, expectedCardinality)
}
b.Run("backward iteration", func(b *testing.B) {
for n := 0; n < b.N; n++ {
counter = 0
Backward(bm)(func(_ uint32) bool {
counter++
return true
})
}
b.StopTimer()
})
if counter != expectedCardinality {
b.Fatalf("Cardinalities don't match: %d, %d", counter, expectedCardinality)
}

b.Run("many iteration with alloc", func(b *testing.B) {
for n := 0; n < b.N; n++ {
Expand Down
23 changes: 23 additions & 0 deletions iter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package roaring

func Values(b *Bitmap) func(func(uint32) bool) {
return func(yield func(uint32) bool) {
it := b.Iterator()
for it.HasNext() {
if !yield(it.Next()) {
return
}
}
}
}

func Backward(b *Bitmap) func(func(uint32) bool) {
return func(yield func(uint32) bool) {
it := b.ReverseIterator()
for it.HasNext() {
if !yield(it.Next()) {
return
}
}
}
}
111 changes: 111 additions & 0 deletions iter123_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
//go:build go1.23
// +build go1.23

package roaring

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestBackwardCount123(t *testing.T) {
array := []int{2, 63, 64, 65, 4095, 4096, 4097, 4159, 4160, 4161, 5000, 20000, 66666}
for _, testSize := range array {
b := New()
for i := uint32(0); i < uint32(testSize); i++ {
b.Add(i)
}

count := 0
for range Values(b) {
count++
}

assert.Equal(t, testSize, count)
}
}

func TestBackward123(t *testing.T) {
t.Run("#1", func(t *testing.T) {
values := []uint32{0, 2, 15, 16, 31, 32, 33, 9999, MaxUint16, MaxUint32}
b := New()
for n := 0; n < len(values); n++ {
b.Add(values[n])
}
n := len(values) - 1
for val := range Backward(b) {
assert.EqualValues(t, val, values[n])
n--
}
})

t.Run("#2", func(t *testing.T) {
b := New()

count := 0
for range Backward(b) {
count++
}

assert.Equal(t, 0, count)
})

t.Run("#3", func(t *testing.T) {
b := New()
b.AddInt(0)

// only one value zero
for val := range Backward(b) {
assert.EqualValues(t, 0, val)
}
})

t.Run("#4", func(t *testing.T) {
b := New()
b.AddInt(9999)

// only one value 9999
for val := range Backward(b) {
assert.EqualValues(t, 9999, val)
}
})

t.Run("#5", func(t *testing.T) {
b := New()
b.AddInt(MaxUint16)

// only one value MaxUint16
for val := range Backward(b) {
assert.EqualValues(t, MaxUint16, val)
}
})

t.Run("#6", func(t *testing.T) {
b := New()
b.AddInt(MaxUint32)
Copy link
Copy Markdown
Member

@lemire lemire Jun 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An int might be a signed 32-bit integer, so MaxUint32 can't be represented.

But when it works, you might be allocating 512 MB of memory. Please don't do this as part of our routine tests.


// only one value MaxUint32
for val := range Backward(b) {
assert.EqualValues(t, MaxUint32, val)
}
})
}

func TestValues123(t *testing.T) {
b := New()

testSize := 5000
for i := 0; i < testSize; i++ {
b.AddInt(i)
}

n := 0
for val := range Values(b) {
assert.Equal(t, uint32(n), val)
n++

}

assert.Equal(t, testSize, n)
}
Loading
Loading