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 16c738f7 perf(compute): reserve binary view cast payload (#1227)
16c738f7 is described below

commit 16c738f7101e057f2ab053d8d22caad693d3c364
Author: Minh Vu <[email protected]>
AuthorDate: Fri Aug 28 23:38:21 2026 +0200

    perf(compute): reserve binary view cast payload (#1227)
    
    ## Summary
    
    - Reserve the output payload buffer after the existing `BinaryView` scan
    computes the total payload size.
    - Keep the existing overflow check and append path unchanged.
    - Add a benchmark for 64K values with 4, 32, and 256 byte payloads, plus
    0%, 10%, and 50% nulls.
    
    This avoids repeated growth copies when materializing out-of-line
    `BinaryView` values as `Binary`.
    
    ## Benchmark
    
    Measured on Apple M1 Pro, macOS arm64, Go 1.26.3. Median of 3 runs. Each
    result is `time/op`, `B/op`, and `allocs/op`.
    
    | Payload | Nulls | Before | After |
    | --- | ---: | --- | --- |
    | 4 B | 0% | 1.46 ms, 1.12 MB, 66 allocs | 1.41 ms, 0.82 MB, 54 allocs |
    | 32 B | 0% | 1.96 ms, 4.81 MB, 70 allocs | 1.69 ms, 2.66 MB, 54 allocs
    |
    | 256 B | 0% | 4.20 ms, 34.2 MB, 71 allocs | 2.93 ms, 17.3 MB, 55 allocs
    |
    | 256 B | 50% | 2.55 ms, 17.4 MB, 70 allocs | 1.87 ms, 8.95 MB, 55
    allocs |
    
    The benchmark matrix also covers 10% nulls. The samples reduce payload
    allocation by 26% to 54% and remove 12 to 16 allocations per operation.
    
    Command:
    
    ```text
    go test -vet=off ./arrow/compute -run '^$' -bench 
'^BenchmarkBinaryViewToBinaryMaterialization$' -benchmem -benchtime=500ms 
-count=3 -cpu=1
    ```
    
    ## Tests
    
    - `go test ./arrow/compute ./arrow/compute/internal/kernels -count=1`
    - `go test ./... -count=1` with the pinned `arrow-testing` and
    `parquet-testing` submodules initialized
---
 arrow/compute/cast_binary_view_benchmark_test.go   | 72 ++++++++++++++++++++++
 .../compute/internal/kernels/binary_view_casts.go  |  8 +++
 2 files changed, 80 insertions(+)

diff --git a/arrow/compute/cast_binary_view_benchmark_test.go 
b/arrow/compute/cast_binary_view_benchmark_test.go
new file mode 100644
index 00000000..a9e1b11a
--- /dev/null
+++ b/arrow/compute/cast_binary_view_benchmark_test.go
@@ -0,0 +1,72 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//go:build go1.24
+
+package compute_test
+
+import (
+       "context"
+       "fmt"
+       "strings"
+       "testing"
+
+       "github.com/apache/arrow-go/v18/arrow"
+       "github.com/apache/arrow-go/v18/arrow/array"
+       "github.com/apache/arrow-go/v18/arrow/compute"
+       "github.com/apache/arrow-go/v18/arrow/memory"
+)
+
+func BenchmarkBinaryViewToBinaryMaterialization(b *testing.B) {
+       for _, valueLen := range []int{4, 32, 256} {
+               for _, nullEvery := range []int{0, 10, 2} {
+                       name := fmt.Sprintf("value-len=%d/null-every=%d", 
valueLen, nullEvery)
+                       b.Run(name, func(b *testing.B) {
+                               const count = 64 * 1024
+
+                               values := make([][]byte, count)
+                               valid := make([]bool, count)
+                               value := []byte(strings.Repeat("x", valueLen))
+                               validCount := 0
+                               for i := range values {
+                                       values[i] = value
+                                       valid[i] = nullEvery == 0 || 
i%nullEvery != 0
+                                       if valid[i] {
+                                               validCount++
+                                       }
+                               }
+
+                               builder := 
array.NewBinaryViewBuilder(memory.DefaultAllocator)
+                               builder.AppendValues(values, valid)
+                               input := builder.NewArray()
+                               builder.Release()
+                               defer input.Release()
+
+                               opts := 
compute.SafeCastOptions(arrow.BinaryTypes.Binary)
+                               b.SetBytes(int64(validCount * valueLen))
+                               b.ReportAllocs()
+                               b.ResetTimer()
+                               for b.Loop() {
+                                       out, err := 
compute.CastArray(context.Background(), input, opts)
+                                       if err != nil {
+                                               b.Fatal(err)
+                                       }
+                                       out.Release()
+                               }
+                       })
+               }
+       }
+}
diff --git a/arrow/compute/internal/kernels/binary_view_casts.go 
b/arrow/compute/internal/kernels/binary_view_casts.go
index 01a5c384..7092e0a2 100644
--- a/arrow/compute/internal/kernels/binary_view_casts.go
+++ b/arrow/compute/internal/kernels/binary_view_casts.go
@@ -250,6 +250,14 @@ func CastBinaryViewToBinary[OutOffsetT int32 | int64](ctx 
*exec.KernelCtx, batch
                return fmt.Errorf("%w: failed casting from %s to %s: input 
array too large",
                        arrow.ErrInvalid, input.Type, out.Type)
        }
+       maxInt := int64(^uint(0) >> 1)
+       if totalBytes > maxInt {
+               return fmt.Errorf("%w: failed casting from %s to %s: output 
data buffer too large for this platform",
+                       arrow.ErrInvalid, input.Type, out.Type)
+       }
+       if totalBytes > 0 {
+               ba.reserveData(int(totalBytes))
+       }
 
        appendBinaryValues(arr, getVal, ba)
 

Reply via email to