This is an automated email from the ASF dual-hosted git repository.

zeroshade pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-go.git


The following commit(s) were added to refs/heads/main by this push:
     new 913751ce perf(bitutil): process unaligned bitmap operations by word 
(#1437)
913751ce is described below

commit 913751ce29539b4fd3d41bd1e48a5f821521a8e4
Author: Minh Vu <[email protected]>
AuthorDate: Mon Nov 2 21:22:18 2026 +0100

    perf(bitutil): process unaligned bitmap operations by word (#1437)
    
    ## What
    Process unaligned bitmap operations with direct word writes when the
    output is byte aligned.
    
    ## Why
    The current path builds word readers and a writer for each operation.
    
    ## Implementation
    - Write full words, bytes, and partial tails directly.
    - Keep the existing path for short inputs, unaligned output, and
    overlapping buffers.
    - Cover AND, OR, AND-NOT, XOR, and XNOR.
    
    ## Benchmark
    AND only, input offsets 1 and 3, byte-aligned output. Apple M1 Pro, Go
    1.26.3, median of 3 runs:
    - **32 bytes:** 277 → 24.4 ns (**11.4x faster**).
    - **512 bytes:** 1,174 → 248 ns (**4.7x faster**).
    - **8 KiB:** 9.13 → 4.96 µs (**1.8x faster**).
    - Allocations for these cases: **192 → 0 B/op**.
    
    An unaligned-output fallback control at 512 bytes measured 1,269 → 1,339
    ns; an alternating comparison measured 856 → 861 ns. These are bitmap
    operation benchmarks, not end-to-end Arrow results.
    
    `GOMAXPROCS=1 go test ./arrow/bitutil -run '^$' -bench
    
'^BenchmarkBitmapUnalignedOps/(32B|512B|8192B)/(left1-right3-out0|left1-right3-out1)/(tail0|tail3)/and$'
    -benchmem -benchtime=150ms -count=3 -cpu=1`
    
    ---------
    
    Signed-off-by: Minh Vu <[email protected]>
---
 arrow/bitutil/bitmaps.go      |  77 ++++++++++++++++++++++
 arrow/bitutil/bitmaps_test.go | 149 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 226 insertions(+)

diff --git a/arrow/bitutil/bitmaps.go b/arrow/bitutil/bitmaps.go
index bcd20716..0b29318a 100644
--- a/arrow/bitutil/bitmaps.go
+++ b/arrow/bitutil/bitmaps.go
@@ -710,7 +710,84 @@ func alignedBitmapOp(op bitOp, left, right []byte, 
lOffset, rOffset int64, out [
        }
 }
 
+// bitmapByteRangesOverlap reports whether two byte slices share any addressed 
bytes.
+// A bitmap op may write into either input, so the direct word loop is only 
used
+// when writes cannot overwrite unread source bytes.
+func bitmapByteRangesOverlap(a, b []byte) bool {
+       if len(a) == 0 || len(b) == 0 {
+               return false
+       }
+       aPtr := uintptr(unsafe.Pointer(&a[0]))
+       bPtr := uintptr(unsafe.Pointer(&b[0]))
+       if aPtr < bPtr {
+               return bPtr-aPtr < uintptr(len(a))
+       }
+       return aPtr-bPtr < uintptr(len(b))
+}
+
 func unalignedBitmapOp(op bitOp, left, right []byte, lOffset, rOffset int64, 
out []byte, outOffset int64, length int64) {
+       // A byte-aligned output can be written directly in 64-bit words.
+       // Avoid constructing word readers and a writer for larger, non-aliasing
+       // slices. Keep the existing path for overlapping buffers, short inputs,
+       // and destinations with a bit offset.
+       // Require at least two 64-bit words to amortize the extra checks.
+       if outOffset%8 == 0 && length >= 128 {
+               leftStart, rightStart := lOffset/8, rOffset/8
+               outStart := outOffset / 8
+               leftBytes := left[leftStart : 
leftStart+BytesForBits(lOffset%8+length)]
+               rightBytes := right[rightStart : 
rightStart+BytesForBits(rOffset%8+length)]
+               outBytes := out[outStart : outStart+BytesForBits(length)]
+               if !bitmapByteRangesOverlap(outBytes, leftBytes) &&
+                       !bitmapByteRangesOverlap(outBytes, rightBytes) {
+                       leftShift, rightShift := uint(lOffset%8), 
uint(rOffset%8)
+                       nwords := length / 64
+                       for i := int64(0); i < nwords; i++ {
+                               lword := binary.LittleEndian.Uint64(leftBytes)
+                               if leftShift != 0 {
+                                       lword = (lword >> leftShift) | 
uint64(leftBytes[8])<<(64-leftShift)
+                               }
+                               rword := binary.LittleEndian.Uint64(rightBytes)
+                               if rightShift != 0 {
+                                       rword = (rword >> rightShift) | 
uint64(rightBytes[8])<<(64-rightShift)
+                               }
+                               binary.LittleEndian.PutUint64(outBytes, 
op.opWord(lword, rword))
+                               leftBytes = leftBytes[8:]
+                               rightBytes = rightBytes[8:]
+                               outBytes = outBytes[8:]
+                       }
+                       // Complete bytes at the end also avoid the 
reader/writer path.
+                       remaining := length % 64
+                       for remaining >= 8 {
+                               lbyte := leftBytes[0] >> leftShift
+                               if leftShift != 0 {
+                                       lbyte |= leftBytes[1] << (8 - leftShift)
+                               }
+                               rbyte := rightBytes[0] >> rightShift
+                               if rightShift != 0 {
+                                       rbyte |= rightBytes[1] << (8 - 
rightShift)
+                               }
+                               outBytes[0] = op.opByte(lbyte, rbyte)
+                               leftBytes = leftBytes[1:]
+                               rightBytes = rightBytes[1:]
+                               outBytes = outBytes[1:]
+                               remaining -= 8
+                       }
+                       if remaining != 0 {
+                               lbyte := leftBytes[0] >> leftShift
+                               if int64(leftShift)+remaining > 8 {
+                                       lbyte |= leftBytes[1] << (8 - leftShift)
+                               }
+                               rbyte := rightBytes[0] >> rightShift
+                               if int64(rightShift)+remaining > 8 {
+                                       rbyte |= rightBytes[1] << (8 - 
rightShift)
+                               }
+                               mask := byte((1 << uint(remaining)) - 1)
+                               outBytes[0] = (outBytes[0] &^ mask) | 
(op.opByte(lbyte, rbyte) & mask)
+                       }
+                       return
+               }
+       }
+
        leftRdr := NewBitmapWordReader(left, int(lOffset), int(length))
        rightRdr := NewBitmapWordReader(right, int(rOffset), int(length))
        writer := NewBitmapWordWriter(out, int(outOffset), int(length))
diff --git a/arrow/bitutil/bitmaps_test.go b/arrow/bitutil/bitmaps_test.go
index 2936485b..b86c1c99 100644
--- a/arrow/bitutil/bitmaps_test.go
+++ b/arrow/bitutil/bitmaps_test.go
@@ -964,3 +964,152 @@ func TestBitmapTransferZeroLengthNil(t *testing.T) {
        bitutil.CopyBitmap(nil, 0, 0, nil, 0)
        bitutil.InvertBitmap(nil, 0, 0, nil, 0)
 }
+
+type bitmapBinaryOp struct {
+       name string
+       run  func([]byte, []byte, int64, int64, []byte, int64, int64)
+       want func(bool, bool) bool
+}
+
+var unalignedBitmapOps = []bitmapBinaryOp{
+       {"and", bitutil.BitmapAnd, func(a, b bool) bool { return a && b }},
+       {"or", bitutil.BitmapOr, func(a, b bool) bool { return a || b }},
+       {"and-not", bitutil.BitmapAndNot, func(a, b bool) bool { return a && !b 
}},
+       {"xor", bitutil.BitmapXor, func(a, b bool) bool { return a != b }},
+       {"xnor", bitutil.BitmapXnor, func(a, b bool) bool { return a == b }},
+}
+
+// Use reproducible mixed bit patterns without adding a random source 
dependency.
+func fillUnalignedBitmapPattern(data []byte, seed uint32) {
+       x := seed | 1
+       for i := range data {
+               x ^= x << 13
+               x ^= x >> 17
+               x ^= x << 5
+               data[i] = byte(x)
+       }
+}
+
+func TestBitmapUnalignedOpsDirectWords(t *testing.T) {
+       lengths := []int64{0, 1, 2, 7, 8, 9, 15, 16, 31, 32, 63, 64, 65,
+               127, 128, 129, 191, 192, 193, 255, 256, 257, 511, 512, 513, 
1025}
+
+       for _, length := range lengths {
+               for leftOffset := int64(0); leftOffset < 8; leftOffset++ {
+                       for rightOffset := int64(0); rightOffset < 8; 
rightOffset++ {
+                               left := make([]byte, (leftOffset+length+7)/8+1)
+                               right := make([]byte, 
(rightOffset+length+7)/8+1)
+                               fillUnalignedBitmapPattern(left, 
uint32(123+length+leftOffset))
+                               fillUnalignedBitmapPattern(right, 
uint32(456+length+rightOffset))
+                               for _, outOffset := range []int64{0, 1, 7} {
+                                       for _, op := range unalignedBitmapOps {
+                                               out := make([]byte, 
(outOffset+length+7)/8+1)
+                                               fillUnalignedBitmapPattern(out, 
uint32(789+length+outOffset))
+                                               want := bytes.Clone(out)
+                                               for i := int64(0); i < length; 
i++ {
+                                                       a := 
bitutil.BitIsSet(left, int(leftOffset+i))
+                                                       b := 
bitutil.BitIsSet(right, int(rightOffset+i))
+                                                       bitutil.SetBitTo(want, 
int(outOffset+i), op.want(a, b))
+                                               }
+                                               op.run(left, right, leftOffset, 
rightOffset, out, outOffset, length)
+                                               if !bytes.Equal(out, want) {
+                                                       t.Fatalf("%s length=%d 
offsets=%d/%d/%d: got %x, want %x",
+                                                               op.name, 
length, leftOffset, rightOffset, outOffset, out, want)
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+}
+
+func TestBitmapUnalignedOpsAliasedOutput(t *testing.T) {
+       const length int64 = 263
+       // The direct path must fall back when the output aliases either input.
+       for _, op := range unalignedBitmapOps {
+               for _, aliasLeft := range []bool{true, false} {
+                       left := make([]byte, (length+7)/8+1)
+                       right := make([]byte, (length+3+7)/8+1)
+                       fillUnalignedBitmapPattern(left, 12345)
+                       fillUnalignedBitmapPattern(right, 67890)
+
+                       var out []byte
+                       var leftOffset, rightOffset int64
+                       if aliasLeft {
+                               out, leftOffset, rightOffset = left, 0, 3
+                       } else {
+                               out, leftOffset, rightOffset = right, 3, 0
+                               // Both source ranges are large enough for the 
chosen offsets.
+                               left = make([]byte, (length+leftOffset+7)/8+1)
+                               fillUnalignedBitmapPattern(left, 12345)
+                       }
+                       beforeLeft := bytes.Clone(left)
+                       beforeRight := bytes.Clone(right)
+                       want := bytes.Clone(out)
+                       for i := int64(0); i < length; i++ {
+                               a := bitutil.BitIsSet(beforeLeft, 
int(leftOffset+i))
+                               b := bitutil.BitIsSet(beforeRight, 
int(rightOffset+i))
+                               bitutil.SetBitTo(want, int(i), op.want(a, b))
+                       }
+                       op.run(left, right, leftOffset, rightOffset, out, 0, 
length)
+                       if !bytes.Equal(out, want) {
+                               t.Fatalf("%s aliasLeft=%v: got %x, want %x", 
op.name, aliasLeft, out, want)
+                       }
+               }
+       }
+}
+
+var bitmapUnalignedBenchmarkSink byte
+
+func BenchmarkBitmapUnalignedOps(b *testing.B) {
+       for _, nbytes := range []int{32, 512, 8192, 131072} {
+               for _, offsets := range []struct{ left, right, out int64 }{
+                       {0, 1, 0}, {1, 3, 0}, {7, 4, 0}, {1, 3, 1},
+               } {
+                       left := make([]byte, int64(nbytes)+2)
+                       right := make([]byte, int64(nbytes)+2)
+                       out := make([]byte, int64(nbytes)+2)
+                       fillUnalignedBitmapPattern(left, 13579)
+                       fillUnalignedBitmapPattern(right, 24680)
+                       for _, tailBits := range []int64{0, 3} {
+                               for _, op := range unalignedBitmapOps {
+                                       
b.Run(fmt.Sprintf("%dB/left%d-right%d-out%d/tail%d/%s",
+                                               nbytes, offsets.left, 
offsets.right, offsets.out, tailBits, op.name), func(b *testing.B) {
+                                               b.SetBytes(int64(2 * nbytes))
+                                               b.ReportAllocs()
+                                               for b.Loop() {
+                                                       op.run(left, right, 
offsets.left, offsets.right, out, offsets.out, int64(nbytes*8)-tailBits)
+                                               }
+                                               bitmapUnalignedBenchmarkSink ^= 
out[0]
+                                       })
+                               }
+                       }
+               }
+       }
+}
+
+func TestBitmapUnalignedExactBuffers(t *testing.T) {
+       for _, length := range []int64{128, 129, 135, 191, 192, 193, 255, 256, 
257} {
+               for leftOffset := int64(0); leftOffset < 16; leftOffset++ {
+                       for rightOffset := int64(0); rightOffset < 16; 
rightOffset++ {
+                               left := make([]byte, (leftOffset+length+7)/8)
+                               right := make([]byte, (rightOffset+length+7)/8)
+                               fillUnalignedBitmapPattern(left, 
uint32(123+length+leftOffset))
+                               fillUnalignedBitmapPattern(right, 
uint32(456+length+rightOffset))
+                               for _, outOffset := range []int64{0, 8, 16} {
+                                       for _, op := range unalignedBitmapOps {
+                                               out := 
bytes.Repeat([]byte{0xa5}, int((outOffset+length+7)/8))
+                                               want := bytes.Clone(out)
+                                               for i := int64(0); i < length; 
i++ {
+                                                       bitutil.SetBitTo(want, 
int(outOffset+i), op.want(bitutil.BitIsSet(left, int(leftOffset+i)), 
bitutil.BitIsSet(right, int(rightOffset+i))))
+                                               }
+                                               op.run(left, right, leftOffset, 
rightOffset, out, outOffset, length)
+                                               if !bytes.Equal(out, want) {
+                                                       t.Fatalf("%s length=%d 
offsets=%d/%d/%d: got %x, want %x", op.name, length, leftOffset, rightOffset, 
outOffset, out, want)
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+}

Reply via email to