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()
+ }
+ })
+ }
+}