diff --git a/bitmap.go b/bitmap.go index 1b6abf5..d2276fb 100644 --- a/bitmap.go +++ b/bitmap.go @@ -236,36 +236,6 @@ func dimensionsOf(n, m int) uint64 { // pointersOf returns a pointer to an array containing pointers to the // first element of each bitmap and the maximum length of all bitmaps -// simdBinary is one of the accelerated two-operand primitives. -type simdBinary func(dst, src unsafe.Pointer, n uint64) - -// uniformLen returns the word count shared by every source, or zero when the sources -// differ in length. The _many primitives apply a single count to all of their inputs, -// so they can only combine sources that are all the same length. -func uniformLen(other Bitmap, extra []Bitmap) int { - n := len(other) - for i := range extra { - if len(extra[i]) != n { - return 0 - } - } - return n -} - -// foldEach applies op to every source in turn, each over its own length. This is what -// ragged inputs need: a longer source would be truncated by a shared count, a shorter -// one read past its end. -func foldEach(dst *Bitmap, op simdBinary, other Bitmap, extra []Bitmap) { - if len(other) > 0 { - op(unsafe.Pointer(&(*dst)[0]), unsafe.Pointer(&other[0]), uint64(len(other))) - } - for i := range extra { - if len(extra[i]) > 0 { - op(unsafe.Pointer(&(*dst)[0]), unsafe.Pointer(&extra[i][0]), uint64(len(extra[i]))) - } - } -} - func pointersOf(other Bitmap, extra []Bitmap) (unsafe.Pointer, int) { out := make([]unsafe.Pointer, len(extra)+1) out[0] = unsafe.Pointer(&other[0]) diff --git a/bitmap_amd64.go b/bitmap_amd64.go index aa86910..1573905 100644 --- a/bitmap_amd64.go +++ b/bitmap_amd64.go @@ -83,26 +83,16 @@ func (dst *Bitmap) Or(other Bitmap, extra ...Bitmap) { case 0: _or(unsafe.Pointer(&(*dst)[0]), unsafe.Pointer(&other[0]), uint64(len(other))) default: - // _or_many applies one word count to every input, so it only fits - // sources that share a length; ragged ones are folded in one by one. - if n := uniformLen(other, extra); n > 0 { - vx, _ := pointersOf(other, extra) - _or_many(unsafe.Pointer(&(*dst)[0]), vx, dimensionsOf(n, len(extra)+1)) - } else { - foldEach(dst, _or, other, extra) - } + vx, max := pointersOf(other, extra) + _or_many(unsafe.Pointer(&(*dst)[0]), vx, dimensionsOf(max, len(extra)+1)) } case isAVX512: switch len(extra) { case 0: _or_avx512(unsafe.Pointer(&(*dst)[0]), unsafe.Pointer(&other[0]), uint64(len(other))) default: - if n := uniformLen(other, extra); n > 0 { - vx, _ := pointersOf(other, extra) - _or_many_avx512(unsafe.Pointer(&(*dst)[0]), vx, dimensionsOf(n, len(extra)+1)) - } else { - foldEach(dst, _or_avx512, other, extra) - } + vx, max := pointersOf(other, extra) + _or_many_avx512(unsafe.Pointer(&(*dst)[0]), vx, dimensionsOf(max, len(extra)+1)) } default: or(*dst, other, extra) @@ -123,26 +113,16 @@ func (dst *Bitmap) Xor(other Bitmap, extra ...Bitmap) { case 0: _xor(unsafe.Pointer(&(*dst)[0]), unsafe.Pointer(&other[0]), uint64(len(other))) default: - // _xor_many applies one word count to every input, so it only fits - // sources that share a length; ragged ones are folded in one by one. - if n := uniformLen(other, extra); n > 0 { - vx, _ := pointersOf(other, extra) - _xor_many(unsafe.Pointer(&(*dst)[0]), vx, dimensionsOf(n, len(extra)+1)) - } else { - foldEach(dst, _xor, other, extra) - } + vx, max := pointersOf(other, extra) + _xor_many(unsafe.Pointer(&(*dst)[0]), vx, dimensionsOf(max, len(extra)+1)) } case isAVX512: switch len(extra) { case 0: _xor_avx512(unsafe.Pointer(&(*dst)[0]), unsafe.Pointer(&other[0]), uint64(len(other))) default: - if n := uniformLen(other, extra); n > 0 { - vx, _ := pointersOf(other, extra) - _xor_many_avx512(unsafe.Pointer(&(*dst)[0]), vx, dimensionsOf(n, len(extra)+1)) - } else { - foldEach(dst, _xor_avx512, other, extra) - } + vx, max := pointersOf(other, extra) + _xor_many_avx512(unsafe.Pointer(&(*dst)[0]), vx, dimensionsOf(max, len(extra)+1)) } default: xor(*dst, other, extra) diff --git a/bitmap_arm64.go b/bitmap_arm64.go index 231de59..694c52d 100644 --- a/bitmap_arm64.go +++ b/bitmap_arm64.go @@ -67,14 +67,8 @@ func (dst *Bitmap) Or(other Bitmap, extra ...Bitmap) { case 0: _or(unsafe.Pointer(&(*dst)[0]), unsafe.Pointer(&other[0]), uint64(len(other))) default: - // _or_many applies one word count to every input, so it only fits - // sources that share a length; ragged ones are folded in one by one. - if n := uniformLen(other, extra); n > 0 { - vx, _ := pointersOf(other, extra) - _or_many(unsafe.Pointer(&(*dst)[0]), vx, dimensionsOf(n, len(extra)+1)) - } else { - foldEach(dst, _or, other, extra) - } + vx, max := pointersOf(other, extra) + _or_many(unsafe.Pointer(&(*dst)[0]), vx, dimensionsOf(max, len(extra)+1)) } default: or(*dst, other, extra) @@ -95,14 +89,8 @@ func (dst *Bitmap) Xor(other Bitmap, extra ...Bitmap) { case 0: _xor(unsafe.Pointer(&(*dst)[0]), unsafe.Pointer(&other[0]), uint64(len(other))) default: - // _xor_many applies one word count to every input, so it only fits - // sources that share a length; ragged ones are folded in one by one. - if n := uniformLen(other, extra); n > 0 { - vx, _ := pointersOf(other, extra) - _xor_many(unsafe.Pointer(&(*dst)[0]), vx, dimensionsOf(n, len(extra)+1)) - } else { - foldEach(dst, _xor, other, extra) - } + vx, max := pointersOf(other, extra) + _xor_many(unsafe.Pointer(&(*dst)[0]), vx, dimensionsOf(max, len(extra)+1)) } default: xor(*dst, other, extra) diff --git a/bitmap_test.go b/bitmap_test.go index 1de32fb..14ab9db 100644 --- a/bitmap_test.go +++ b/bitmap_test.go @@ -130,25 +130,6 @@ func BenchmarkMany(b *testing.B) { run(b, "and4-batch", func(index Bitmap) { index.And(other, other, other, other) }) - - run(b, "or4-batch", func(index Bitmap) { - index.Or(other, other, other, other) - }) - - run(b, "xor4-batch", func(index Bitmap) { - index.Xor(other, other, other, other) - }) - - shorter := make(Bitmap, 1000000/64/2) - shorter.Set(1000000 / 2) - - run(b, "or4-batch-ragged", func(index Bitmap) { - index.Or(other, other, shorter, other) - }) - - run(b, "xor4-batch-ragged", func(index Bitmap) { - index.Xor(other, other, shorter, other) - }) } func TestSetRemove(t *testing.T) { @@ -648,49 +629,3 @@ func testTruthTables(t *testing.T) { assert.Equal(t, 0b0110, int(a[0])) } } - -func TestOr_ExtraDifferentBitmapSizes(t *testing.T) { - // Ragged inputs: every set bit must survive regardless of which is longest. - var dst Bitmap - dst.Set(1) // word 0 - dst.Set(70) // word 1 - - var other Bitmap - other.Set(3) // word 0 - other.Set(200) // word 3, beyond every extra bitmap - - var short Bitmap - short.Set(5) // word 0 only - - var mid Bitmap - mid.Set(130) // word 2 - - dst.Or(other, short, mid) - - want := []uint32{1, 70, 3, 200, 5, 130} - for _, v := range want { - assert.True(t, dst.Contains(v), "missing bit "+strconv.Itoa(int(v))) - } - assert.Equal(t, len(want), dst.Count()) -} - -func TestXor_ExtraDifferentBitmapSizes(t *testing.T) { - var dst Bitmap - dst.Set(1) - dst.Set(200) // shared with other, must cancel out - - var other Bitmap - other.Set(200) // cancels dst's bit 200 - other.Set(201) // survives, beyond every extra bitmap - - var short Bitmap - short.Set(5) - - dst.Xor(other, short) - - assert.True(t, dst.Contains(1)) - assert.False(t, dst.Contains(200), "bit 200 should cancel") - assert.True(t, dst.Contains(201), "bit 201 from longer source must survive") - assert.True(t, dst.Contains(5)) - assert.Equal(t, 3, dst.Count()) -}