diff --git a/roaring/roaring.go b/roaring/roaring.go index c17c297f5..6eaad740d 100644 --- a/roaring/roaring.go +++ b/roaring/roaring.go @@ -2139,15 +2139,25 @@ func intersectArrayBitmap(a, b *Container) *Container { } func intersectBitmapBitmap(a, b *Container) *Container { - output := &Container{bitmap: make([]uint64, bitmapN), containerType: containerBitmap} - - for i := range a.bitmap { - v := a.bitmap[i] & b.bitmap[i] - output.bitmap[i] = v - output.n += int(popcount(v)) - + // local variables added to prevent BCE checks in loop + // see https://go101.org/article/bounds-check-elimination.html + var ( + ab = a.bitmap[:bitmapN] + bb = b.bitmap[:bitmapN] + buf = make([]uint64, bitmapN) + ob = buf[:bitmapN] + n int + ) + for i := 0; i < bitmapN; i++ { + ob[i] = ab[i] & bb[i] + n += int(popcount(ob[i])) + } + + output := &Container{ + bitmap: ob, + n: n, + containerType: containerBitmap, } - output.optimize() return output } @@ -2437,17 +2447,28 @@ func unionArrayBitmap(a, b *Container) *Container { } func unionBitmapBitmap(a, b *Container) *Container { - output := &Container{ - bitmap: make([]uint64, bitmapN), - containerType: containerBitmap, - } + // local variables added to prevent BCE checks in loop + // see https://go101.org/article/bounds-check-elimination.html + + var ( + ab = a.bitmap[:bitmapN] + bb = b.bitmap[:bitmapN] + buf = make([]uint64, bitmapN) + ob = buf[:bitmapN] + + n int + ) for i := 0; i < bitmapN; i++ { - v := a.bitmap[i] | b.bitmap[i] - output.bitmap[i] = v - output.n += int(popcount(v)) + ob[i] = ab[i] | bb[i] + n += int(popcount(ob[i])) } + output := &Container{ + bitmap: ob, + n: n, + containerType: containerBitmap, + } return output } @@ -2783,13 +2804,27 @@ func differenceBitmapArray(a, b *Container) *Container { } func differenceBitmapBitmap(a, b *Container) *Container { - output := &Container{bitmap: make([]uint64, bitmapN), containerType: containerBitmap} + // local variables added to prevent BCE checks in loop + // see https://go101.org/article/bounds-check-elimination.html - for i := range a.bitmap { - v := a.bitmap[i] & (^b.bitmap[i]) - output.bitmap[i] = v - output.n += int(popcount(v)) + var ( + ab = a.bitmap[:bitmapN] + bb = b.bitmap[:bitmapN] + buf = make([]uint64, bitmapN) + ob = buf[:bitmapN] + n int + ) + + for i := 0; i < bitmapN; i++ { + ob[i] = ab[i] & (^bb[i]) + n += int(popcount(ob[i])) + } + + output := &Container{ + bitmap: ob, + n: n, + containerType: containerBitmap, } if output.n < ArrayMaxSize { output.bitmapToArray() @@ -2874,16 +2909,28 @@ func xorArrayBitmap(a, b *Container) *Container { } func xorBitmapBitmap(a, b *Container) *Container { - output := &Container{ - bitmap: make([]uint64, bitmapN), - containerType: containerBitmap, - } + // local variables added to prevent BCE checks in loop + // see https://go101.org/article/bounds-check-elimination.html + + var ( + ab = a.bitmap[:bitmapN] + bb = b.bitmap[:bitmapN] + buf = make([]uint64, bitmapN) + ob = buf[:bitmapN] + + n int + ) + for i := 0; i < bitmapN; i++ { - v := a.bitmap[i] ^ b.bitmap[i] - output.bitmap[i] = v - output.n += int(popcount(v)) + ob[i] = ab[i] ^ bb[i] + n += int(popcount(ob[i])) } + output := &Container{ + bitmap: ob, + n: n, + containerType: containerBitmap, + } if output.count() < ArrayMaxSize { output.bitmapToArray() } @@ -3336,9 +3383,14 @@ func popcount(x uint64) uint64 { } func popcountAndSlice(s, m []uint64) uint64 { + var ( + a = s[:bitmapN] + b = m[:bitmapN] + ) + cnt := uint64(0) - for i := range s { - cnt += popcount(s[i] & m[i]) + for i := 0; i < bitmapN; i++ { + cnt += popcount(a[i] & b[i]) } return cnt } diff --git a/roaring/roaring_internal_test.go b/roaring/roaring_internal_test.go index b049d40cf..c77ef4ee1 100644 --- a/roaring/roaring_internal_test.go +++ b/roaring/roaring_internal_test.go @@ -784,11 +784,11 @@ func TestDifferenceMixed(t *testing.T) { t.Fatalf("test #3 expected empty but got %v", res.runs) } - c.bitmap = []uint64{0x64} + c.bitmap = MakeBitmap([]uint64{0x64}) c.n = c.countRange(0, 100) c.containerType = containerBitmap res = difference(c, a) - if !reflect.DeepEqual(res.bitmap, []uint64{0x4}) { + if !reflect.DeepEqual(res.bitmap, MakeBitmap([]uint64{0x4})) { t.Fatalf("test #4 expected %v, but got %v", []uint16{4}, res.bitmap) }