Skip to content

Commit

Permalink
Merge pull request #394 from anacrolix/remove-bsi-allocs
Browse files Browse the repository at this point in the history
Remove unnecessary allocations in 64 bit BSI
  • Loading branch information
lemire authored Aug 25, 2023
2 parents f60397f + d672d44 commit 507997c
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 50 deletions.
82 changes: 38 additions & 44 deletions roaring64/bsi64.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ const (
// It depends upon the bitmap libraries. It is not thread safe, so
// upstream concurrency guards must be provided.
type BSI struct {
bA []*Bitmap
eBM *Bitmap // Existence BitMap
bA []Bitmap
eBM Bitmap // Existence BitMap
MaxValue int64
MinValue int64
runOptimized bool
Expand All @@ -40,11 +40,8 @@ func NewBSI(maxValue int64, minValue int64) *BSI {
if bits.Len64(uint64(maxValue)) > bitsz {
bitsz = bits.Len64(uint64(maxValue))
}
ba := make([]*Bitmap, bitsz)
for i := 0; i < len(ba); i++ {
ba[i] = NewBitmap()
}
return &BSI{bA: ba, eBM: NewBitmap(), MaxValue: maxValue, MinValue: minValue}
ba := make([]Bitmap, bitsz)
return &BSI{bA: ba, MaxValue: maxValue, MinValue: minValue}
}

// NewDefaultBSI constructs an auto-sized BSI
Expand All @@ -68,7 +65,7 @@ func (b *BSI) HasRunCompression() bool {

// GetExistenceBitmap returns a pointer to the underlying existence bitmap of the BSI
func (b *BSI) GetExistenceBitmap() *Bitmap {
return b.eBM
return &b.eBM
}

// ValueExists tests whether the value exists.
Expand All @@ -91,9 +88,9 @@ func (b *BSI) BitCount() int {
func (b *BSI) SetValue(columnID uint64, value int64) {
// If max/min values are set to zero then automatically determine bit array size
if b.MaxValue == 0 && b.MinValue == 0 {
ba := make([]*Bitmap, bits.Len64(uint64(value)))
for i := len(ba) - b.BitCount(); i > 0; i-- {
b.bA = append(b.bA, NewBitmap())
minBits := bits.Len64(uint64(value))
for len(b.bA) < minBits {
b.bA = append(b.bA, Bitmap{})
}
}

Expand Down Expand Up @@ -254,7 +251,7 @@ func (b *BSI) CompareValue(parallelism int, op Operation, valueOrStart, end int6

comp := &task{bsi: b, op: op, valueOrStart: valueOrStart, end: end}
if foundSet == nil {
return parallelExecutor(parallelism, comp, compareValue, b.eBM)
return parallelExecutor(parallelism, comp, compareValue, &b.eBM)
}
return parallelExecutor(parallelism, comp, compareValue, foundSet)
}
Expand Down Expand Up @@ -517,7 +514,7 @@ func (b *BSI) Sum(foundSet *Bitmap) (sum int64, count uint64) {
wg.Add(1)
go func(j int) {
defer wg.Done()
atomic.AddInt64(&sum, int64(foundSet.AndCardinality(b.bA[j])<<uint(j)))
atomic.AddInt64(&sum, int64(foundSet.AndCardinality(&b.bA[j])<<uint(j)))
}(i)
}
wg.Wait()
Expand All @@ -526,7 +523,7 @@ func (b *BSI) Sum(foundSet *Bitmap) (sum int64, count uint64) {

// Transpose calls b.IntersectAndTranspose(0, b.eBM)
func (b *BSI) Transpose() *Bitmap {
return b.IntersectAndTranspose(0, b.eBM)
return b.IntersectAndTranspose(0, &b.eBM)
}

// IntersectAndTranspose is a matrix transpose function. Return a bitmap such that the values are represented as column IDs
Expand Down Expand Up @@ -572,21 +569,18 @@ func (b *BSI) ParOr(parallelism int, bsis ...*BSI) {

// Make sure we have enough bit slices
for bits > b.BitCount() {
newBm := NewBitmap()
if b.runOptimized {
newBm.RunOptimize()
}
b.bA = append(b.bA, newBm)
bm := Bitmap{}
bm.RunOptimize()
b.bA = append(b.bA, bm)
}

a := make([][]*Bitmap, bits)
for i := range a {
a[i] = make([]*Bitmap, 0)
for _, x := range bsis {
if len(x.bA) > i {
a[i] = append(a[i], x.bA[i])
a[i] = append(a[i], &x.bA[i])
} else {
a[i] = []*Bitmap{NewBitmap()}
if b.runOptimized {
a[i][0].RunOptimize()
}
Expand All @@ -597,7 +591,7 @@ func (b *BSI) ParOr(parallelism int, bsis ...*BSI) {
// Consolidate existence bit maps
ebms := make([]*Bitmap, len(bsis))
for i := range ebms {
ebms[i] = bsis[i].eBM
ebms[i] = &bsis[i].eBM
}

// First merge all the bit slices from all bsi maps that exist in target
Expand All @@ -606,17 +600,17 @@ func (b *BSI) ParOr(parallelism int, bsis ...*BSI) {
wg.Add(1)
go func(j int) {
defer wg.Done()
x := []*Bitmap{b.bA[j]}
x := []*Bitmap{&b.bA[j]}
x = append(x, a[j]...)
b.bA[j] = ParOr(parallelism, x...)
b.bA[j] = *ParOr(parallelism, x...)
}(i)
}
wg.Wait()

// merge all the EBM maps
x := []*Bitmap{b.eBM}
x := []*Bitmap{&b.eBM}
x = append(x, ebms...)
b.eBM = ParOr(parallelism, x...)
b.eBM = *ParOr(parallelism, x...)
}

// UnmarshalBinary de-serialize a BSI. The value at bitData[0] is the EBM. Other indices are in least to most
Expand All @@ -628,7 +622,7 @@ func (b *BSI) UnmarshalBinary(bitData [][]byte) error {
continue
}
if b.BitCount() < i {
newBm := NewBitmap()
newBm := Bitmap{}
if b.runOptimized {
newBm.RunOptimize()
}
Expand All @@ -644,7 +638,7 @@ func (b *BSI) UnmarshalBinary(bitData [][]byte) error {
}
// First element of bitData is the EBM
if bitData[0] == nil {
b.eBM = NewBitmap()
b.eBM = Bitmap{}
if b.runOptimized {
b.eBM.RunOptimize()
}
Expand All @@ -667,7 +661,7 @@ func (b *BSI) ReadFrom(stream io.Reader) (p int64, err error) {
err = fmt.Errorf("reading existence bitmap: %w", err)
return
}
b.eBM = &bm
b.eBM = bm
b.bA = b.bA[:0]
for {
// This forces a new memory location to be allocated and if we're lucky it only escapes if
Expand All @@ -683,7 +677,7 @@ func (b *BSI) ReadFrom(stream io.Reader) (p int64, err error) {
err = fmt.Errorf("reading bit slice index %v: %w", len(b.bA), err)
return
}
b.bA = append(b.bA, &bm)
b.bA = append(b.bA, bm)
}
}

Expand Down Expand Up @@ -737,7 +731,7 @@ func (b *BSI) BatchEqual(parallelism int, values []int64) *Bitmap {
valMap[values[i]] = struct{}{}
}
comp := &task{bsi: b, values: valMap}
return parallelExecutor(parallelism, comp, batchEqual, b.eBM)
return parallelExecutor(parallelism, comp, batchEqual, &b.eBM)
}

func batchEqual(e *task, batch []uint64, resultsChan chan *Bitmap,
Expand Down Expand Up @@ -777,13 +771,13 @@ func (b *BSI) ClearValues(foundSet *Bitmap) {
wg.Add(1)
go func() {
defer wg.Done()
ClearBits(foundSet, b.eBM)
ClearBits(foundSet, &b.eBM)
}()
for i := 0; i < b.BitCount(); i++ {
wg.Add(1)
go func(j int) {
defer wg.Done()
ClearBits(foundSet, b.bA[j])
ClearBits(foundSet, &b.bA[j])
}(i)
}
wg.Wait()
Expand All @@ -793,19 +787,19 @@ func (b *BSI) ClearValues(foundSet *Bitmap) {
func (b *BSI) NewBSIRetainSet(foundSet *Bitmap) *BSI {

newBSI := NewBSI(b.MaxValue, b.MinValue)
newBSI.bA = make([]*Bitmap, b.BitCount())
newBSI.bA = make([]Bitmap, b.BitCount())
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
newBSI.eBM = b.eBM.Clone()
newBSI.eBM = *b.eBM.Clone()
newBSI.eBM.And(foundSet)
}()
for i := 0; i < b.BitCount(); i++ {
wg.Add(1)
go func(j int) {
defer wg.Done()
newBSI.bA[j] = b.bA[j].Clone()
newBSI.bA[j] = *b.bA[j].Clone()
newBSI.bA[j].And(foundSet)
}(i)
}
Expand All @@ -815,28 +809,28 @@ func (b *BSI) NewBSIRetainSet(foundSet *Bitmap) *BSI {

// Clone performs a deep copy of BSI contents.
func (b *BSI) Clone() *BSI {
return b.NewBSIRetainSet(b.eBM)
return b.NewBSIRetainSet(&b.eBM)
}

// Add - In-place sum the contents of another BSI with this BSI, column wise.
func (b *BSI) Add(other *BSI) {

b.eBM.Or(other.eBM)
b.eBM.Or(&other.eBM)
for i := 0; i < len(other.bA); i++ {
b.addDigit(other.bA[i], i)
b.addDigit(&other.bA[i], i)
}
}

func (b *BSI) addDigit(foundSet *Bitmap, i int) {

if i >= len(b.bA) {
b.bA = append(b.bA, NewBitmap())
b.bA = append(b.bA, Bitmap{})
}
carry := And(b.bA[i], foundSet)
carry := And(&b.bA[i], foundSet)
b.bA[i].Xor(foundSet)
if !carry.IsEmpty() {
if i+1 >= len(b.bA) {
b.bA = append(b.bA, NewBitmap())
b.bA = append(b.bA, Bitmap{})
}
b.addDigit(carry, i+1)
}
Expand Down Expand Up @@ -887,7 +881,7 @@ func (b *BSI) IncrementAll() {
}

func (b *BSI) Equals(other *BSI) bool {
if !b.eBM.Equals(other.eBM) {
if !b.eBM.Equals(&other.eBM) {
return false
}
for i := 0; i < len(b.bA) || i < len(other.bA); i++ {
Expand All @@ -900,7 +894,7 @@ func (b *BSI) Equals(other *BSI) bool {
return false
}
} else {
if !b.bA[i].Equals(other.bA[i]) {
if !b.bA[i].Equals(&other.bA[i]) {
return false
}
}
Expand Down
8 changes: 2 additions & 6 deletions roaring64/roaring64.go
Original file line number Diff line number Diff line change
Expand Up @@ -391,12 +391,8 @@ func (rb *Bitmap) ContainsInt(x int) bool {
}

// Equals returns true if the two bitmaps contain the same integers
func (rb *Bitmap) Equals(o interface{}) bool {
srb, ok := o.(*Bitmap)
if ok {
return srb.highlowcontainer.equals(rb.highlowcontainer)
}
return false
func (rb *Bitmap) Equals(srb *Bitmap) bool {
return srb.highlowcontainer.equals(rb.highlowcontainer)
}

// Add the integer x to the bitmap
Expand Down

0 comments on commit 507997c

Please sign in to comment.