Skip to content

Commit

Permalink
Merge pull request #354 from RoaringBitmap/dlemire/allocfreeiterator
Browse files Browse the repository at this point in the history
Introducing stack-based iterators.
  • Loading branch information
lemire authored May 19, 2022
2 parents 3bf9f29 + 3f418c4 commit 21f5fe7
Show file tree
Hide file tree
Showing 2 changed files with 141 additions and 14 deletions.
106 changes: 106 additions & 0 deletions benchmark_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,112 @@ import (

// BENCHMARKS, to run them type "go test -bench Benchmark -run -"


// go test -bench BenchmarkIteratorAlloc -benchmem -run -
func BenchmarkIteratorAlloc(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{}
expected_cardinality := 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 != expected_cardinality {
b.Fatalf("Cardinalities don't match: %d, %d", counter, expected_cardinality)
}
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 != expected_cardinality {
b.Fatalf("Cardinalities don't match: %d, %d", counter, expected_cardinality)
}
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 != expected_cardinality {
b.Fatalf("Cardinalities don't match: %d, %d", counter, expected_cardinality)
}
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 != expected_cardinality {
b.Fatalf("Cardinalities don't match: %d, %d", counter, expected_cardinality)
}


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 != expected_cardinality {
b.Fatalf("Cardinalities don't match: %d, %d", counter, expected_cardinality)
}
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 != expected_cardinality {
b.Fatalf("Cardinalities don't match: %d, %d", counter, expected_cardinality)
}
}


// go test -bench BenchmarkOrs -benchmem -run -
func BenchmarkOrs(b *testing.B) {

Expand Down
49 changes: 35 additions & 14 deletions roaring.go
Original file line number Diff line number Diff line change
Expand Up @@ -338,12 +338,17 @@ func (ii *intIterator) AdvanceIfNeeded(minval uint32) {
}
}

func newIntIterator(a *Bitmap) *intIterator {
p := new(intIterator)
// IntIterator is meant to allow you to iterate through the values of a bitmap, see Initialize(a *Bitmap)
type IntIterator = intIterator


// Initialize configures the existing iterator so that it can iterate through the values of
// the provided bitmap.
// The iteration results are undefined if the bitmap is modified (e.g., with Add or Remove).
func (p *intIterator) Initialize(a *Bitmap) {
p.pos = 0
p.highlowcontainer = &a.highlowcontainer
p.init()
return p
}

type intReverseIterator struct {
Expand Down Expand Up @@ -403,12 +408,16 @@ func (ii *intReverseIterator) Next() uint32 {
return x
}

func newIntReverseIterator(a *Bitmap) *intReverseIterator {
p := new(intReverseIterator)
// IntReverseIterator is meant to allow you to iterate through the values of a bitmap, see Initialize(a *Bitmap)
type IntReverseIterator = intReverseIterator

// Initialize configures the existing iterator so that it can iterate through the values of
// the provided bitmap.
// The iteration results are undefined if the bitmap is modified (e.g., with Add or Remove).
func (p *intReverseIterator) Initialize(a *Bitmap) {
p.highlowcontainer = &a.highlowcontainer
p.pos = a.highlowcontainer.size() - 1
p.init()
return p
}

// ManyIntIterable allows you to iterate over the values in a Bitmap
Expand Down Expand Up @@ -486,12 +495,17 @@ func (ii *manyIntIterator) NextMany64(hs64 uint64, buf []uint64) int {
return n
}

func newManyIntIterator(a *Bitmap) *manyIntIterator {
p := new(manyIntIterator)

// ManyIntIterator is meant to allow you to iterate through the values of a bitmap, see Initialize(a *Bitmap)
type ManyIntIterator = manyIntIterator

// Initialize configures the existing iterator so that it can iterate through the values of
// the provided bitmap.
// The iteration results are undefined if the bitmap is modified (e.g., with Add or Remove).
func (p *manyIntIterator) Initialize(a *Bitmap) {
p.pos = 0
p.highlowcontainer = &a.highlowcontainer
p.init()
return p
}

// String creates a string representation of the Bitmap
Expand Down Expand Up @@ -523,7 +537,7 @@ func (rb *Bitmap) String() string {
// Iterate iterates over the bitmap, calling the given callback with each value in the bitmap. If the callback returns
// false, the iteration is halted.
// The iteration results are undefined if the bitmap is modified (e.g., with Add or Remove).
// There is no guarantee as to what order the values will be iterated
// There is no guarantee as to what order the values will be iterated.
func (rb *Bitmap) Iterate(cb func(x uint32) bool) {
for i := 0; i < rb.highlowcontainer.size(); i++ {
hs := uint32(rb.highlowcontainer.getKeyAtIndex(i)) << 16
Expand Down Expand Up @@ -555,19 +569,25 @@ func (rb *Bitmap) Iterate(cb func(x uint32) bool) {
// Iterator creates a new IntPeekable to iterate over the integers contained in the bitmap, in sorted order;
// the iterator becomes invalid if the bitmap is modified (e.g., with Add or Remove).
func (rb *Bitmap) Iterator() IntPeekable {
return newIntIterator(rb)
p := new(intIterator)
p.Initialize(rb)
return p
}

// ReverseIterator creates a new IntIterable to iterate over the integers contained in the bitmap, in sorted order;
// the iterator becomes invalid if the bitmap is modified (e.g., with Add or Remove).
func (rb *Bitmap) ReverseIterator() IntIterable {
return newIntReverseIterator(rb)
p := new(intReverseIterator)
p.Initialize(rb)
return p
}

// ManyIterator creates a new ManyIntIterable to iterate over the integers contained in the bitmap, in sorted order;
// the iterator becomes invalid if the bitmap is modified (e.g., with Add or Remove).
func (rb *Bitmap) ManyIterator() ManyIntIterable {
return newManyIntIterator(rb)
p := new(manyIntIterator)
p.Initialize(rb)
return p
}

// Clone creates a copy of the Bitmap
Expand Down Expand Up @@ -988,7 +1008,8 @@ func (rb *Bitmap) IntersectsWithInterval(x, y uint64) bool {
return false
}

it := newIntIterator(rb)
it := intIterator{}
it.Initialize(rb)
it.AdvanceIfNeeded(uint32(x))
if !it.HasNext() {
return false
Expand Down

0 comments on commit 21f5fe7

Please sign in to comment.