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 4bcea877 perf(arrow/array): append fixed-width nulls in bulk (#1173)
4bcea877 is described below

commit 4bcea877462c42d01281317e54b296d5125f7ad8
Author: Minh Vu <[email protected]>
AuthorDate: Mon Aug 31 19:39:05 2026 +0200

    perf(arrow/array): append fixed-width nulls in bulk (#1173)
    
    ### Rationale for this change
    
    Fixed-width builders currently implement `AppendNulls` by calling
    `AppendNull` for every value. Large batches repeatedly reserve capacity,
    update the validity bitmap, and update builder counters.
    
    ### What changes are included in this PR?
    
    - Reserve capacity once for a null batch.
    - Clear the validity range in bulk.
    - Update the length and null count once.
    - Advance fixed-size binary value storage once.
    - Keep the existing scalar path for a single null.
    - Keep the existing builder capacity growth policy.
    - Size fixed-size binary value storage to the effective builder capacity
    during `Resize`.
    - Update the numeric builder template and generated source.
    - Add correctness tests and benchmarks for representative fixed-width
    builders.
    
    Null values and array results are unchanged.
    `FixedSizeBinaryBuilder.Resize` now reserves value storage together with
    validity storage. Empty-value appends are separate because their value
    bytes must also be zeroed.
    
    ### Benchmark
    
    `BenchmarkFixedWidthBuilderAppendNulls` covers:
    
    - `Int32`
    - `Int64`
    - `Decimal128`
    - `Timestamp`
    - `Boolean`
    - `FixedSizeBinary(16)`
    - Batch sizes of 1, 8, 64, 1,024, and 65,536
    
    The benchmark creates a fresh builder for each iteration, so allocation
    costs are included. The same benchmark was run against `upstream/main`
    and this branch.
    
    Allocation results for 65,536 nulls, checked against current
    `upstream/main` and this branch. Three runs per case on Apple M1 Pro
    with Go 1.26.3:
    
    | Builder | Before B/op | After B/op | Before allocs/op | After
    allocs/op |
    | --- | ---: | ---: | ---: | ---: |
    | Int32 | 580,864 | 551,168 | 23 | 5 |
    | Int64 | 1,113,152 | 1,075,456 | 23 | 5 |
    | Decimal128 | 2,169,655 | 2,124,080 | 25 | 7 |
    | Timestamp | 1,113,232 | 1,075,536 | 24 | 6 |
    | Boolean | 38,272 | 37,120 | 19 | 5 |
    | FixedSizeBinary(16) | 2,170,319 | 2,124,104 | 28 | 7 |
    
    Command:
    
    ```bash
    go test -trimpath -p 1 ./arrow/array -run '^$' -bench 
'^BenchmarkFixedWidthBuilderAppendNulls$/./count-65536$' -benchmem 
-benchtime=150ms -count=3 -cpu=1
    ```
    
    ### Are these changes tested?
    
    Yes.
    
    - `go test ./arrow/array -count=1`
    - `go test -race ./arrow/array -count=1`
    - `go vet -composites=false ./arrow/array`
    - Regenerated the numeric builders and verified there is no diff.
    
    ### Are there any user-facing changes?
    
    `FixedSizeBinaryBuilder.Resize` now reserves the value buffer eagerly.
    Null-appending results and the general builder growth policy are
    unchanged.
---
 arrow/array/booleanbuilder.go                      |   8 +-
 arrow/array/builder.go                             |  14 +++
 arrow/array/builder_append_nulls_benchmark_test.go |  63 +++++++++++
 arrow/array/builder_append_nulls_test.go           | 124 +++++++++++++++++++++
 arrow/array/decimal.go                             |   8 +-
 arrow/array/fixedsize_binarybuilder.go             |  12 +-
 arrow/array/float16_builder.go                     |   8 +-
 arrow/array/interval.go                            |  24 +++-
 arrow/array/numericbuilder.gen.go                  | 120 +++++++++++++++++---
 arrow/array/numericbuilder.gen.go.tmpl             |   8 +-
 arrow/array/timestamp.go                           |   8 +-
 11 files changed, 373 insertions(+), 24 deletions(-)

diff --git a/arrow/array/booleanbuilder.go b/arrow/array/booleanbuilder.go
index f90ac0b4..4b27462c 100644
--- a/arrow/array/booleanbuilder.go
+++ b/arrow/array/booleanbuilder.go
@@ -79,9 +79,15 @@ func (b *BooleanBuilder) AppendNull() {
 }
 
 func (b *BooleanBuilder) AppendNulls(n int) {
-       for i := 0; i < n; i++ {
+       if n <= 0 {
+               return
+       }
+       if n == 1 {
                b.AppendNull()
+               return
        }
+       b.Reserve(n)
+       b.unsafeAppendNulls(n)
 }
 
 func (b *BooleanBuilder) AppendEmptyValue() {
diff --git a/arrow/array/builder.go b/arrow/array/builder.go
index 2b5aa2cf..48113f43 100644
--- a/arrow/array/builder.go
+++ b/arrow/array/builder.go
@@ -355,6 +355,20 @@ func (b *builder) unsafeSetValid(length int) {
        b.length = newLength
 }
 
+func (b *builder) unsafeAppendNulls(n int) {
+       if n <= 0 {
+               return
+       }
+
+       if n == 1 {
+               bitutil.ClearBit(b.nullBitmap.Bytes(), b.length)
+       } else {
+               bitutil.SetBitsTo(b.nullBitmap.Bytes(), int64(b.length), 
int64(n), false)
+       }
+       b.length += n
+       b.nulls += n
+}
+
 func (b *builder) unsafeAppendEmptyValues(data []byte, valueSize, length int) {
        if length <= 0 {
                return
diff --git a/arrow/array/builder_append_nulls_benchmark_test.go 
b/arrow/array/builder_append_nulls_benchmark_test.go
new file mode 100644
index 00000000..e334367c
--- /dev/null
+++ b/arrow/array/builder_append_nulls_benchmark_test.go
@@ -0,0 +1,63 @@
+// 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 BenchmarkFixedWidthBuilderAppendNulls(b *testing.B) {
+       builders := []struct {
+               name string
+               new  func(memory.Allocator) array.Builder
+       }{
+               {"int32", func(mem memory.Allocator) array.Builder { return 
array.NewInt32Builder(mem) }},
+               {"int64", func(mem memory.Allocator) array.Builder { return 
array.NewInt64Builder(mem) }},
+               {"decimal128", func(mem memory.Allocator) array.Builder {
+                       return array.NewDecimal128Builder(mem, 
&arrow.Decimal128Type{Precision: 38})
+               }},
+               {"timestamp", func(mem memory.Allocator) array.Builder {
+                       return array.NewTimestampBuilder(mem, 
&arrow.TimestampType{Unit: arrow.Nanosecond})
+               }},
+               {"boolean", func(mem memory.Allocator) array.Builder { return 
array.NewBooleanBuilder(mem) }},
+               {"fixed-size-binary", func(mem memory.Allocator) array.Builder {
+                       return array.NewFixedSizeBinaryBuilder(mem, 
&arrow.FixedSizeBinaryType{ByteWidth: 16})
+               }},
+       }
+
+       for _, builder := range builders {
+               b.Run(builder.name, func(b *testing.B) {
+                       for _, count := range []int{1, 8, 64, 1024, 65536} {
+                               b.Run(fmt.Sprintf("count-%d", count), func(b 
*testing.B) {
+                                       mem := memory.NewGoAllocator()
+                                       b.ReportAllocs()
+                                       b.ResetTimer()
+                                       for b.Loop() {
+                                               instance := builder.new(mem)
+                                               instance.AppendNulls(count)
+                                               instance.Release()
+                                       }
+                               })
+                       }
+               })
+       }
+}
diff --git a/arrow/array/builder_append_nulls_test.go 
b/arrow/array/builder_append_nulls_test.go
new file mode 100644
index 00000000..d6eb9236
--- /dev/null
+++ b/arrow/array/builder_append_nulls_test.go
@@ -0,0 +1,124 @@
+// 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 (
+       "testing"
+
+       "github.com/apache/arrow-go/v18/arrow"
+       "github.com/apache/arrow-go/v18/arrow/array"
+       "github.com/apache/arrow-go/v18/arrow/memory"
+       "github.com/stretchr/testify/require"
+)
+
+func TestFixedWidthBuilderAppendNulls(t *testing.T) {
+       builders := []struct {
+               name string
+               new  func(memory.Allocator) array.Builder
+       }{
+               {"int32", func(mem memory.Allocator) array.Builder { return 
array.NewInt32Builder(mem) }},
+               {"float16", func(mem memory.Allocator) array.Builder { return 
array.NewFloat16Builder(mem) }},
+               {"decimal128", func(mem memory.Allocator) array.Builder {
+                       return array.NewDecimal128Builder(mem, 
&arrow.Decimal128Type{Precision: 38})
+               }},
+               {"timestamp", func(mem memory.Allocator) array.Builder {
+                       return array.NewTimestampBuilder(mem, 
&arrow.TimestampType{Unit: arrow.Nanosecond})
+               }},
+               {"month-interval", func(mem memory.Allocator) array.Builder { 
return array.NewMonthIntervalBuilder(mem) }},
+               {"day-time-interval", func(mem memory.Allocator) array.Builder 
{ return array.NewDayTimeIntervalBuilder(mem) }},
+               {"month-day-nano-interval", func(mem memory.Allocator) 
array.Builder {
+                       return array.NewMonthDayNanoIntervalBuilder(mem)
+               }},
+               {"boolean", func(mem memory.Allocator) array.Builder { return 
array.NewBooleanBuilder(mem) }},
+               {"fixed-size-binary", func(mem memory.Allocator) array.Builder {
+                       return array.NewFixedSizeBinaryBuilder(mem, 
&arrow.FixedSizeBinaryType{ByteWidth: 16})
+               }},
+       }
+
+       for _, builder := range builders {
+               t.Run(builder.name, func(t *testing.T) {
+                       mem := 
memory.NewCheckedAllocator(memory.NewGoAllocator())
+                       defer mem.AssertSize(t, 0)
+
+                       instance := builder.new(mem)
+                       defer instance.Release()
+
+                       instance.AppendNulls(0)
+                       instance.AppendNulls(-1)
+                       require.Zero(t, instance.Len())
+                       require.Zero(t, instance.Cap())
+                       require.Zero(t, instance.NullN())
+
+                       instance.AppendEmptyValue()
+                       instance.AppendNulls(10)
+                       instance.AppendEmptyValue()
+                       require.Equal(t, 12, instance.Len())
+                       require.Equal(t, 10, instance.NullN())
+
+                       result := instance.NewArray()
+                       defer result.Release()
+                       require.True(t, result.IsValid(0))
+                       for idx := 1; idx <= 10; idx++ {
+                               require.True(t, result.IsNull(idx))
+                       }
+                       require.True(t, result.IsValid(11))
+               })
+       }
+}
+
+func TestFixedWidthBuilderAppendNullsAfterTruncate(t *testing.T) {
+       mem := memory.NewCheckedAllocator(memory.NewGoAllocator())
+       defer mem.AssertSize(t, 0)
+
+       builder := array.NewInt32Builder(mem)
+       defer builder.Release()
+       builder.AppendEmptyValues(16)
+       builder.Resize(3)
+       builder.AppendNulls(10)
+
+       result := builder.NewInt32Array()
+       defer result.Release()
+       require.Equal(t, 13, result.Len())
+       require.Equal(t, 10, result.NullN())
+       for idx := 0; idx < 3; idx++ {
+               require.True(t, result.IsValid(idx))
+       }
+       for idx := 3; idx < result.Len(); idx++ {
+               require.True(t, result.IsNull(idx))
+       }
+}
+
+func TestFixedSizeBinaryBuilderAppendNullsAcrossGrowthBoundary(t *testing.T) {
+       const (
+               count    = 1 << 16
+               byteSize = 16
+       )
+
+       mem := memory.NewCheckedAllocator(memory.NewGoAllocator())
+       defer mem.AssertSize(t, 0)
+
+       builder := array.NewFixedSizeBinaryBuilder(mem, 
&arrow.FixedSizeBinaryType{ByteWidth: byteSize})
+       builder.AppendNulls(count)
+       builder.AppendNulls(count)
+
+       result := builder.NewFixedSizeBinaryArray()
+       require.Equal(t, count*2, result.Len())
+       require.Equal(t, count*2, result.NullN())
+       require.Equal(t, count*2*byteSize, result.Data().Buffers()[1].Cap())
+       result.Release()
+       builder.Release()
+}
diff --git a/arrow/array/decimal.go b/arrow/array/decimal.go
index d284e2fa..92d049f4 100644
--- a/arrow/array/decimal.go
+++ b/arrow/array/decimal.go
@@ -232,9 +232,15 @@ func (b *baseDecimalBuilder[T]) AppendNull() {
 }
 
 func (b *baseDecimalBuilder[T]) AppendNulls(n int) {
-       for i := 0; i < n; i++ {
+       if n <= 0 {
+               return
+       }
+       if n == 1 {
                b.AppendNull()
+               return
        }
+       b.Reserve(n)
+       b.unsafeAppendNulls(n)
 }
 
 func (b *baseDecimalBuilder[T]) AppendEmptyValue() {
diff --git a/arrow/array/fixedsize_binarybuilder.go 
b/arrow/array/fixedsize_binarybuilder.go
index b077f6d9..9721b547 100644
--- a/arrow/array/fixedsize_binarybuilder.go
+++ b/arrow/array/fixedsize_binarybuilder.go
@@ -84,9 +84,16 @@ func (b *FixedSizeBinaryBuilder) AppendNull() {
 }
 
 func (b *FixedSizeBinaryBuilder) AppendNulls(n int) {
-       for i := 0; i < n; i++ {
+       if n <= 0 {
+               return
+       }
+       if n == 1 {
                b.AppendNull()
+               return
        }
+       b.Reserve(n)
+       b.values.Advance(n * b.dtype.ByteWidth)
+       b.unsafeAppendNulls(n)
 }
 
 func (b *FixedSizeBinaryBuilder) AppendEmptyValue() {
@@ -155,6 +162,9 @@ func (b *FixedSizeBinaryBuilder) Reserve(n int) {
 // additional memory will be allocated. If n is smaller, the allocated memory 
may reduced.
 func (b *FixedSizeBinaryBuilder) Resize(n int) {
        b.resize(n, b.init)
+       // Keep the value buffer sized to the effective builder capacity. 
b.resize
+       // may clamp n to minBuilderCapacity.
+       b.values.resize(b.capacity * b.dtype.ByteWidth)
 }
 
 func (b *FixedSizeBinaryBuilder) truncate(n int) {
diff --git a/arrow/array/float16_builder.go b/arrow/array/float16_builder.go
index 2c198f5d..2b334b4e 100644
--- a/arrow/array/float16_builder.go
+++ b/arrow/array/float16_builder.go
@@ -80,9 +80,15 @@ func (b *Float16Builder) AppendNull() {
 }
 
 func (b *Float16Builder) AppendNulls(n int) {
-       for i := 0; i < n; i++ {
+       if n <= 0 {
+               return
+       }
+       if n == 1 {
                b.AppendNull()
+               return
        }
+       b.Reserve(n)
+       b.unsafeAppendNulls(n)
 }
 
 func (b *Float16Builder) AppendEmptyValue() {
diff --git a/arrow/array/interval.go b/arrow/array/interval.go
index 61e9b8fd..df2eaf0e 100644
--- a/arrow/array/interval.go
+++ b/arrow/array/interval.go
@@ -183,9 +183,15 @@ func (b *MonthIntervalBuilder) AppendNull() {
 }
 
 func (b *MonthIntervalBuilder) AppendNulls(n int) {
-       for i := 0; i < n; i++ {
+       if n <= 0 {
+               return
+       }
+       if n == 1 {
                b.AppendNull()
+               return
        }
+       b.Reserve(n)
+       b.unsafeAppendNulls(n)
 }
 
 func (b *MonthIntervalBuilder) AppendEmptyValue() {
@@ -499,9 +505,15 @@ func (b *DayTimeIntervalBuilder) AppendNull() {
 }
 
 func (b *DayTimeIntervalBuilder) AppendNulls(n int) {
-       for i := 0; i < n; i++ {
+       if n <= 0 {
+               return
+       }
+       if n == 1 {
                b.AppendNull()
+               return
        }
+       b.Reserve(n)
+       b.unsafeAppendNulls(n)
 }
 
 func (b *DayTimeIntervalBuilder) AppendEmptyValue() {
@@ -818,9 +830,15 @@ func (b *MonthDayNanoIntervalBuilder) AppendNull() {
 }
 
 func (b *MonthDayNanoIntervalBuilder) AppendNulls(n int) {
-       for i := 0; i < n; i++ {
+       if n <= 0 {
+               return
+       }
+       if n == 1 {
                b.AppendNull()
+               return
        }
+       b.Reserve(n)
+       b.unsafeAppendNulls(n)
 }
 
 func (b *MonthDayNanoIntervalBuilder) AppendEmptyValue() {
diff --git a/arrow/array/numericbuilder.gen.go 
b/arrow/array/numericbuilder.gen.go
index b189ba51..93dda6a5 100644
--- a/arrow/array/numericbuilder.gen.go
+++ b/arrow/array/numericbuilder.gen.go
@@ -78,9 +78,15 @@ func (b *Int64Builder) AppendNull() {
 }
 
 func (b *Int64Builder) AppendNulls(n int) {
-       for i := 0; i < n; i++ {
+       if n <= 0 {
+               return
+       }
+       if n == 1 {
                b.AppendNull()
+               return
        }
+       b.Reserve(n)
+       b.unsafeAppendNulls(n)
 }
 
 func (b *Int64Builder) AppendEmptyValue() {
@@ -408,9 +414,15 @@ func (b *Uint64Builder) AppendNull() {
 }
 
 func (b *Uint64Builder) AppendNulls(n int) {
-       for i := 0; i < n; i++ {
+       if n <= 0 {
+               return
+       }
+       if n == 1 {
                b.AppendNull()
+               return
        }
+       b.Reserve(n)
+       b.unsafeAppendNulls(n)
 }
 
 func (b *Uint64Builder) AppendEmptyValue() {
@@ -742,9 +754,15 @@ func (b *Float64Builder) AppendNull() {
 }
 
 func (b *Float64Builder) AppendNulls(n int) {
-       for i := 0; i < n; i++ {
+       if n <= 0 {
+               return
+       }
+       if n == 1 {
                b.AppendNull()
+               return
        }
+       b.Reserve(n)
+       b.unsafeAppendNulls(n)
 }
 
 func (b *Float64Builder) AppendEmptyValue() {
@@ -990,9 +1008,15 @@ func (b *Int32Builder) AppendNull() {
 }
 
 func (b *Int32Builder) AppendNulls(n int) {
-       for i := 0; i < n; i++ {
+       if n <= 0 {
+               return
+       }
+       if n == 1 {
                b.AppendNull()
+               return
        }
+       b.Reserve(n)
+       b.unsafeAppendNulls(n)
 }
 
 func (b *Int32Builder) AppendEmptyValue() {
@@ -1304,9 +1328,15 @@ func (b *Uint32Builder) AppendNull() {
 }
 
 func (b *Uint32Builder) AppendNulls(n int) {
-       for i := 0; i < n; i++ {
+       if n <= 0 {
+               return
+       }
+       if n == 1 {
                b.AppendNull()
+               return
        }
+       b.Reserve(n)
+       b.unsafeAppendNulls(n)
 }
 
 func (b *Uint32Builder) AppendEmptyValue() {
@@ -1618,9 +1648,15 @@ func (b *Float32Builder) AppendNull() {
 }
 
 func (b *Float32Builder) AppendNulls(n int) {
-       for i := 0; i < n; i++ {
+       if n <= 0 {
+               return
+       }
+       if n == 1 {
                b.AppendNull()
+               return
        }
+       b.Reserve(n)
+       b.unsafeAppendNulls(n)
 }
 
 func (b *Float32Builder) AppendEmptyValue() {
@@ -1866,9 +1902,15 @@ func (b *Int16Builder) AppendNull() {
 }
 
 func (b *Int16Builder) AppendNulls(n int) {
-       for i := 0; i < n; i++ {
+       if n <= 0 {
+               return
+       }
+       if n == 1 {
                b.AppendNull()
+               return
        }
+       b.Reserve(n)
+       b.unsafeAppendNulls(n)
 }
 
 func (b *Int16Builder) AppendEmptyValue() {
@@ -2180,9 +2222,15 @@ func (b *Uint16Builder) AppendNull() {
 }
 
 func (b *Uint16Builder) AppendNulls(n int) {
-       for i := 0; i < n; i++ {
+       if n <= 0 {
+               return
+       }
+       if n == 1 {
                b.AppendNull()
+               return
        }
+       b.Reserve(n)
+       b.unsafeAppendNulls(n)
 }
 
 func (b *Uint16Builder) AppendEmptyValue() {
@@ -2494,9 +2542,15 @@ func (b *Int8Builder) AppendNull() {
 }
 
 func (b *Int8Builder) AppendNulls(n int) {
-       for i := 0; i < n; i++ {
+       if n <= 0 {
+               return
+       }
+       if n == 1 {
                b.AppendNull()
+               return
        }
+       b.Reserve(n)
+       b.unsafeAppendNulls(n)
 }
 
 func (b *Int8Builder) AppendEmptyValue() {
@@ -2808,9 +2862,15 @@ func (b *Uint8Builder) AppendNull() {
 }
 
 func (b *Uint8Builder) AppendNulls(n int) {
-       for i := 0; i < n; i++ {
+       if n <= 0 {
+               return
+       }
+       if n == 1 {
                b.AppendNull()
+               return
        }
+       b.Reserve(n)
+       b.unsafeAppendNulls(n)
 }
 
 func (b *Uint8Builder) AppendEmptyValue() {
@@ -3123,9 +3183,15 @@ func (b *Time32Builder) AppendNull() {
 }
 
 func (b *Time32Builder) AppendNulls(n int) {
-       for i := 0; i < n; i++ {
+       if n <= 0 {
+               return
+       }
+       if n == 1 {
                b.AppendNull()
+               return
        }
+       b.Reserve(n)
+       b.unsafeAppendNulls(n)
 }
 
 func (b *Time32Builder) AppendEmptyValue() {
@@ -3389,9 +3455,15 @@ func (b *Time64Builder) AppendNull() {
 }
 
 func (b *Time64Builder) AppendNulls(n int) {
-       for i := 0; i < n; i++ {
+       if n <= 0 {
+               return
+       }
+       if n == 1 {
                b.AppendNull()
+               return
        }
+       b.Reserve(n)
+       b.unsafeAppendNulls(n)
 }
 
 func (b *Time64Builder) AppendEmptyValue() {
@@ -3645,9 +3717,15 @@ func (b *Date32Builder) AppendNull() {
 }
 
 func (b *Date32Builder) AppendNulls(n int) {
-       for i := 0; i < n; i++ {
+       if n <= 0 {
+               return
+       }
+       if n == 1 {
                b.AppendNull()
+               return
        }
+       b.Reserve(n)
+       b.unsafeAppendNulls(n)
 }
 
 func (b *Date32Builder) AppendEmptyValue() {
@@ -3910,9 +3988,15 @@ func (b *Date64Builder) AppendNull() {
 }
 
 func (b *Date64Builder) AppendNulls(n int) {
-       for i := 0; i < n; i++ {
+       if n <= 0 {
+               return
+       }
+       if n == 1 {
                b.AppendNull()
+               return
        }
+       b.Reserve(n)
+       b.unsafeAppendNulls(n)
 }
 
 func (b *Date64Builder) AppendEmptyValue() {
@@ -4167,9 +4251,15 @@ func (b *DurationBuilder) AppendNull() {
 }
 
 func (b *DurationBuilder) AppendNulls(n int) {
-       for i := 0; i < n; i++ {
+       if n <= 0 {
+               return
+       }
+       if n == 1 {
                b.AppendNull()
+               return
        }
+       b.Reserve(n)
+       b.unsafeAppendNulls(n)
 }
 
 func (b *DurationBuilder) AppendEmptyValue() {
diff --git a/arrow/array/numericbuilder.gen.go.tmpl 
b/arrow/array/numericbuilder.gen.go.tmpl
index 0a25279c..5fd59c7a 100644
--- a/arrow/array/numericbuilder.gen.go.tmpl
+++ b/arrow/array/numericbuilder.gen.go.tmpl
@@ -88,9 +88,15 @@ func (b *{{.Name}}Builder) AppendNull() {
 }
 
 func (b *{{.Name}}Builder) AppendNulls(n int) {
-       for i := 0; i < n; i++ {
+       if n <= 0 {
+               return
+       }
+       if n == 1 {
                b.AppendNull()
+               return
        }
+       b.Reserve(n)
+       b.unsafeAppendNulls(n)
 }
 
 func (b *{{.Name}}Builder) AppendEmptyValue() {
diff --git a/arrow/array/timestamp.go b/arrow/array/timestamp.go
index 1b17c3e2..fcb78366 100644
--- a/arrow/array/timestamp.go
+++ b/arrow/array/timestamp.go
@@ -197,9 +197,15 @@ func (b *TimestampBuilder) AppendNull() {
 }
 
 func (b *TimestampBuilder) AppendNulls(n int) {
-       for i := 0; i < n; i++ {
+       if n <= 0 {
+               return
+       }
+       if n == 1 {
                b.AppendNull()
+               return
        }
+       b.Reserve(n)
+       b.unsafeAppendNulls(n)
 }
 
 func (b *TimestampBuilder) AppendEmptyValue() {

Reply via email to