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 d72b3134 perf(arrow/array): remove map validity slice (#1228)
d72b3134 is described below

commit d72b3134943bd11bd666c29cde08ae22fbf5bd43
Author: Minh Vu <[email protected]>
AuthorDate: Fri Aug 28 20:11:30 2026 +0200

    perf(arrow/array): remove map validity slice (#1228)
    
    ## Summary
    
    - Replace the temporary all-true validity slice in
    `MapBuilder.adjustStructBuilderLen`.
    - Reuse the existing all-valid validity bitmap fast path.
    - Add a benchmark for `NewMapArray` after bulk key/item appends.
    
    The benchmark stops the timer while setting up the map entries, so it
    focuses on the finalization path where the temporary slice was created.
    
    ## 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`.
    
    | Bulk entries | Before | After |
    | ---: | --- | --- |
    | 32 | 6.59 us, 4,392 B, 54 allocs | 6.20 us, 4,392 B, 54 allocs |
    | 1,024 | 9.00 us, 5,608 B, 55 allocs | 7.69 us, 4,584 B, 54 allocs |
    | 65,536 | 92.9 us, 88.2 KB, 55 allocs | 31.0 us, 22.7 KB, 54 allocs |
    
    For 65,536 entries, this reduces bytes allocated by about 74% and
    removes one allocation per operation. The 1,024-entry case removes the
    temporary 1,024-byte slice as well.
    
    Command:
    
    ```text
    go test -vet=off ./arrow/array -run '^$' -bench 
'^BenchmarkMapBuilderNewArrayAfterBulkChildren$' -benchmem -benchtime=500ms 
-count=3 -cpu=1
    ```
    
    ## Tests
    
    - `go test ./arrow/array -run 'TestMap' -count=1`
    - `go test ./... -count=1` with the pinned `arrow-testing` and
    `parquet-testing` submodules initialized
---
 arrow/array/map.go                        |  8 ++---
 arrow/array/map_builder_benchmark_test.go | 58 +++++++++++++++++++++++++++++++
 2 files changed, 61 insertions(+), 5 deletions(-)

diff --git a/arrow/array/map.go b/arrow/array/map.go
index d8ad4730..8bc6332d 100644
--- a/arrow/array/map.go
+++ b/arrow/array/map.go
@@ -294,11 +294,9 @@ func (b *MapBuilder) adjustStructBuilderLen() {
                        arrow.ErrInvalid, sb.Len(), keyLen))
        }
        if sb.Len() < keyLen {
-               valids := make([]bool, keyLen-sb.Len())
-               for i := range valids {
-                       valids[i] = true
-               }
-               sb.AppendValues(valids)
+               missing := keyLen - sb.Len()
+               sb.Reserve(missing)
+               sb.unsafeAppendBoolsToBitmap(nil, missing)
        }
 }
 
diff --git a/arrow/array/map_builder_benchmark_test.go 
b/arrow/array/map_builder_benchmark_test.go
new file mode 100644
index 00000000..d37a56e5
--- /dev/null
+++ b/arrow/array/map_builder_benchmark_test.go
@@ -0,0 +1,58 @@
+// 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.
+
+package array_test
+
+import (
+       "fmt"
+       "testing"
+
+       "github.com/apache/arrow-go/v18/arrow"
+       "github.com/apache/arrow-go/v18/arrow/array"
+       "github.com/apache/arrow-go/v18/arrow/memory"
+)
+
+func BenchmarkMapBuilderNewArrayAfterBulkChildren(b *testing.B) {
+       for _, entries := range []int{32, 1024, 65536} {
+               b.Run(fmt.Sprintf("entries=%d", entries), func(b *testing.B) {
+                       builder := array.NewMapBuilder(memory.DefaultAllocator, 
arrow.PrimitiveTypes.Int32, arrow.PrimitiveTypes.Int32, false)
+                       defer builder.Release()
+
+                       offsets := []int32{0, int32(entries)}
+                       valid := []bool{true}
+                       keys := make([]int32, entries)
+                       items := make([]int32, entries)
+                       for i := range keys {
+                               keys[i] = int32(i)
+                               items[i] = int32(i)
+                       }
+
+                       keyBuilder := builder.KeyBuilder().(*array.Int32Builder)
+                       itemBuilder := 
builder.ItemBuilder().(*array.Int32Builder)
+                       b.ReportAllocs()
+                       b.ResetTimer()
+                       for b.Loop() {
+                               b.StopTimer()
+                               builder.AppendValues(offsets, valid)
+                               keyBuilder.AppendValues(keys, nil)
+                               itemBuilder.AppendValues(items, nil)
+                               b.StartTimer()
+                               arr := builder.NewMapArray()
+                               arr.Release()
+                       }
+               })
+       }
+}

Reply via email to