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 726d10b2 perf(arrow/array): bulk append fixed-width empty values 
(#1180)
726d10b2 is described below

commit 726d10b240963eab067b8976be5589cdda8cd6ca
Author: Minh Vu <[email protected]>
AuthorDate: Fri Aug 14 19:33:08 2026 +0200

    perf(arrow/array): bulk append fixed-width empty values (#1180)
    
    ## What changed
    
    `AppendEmptyValues` used to call `AppendEmptyValue` in a loop. For
    larger batches, each value repeated the reserve, bitmap, and storage
    work.
    
    This change makes fixed-width empty appends bulk operations:
    
    - reserve space once
    - zero the appended value range in one operation
    - set validity bits in bulk
    - clear boolean value bits separately from the validity bitmap
    - keep the existing single-value path for count 1
    
    The numeric generated source and template are updated together. The
    covered builders are numeric, temporal, interval, float16, decimal,
    fixed-size binary, and boolean.
    
    ## Correctness
    
    - Empty values stay valid, including when the builder reuses storage.
    - Reused numeric and boolean storage is explicitly tested.
    - `bufferBuilder.Advance` now zeroes skipped bytes as documented.
    - `BooleanBuilder.Resize` now preserves the requested logical length
    when truncating.
    - Zero and negative counts remain no-ops.
    
    ## Benchmark
    
    `AppendEmptyValues(1024)` on an Apple M1 Pro. Three runs were used for
    each result.
    
    | Builder | Before | After | Allocs |
    | --- | ---: | ---: | ---: |
    | Int32 | 5.15 us | 1.50 us | 11 -> 5 |
    | Int64 | 6.44 us | 2.52 us | 11 -> 5 |
    | Timestamp | 6.50 us | 3.22 us | 12 -> 6 |
    | Boolean | 4.04 us | 0.30 us | 7 -> 5 |
    | Fixed-size binary | 10.1 us | 5.69 us | 16 -> 7 |
    
    Small counts keep the single-value path, so the biggest improvement is
    for larger batches.
    
    ## Tests
    
    - `go test ./arrow/array`
    - `go vet ./arrow/array`
    - Added focused correctness tests and representative benchmarks
---
 arrow/array/booleanbuilder.go                      |  12 +-
 arrow/array/bufferbuilder.go                       |   1 +
 arrow/array/bufferbuilder_test.go                  |  14 ++
 arrow/array/builder.go                             |  10 ++
 .../builder_append_empty_values_benchmark_test.go  |  63 +++++++++
 arrow/array/builder_append_empty_values_test.go    | 145 +++++++++++++++++++++
 arrow/array/decimal.go                             |   8 +-
 arrow/array/fixedsize_binarybuilder.go             |   9 +-
 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 +-
 13 files changed, 405 insertions(+), 25 deletions(-)

diff --git a/arrow/array/booleanbuilder.go b/arrow/array/booleanbuilder.go
index fddc51b8..88a1e00c 100644
--- a/arrow/array/booleanbuilder.go
+++ b/arrow/array/booleanbuilder.go
@@ -90,9 +90,16 @@ func (b *BooleanBuilder) AppendEmptyValue() {
 }
 
 func (b *BooleanBuilder) AppendEmptyValues(n int) {
-       for i := 0; i < n; i++ {
+       if n <= 0 {
+               return
+       }
+       if n == 1 {
                b.AppendEmptyValue()
+               return
        }
+       b.Reserve(n)
+       bitutil.SetBitsTo(b.rawData, int64(b.length), int64(n), false)
+       b.unsafeSetValid(n)
 }
 
 func (b *BooleanBuilder) AppendValueFromString(s string) error {
@@ -152,6 +159,7 @@ func (b *BooleanBuilder) Reserve(n int) {
 // Resize adjusts the space allocated by b to n elements. If n is greater than 
b.Cap(),
 // additional memory will be allocated. If n is smaller, the allocated memory 
may reduced.
 func (b *BooleanBuilder) Resize(n int) {
+       nBuilder := n
        if n < minBuilderCapacity {
                n = minBuilderCapacity
        }
@@ -159,7 +167,7 @@ func (b *BooleanBuilder) Resize(n int) {
        if b.capacity == 0 {
                b.init(n)
        } else {
-               b.resize(n, b.init)
+               b.resize(nBuilder, b.init)
                b.data.Resize(arrow.BooleanTraits.BytesRequired(n))
                b.rawData = b.data.Bytes()
        }
diff --git a/arrow/array/bufferbuilder.go b/arrow/array/bufferbuilder.go
index dbf1fbbb..6d654d8f 100644
--- a/arrow/array/bufferbuilder.go
+++ b/arrow/array/bufferbuilder.go
@@ -114,6 +114,7 @@ func (b *bufferBuilder) Advance(length int) {
                newCapacity := bitutil.NextPowerOf2(b.length + length)
                b.resize(newCapacity)
        }
+       memory.Set(b.bytes[b.length:b.length+length], 0)
        b.length += length
 }
 
diff --git a/arrow/array/bufferbuilder_test.go 
b/arrow/array/bufferbuilder_test.go
index bd7b6baf..070b6304 100644
--- a/arrow/array/bufferbuilder_test.go
+++ b/arrow/array/bufferbuilder_test.go
@@ -72,3 +72,17 @@ func TestMultiBufferCheckpointRestoresTouchedBlocks(t 
*testing.T) {
        assert.Equal(t, 4, builder.blocks[1].Len())
        assert.Equal(t, 1, builder.currentOutBuffer)
 }
+
+func TestBufferBuilderAdvanceZeroesReusedStorage(t *testing.T) {
+       mem := memory.NewCheckedAllocator(memory.NewGoAllocator())
+       defer mem.AssertSize(t, 0)
+
+       builder := newByteBufferBuilder(mem)
+       defer builder.Release()
+
+       builder.Append([]byte{1, 2, 3, 4})
+       builder.SetLength(0)
+       builder.Advance(4)
+
+       assert.Equal(t, []byte{0, 0, 0, 0}, builder.Bytes())
+}
diff --git a/arrow/array/builder.go b/arrow/array/builder.go
index fe0112b7..18a799aa 100644
--- a/arrow/array/builder.go
+++ b/arrow/array/builder.go
@@ -314,6 +314,16 @@ func (b *builder) unsafeSetValid(length int) {
        b.length = newLength
 }
 
+func (b *builder) unsafeAppendEmptyValues(data []byte, valueSize, length int) {
+       if length <= 0 {
+               return
+       }
+
+       start := b.length * valueSize
+       memory.Set(data[start:start+length*valueSize], 0)
+       b.unsafeSetValid(length)
+}
+
 func (b *builder) UnsafeAppendBoolToBitmap(isValid bool) {
        if isValid {
                bitutil.SetBit(b.nullBitmap.Bytes(), b.length)
diff --git a/arrow/array/builder_append_empty_values_benchmark_test.go 
b/arrow/array/builder_append_empty_values_benchmark_test.go
new file mode 100644
index 00000000..1729d217
--- /dev/null
+++ b/arrow/array/builder_append_empty_values_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 BenchmarkFixedWidthBuilderAppendEmptyValues(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.AppendEmptyValues(count)
+                                               instance.Release()
+                                       }
+                               })
+                       }
+               })
+       }
+}
diff --git a/arrow/array/builder_append_empty_values_test.go 
b/arrow/array/builder_append_empty_values_test.go
new file mode 100644
index 00000000..1de7bab6
--- /dev/null
+++ b/arrow/array/builder_append_empty_values_test.go
@@ -0,0 +1,145 @@
+// 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 TestFixedWidthBuilderAppendEmptyValues(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.AppendEmptyValues(0)
+                       instance.AppendEmptyValues(-1)
+                       require.Zero(t, instance.Len())
+                       require.Zero(t, instance.Cap())
+                       require.Zero(t, instance.NullN())
+
+                       instance.AppendEmptyValue()
+                       instance.AppendEmptyValues(10)
+                       instance.AppendEmptyValue()
+                       require.Equal(t, 12, instance.Len())
+                       require.Zero(t, instance.NullN())
+
+                       result := instance.NewArray()
+                       defer result.Release()
+                       require.Equal(t, 12, result.Len())
+                       require.Zero(t, result.NullN())
+                       for i := 0; i < result.Len(); i++ {
+                               require.True(t, result.IsValid(i))
+                       }
+
+                       values := result.Data().Buffers()[1]
+                       require.NotNil(t, values)
+                       require.Equal(t, make([]byte, values.Len()), 
values.Bytes())
+               })
+       }
+}
+
+func TestFixedWidthBuilderAppendEmptyValuesClearsReusedStorage(t *testing.T) {
+       t.Run("int32", func(t *testing.T) {
+               mem := memory.NewCheckedAllocator(memory.NewGoAllocator())
+               defer mem.AssertSize(t, 0)
+
+               builder := array.NewInt32Builder(mem)
+               defer builder.Release()
+
+               values := make([]int32, 16)
+               for i := range values {
+                       values[i] = int32(i + 1)
+               }
+               builder.AppendValues(values, nil)
+               builder.Resize(3)
+               builder.AppendEmptyValues(10)
+
+               result := builder.NewInt32Array()
+               defer result.Release()
+               require.Equal(t, 13, result.Len())
+               require.Zero(t, result.NullN())
+               for i := 0; i < result.Len(); i++ {
+                       require.True(t, result.IsValid(i))
+                       if i < 3 {
+                               require.Equal(t, int32(i+1), result.Value(i))
+                       } else {
+                               require.Zero(t, result.Value(i))
+                       }
+               }
+       })
+
+       t.Run("boolean", func(t *testing.T) {
+               mem := memory.NewCheckedAllocator(memory.NewGoAllocator())
+               defer mem.AssertSize(t, 0)
+
+               builder := array.NewBooleanBuilder(mem)
+               defer builder.Release()
+
+               values := make([]bool, 16)
+               for i := range values {
+                       values[i] = true
+               }
+               builder.AppendValues(values, nil)
+               builder.Resize(3)
+               builder.AppendEmptyValues(10)
+
+               result := builder.NewBooleanArray()
+               defer result.Release()
+               require.Equal(t, 13, result.Len())
+               require.Zero(t, result.NullN())
+               for i := 0; i < result.Len(); i++ {
+                       require.True(t, result.IsValid(i))
+                       require.Equal(t, i < 3, result.Value(i))
+               }
+       })
+}
diff --git a/arrow/array/decimal.go b/arrow/array/decimal.go
index aac075a2..d284e2fa 100644
--- a/arrow/array/decimal.go
+++ b/arrow/array/decimal.go
@@ -243,9 +243,15 @@ func (b *baseDecimalBuilder[T]) AppendEmptyValue() {
 }
 
 func (b *baseDecimalBuilder[T]) AppendEmptyValues(n int) {
-       for i := 0; i < n; i++ {
+       if n <= 0 {
+               return
+       }
+       if n == 1 {
                b.AppendEmptyValue()
+               return
        }
+       b.Reserve(n)
+       b.unsafeAppendEmptyValues(b.data.Bytes(), b.traits.BytesRequired(1), n)
 }
 
 func (b *baseDecimalBuilder[T]) UnsafeAppendBoolToBitmap(isValid bool) {
diff --git a/arrow/array/fixedsize_binarybuilder.go 
b/arrow/array/fixedsize_binarybuilder.go
index b11ef52d..b077f6d9 100644
--- a/arrow/array/fixedsize_binarybuilder.go
+++ b/arrow/array/fixedsize_binarybuilder.go
@@ -96,9 +96,16 @@ func (b *FixedSizeBinaryBuilder) AppendEmptyValue() {
 }
 
 func (b *FixedSizeBinaryBuilder) AppendEmptyValues(n int) {
-       for i := 0; i < n; i++ {
+       if n <= 0 {
+               return
+       }
+       if n == 1 {
                b.AppendEmptyValue()
+               return
        }
+       b.Reserve(n)
+       b.values.Advance(n * b.dtype.ByteWidth)
+       b.unsafeSetValid(n)
 }
 
 func (b *FixedSizeBinaryBuilder) UnsafeAppend(v []byte) {
diff --git a/arrow/array/float16_builder.go b/arrow/array/float16_builder.go
index 25e8f4a9..2c198f5d 100644
--- a/arrow/array/float16_builder.go
+++ b/arrow/array/float16_builder.go
@@ -91,9 +91,15 @@ func (b *Float16Builder) AppendEmptyValue() {
 }
 
 func (b *Float16Builder) AppendEmptyValues(n int) {
-       for i := 0; i < n; i++ {
+       if n <= 0 {
+               return
+       }
+       if n == 1 {
                b.AppendEmptyValue()
+               return
        }
+       b.Reserve(n)
+       b.unsafeAppendEmptyValues(b.data.Bytes(), 
arrow.Float16Traits.BytesRequired(1), n)
 }
 
 func (b *Float16Builder) UnsafeAppendBoolToBitmap(isValid bool) {
diff --git a/arrow/array/interval.go b/arrow/array/interval.go
index 5053c3a2..61e9b8fd 100644
--- a/arrow/array/interval.go
+++ b/arrow/array/interval.go
@@ -193,9 +193,15 @@ func (b *MonthIntervalBuilder) AppendEmptyValue() {
 }
 
 func (b *MonthIntervalBuilder) AppendEmptyValues(n int) {
-       for i := 0; i < n; i++ {
+       if n <= 0 {
+               return
+       }
+       if n == 1 {
                b.AppendEmptyValue()
+               return
        }
+       b.Reserve(n)
+       b.unsafeAppendEmptyValues(b.data.Bytes(), 
arrow.MonthIntervalTraits.BytesRequired(1), n)
 }
 
 func (b *MonthIntervalBuilder) UnsafeAppend(v arrow.MonthInterval) {
@@ -503,9 +509,15 @@ func (b *DayTimeIntervalBuilder) AppendEmptyValue() {
 }
 
 func (b *DayTimeIntervalBuilder) AppendEmptyValues(n int) {
-       for i := 0; i < n; i++ {
+       if n <= 0 {
+               return
+       }
+       if n == 1 {
                b.AppendEmptyValue()
+               return
        }
+       b.Reserve(n)
+       b.unsafeAppendEmptyValues(b.data.Bytes(), 
arrow.DayTimeIntervalTraits.BytesRequired(1), n)
 }
 
 func (b *DayTimeIntervalBuilder) UnsafeAppend(v arrow.DayTimeInterval) {
@@ -816,9 +828,15 @@ func (b *MonthDayNanoIntervalBuilder) AppendEmptyValue() {
 }
 
 func (b *MonthDayNanoIntervalBuilder) AppendEmptyValues(n int) {
-       for i := 0; i < n; i++ {
+       if n <= 0 {
+               return
+       }
+       if n == 1 {
                b.AppendEmptyValue()
+               return
        }
+       b.Reserve(n)
+       b.unsafeAppendEmptyValues(b.data.Bytes(), 
arrow.MonthDayNanoIntervalTraits.BytesRequired(1), n)
 }
 
 func (b *MonthDayNanoIntervalBuilder) UnsafeAppend(v 
arrow.MonthDayNanoInterval) {
diff --git a/arrow/array/numericbuilder.gen.go 
b/arrow/array/numericbuilder.gen.go
index b54f3e9d..b189ba51 100644
--- a/arrow/array/numericbuilder.gen.go
+++ b/arrow/array/numericbuilder.gen.go
@@ -88,9 +88,15 @@ func (b *Int64Builder) AppendEmptyValue() {
 }
 
 func (b *Int64Builder) AppendEmptyValues(n int) {
-       for i := 0; i < n; i++ {
+       if n <= 0 {
+               return
+       }
+       if n == 1 {
                b.AppendEmptyValue()
+               return
        }
+       b.Reserve(n)
+       b.unsafeAppendEmptyValues(b.data.Bytes(), 
arrow.Int64Traits.BytesRequired(1), n)
 }
 
 func (b *Int64Builder) UnsafeAppend(v int64) {
@@ -412,9 +418,15 @@ func (b *Uint64Builder) AppendEmptyValue() {
 }
 
 func (b *Uint64Builder) AppendEmptyValues(n int) {
-       for i := 0; i < n; i++ {
+       if n <= 0 {
+               return
+       }
+       if n == 1 {
                b.AppendEmptyValue()
+               return
        }
+       b.Reserve(n)
+       b.unsafeAppendEmptyValues(b.data.Bytes(), 
arrow.Uint64Traits.BytesRequired(1), n)
 }
 
 func (b *Uint64Builder) UnsafeAppend(v uint64) {
@@ -740,9 +752,15 @@ func (b *Float64Builder) AppendEmptyValue() {
 }
 
 func (b *Float64Builder) AppendEmptyValues(n int) {
-       for i := 0; i < n; i++ {
+       if n <= 0 {
+               return
+       }
+       if n == 1 {
                b.AppendEmptyValue()
+               return
        }
+       b.Reserve(n)
+       b.unsafeAppendEmptyValues(b.data.Bytes(), 
arrow.Float64Traits.BytesRequired(1), n)
 }
 
 func (b *Float64Builder) UnsafeAppend(v float64) {
@@ -982,9 +1000,15 @@ func (b *Int32Builder) AppendEmptyValue() {
 }
 
 func (b *Int32Builder) AppendEmptyValues(n int) {
-       for i := 0; i < n; i++ {
+       if n <= 0 {
+               return
+       }
+       if n == 1 {
                b.AppendEmptyValue()
+               return
        }
+       b.Reserve(n)
+       b.unsafeAppendEmptyValues(b.data.Bytes(), 
arrow.Int32Traits.BytesRequired(1), n)
 }
 
 func (b *Int32Builder) UnsafeAppend(v int32) {
@@ -1290,9 +1314,15 @@ func (b *Uint32Builder) AppendEmptyValue() {
 }
 
 func (b *Uint32Builder) AppendEmptyValues(n int) {
-       for i := 0; i < n; i++ {
+       if n <= 0 {
+               return
+       }
+       if n == 1 {
                b.AppendEmptyValue()
+               return
        }
+       b.Reserve(n)
+       b.unsafeAppendEmptyValues(b.data.Bytes(), 
arrow.Uint32Traits.BytesRequired(1), n)
 }
 
 func (b *Uint32Builder) UnsafeAppend(v uint32) {
@@ -1598,9 +1628,15 @@ func (b *Float32Builder) AppendEmptyValue() {
 }
 
 func (b *Float32Builder) AppendEmptyValues(n int) {
-       for i := 0; i < n; i++ {
+       if n <= 0 {
+               return
+       }
+       if n == 1 {
                b.AppendEmptyValue()
+               return
        }
+       b.Reserve(n)
+       b.unsafeAppendEmptyValues(b.data.Bytes(), 
arrow.Float32Traits.BytesRequired(1), n)
 }
 
 func (b *Float32Builder) UnsafeAppend(v float32) {
@@ -1840,9 +1876,15 @@ func (b *Int16Builder) AppendEmptyValue() {
 }
 
 func (b *Int16Builder) AppendEmptyValues(n int) {
-       for i := 0; i < n; i++ {
+       if n <= 0 {
+               return
+       }
+       if n == 1 {
                b.AppendEmptyValue()
+               return
        }
+       b.Reserve(n)
+       b.unsafeAppendEmptyValues(b.data.Bytes(), 
arrow.Int16Traits.BytesRequired(1), n)
 }
 
 func (b *Int16Builder) UnsafeAppend(v int16) {
@@ -2148,9 +2190,15 @@ func (b *Uint16Builder) AppendEmptyValue() {
 }
 
 func (b *Uint16Builder) AppendEmptyValues(n int) {
-       for i := 0; i < n; i++ {
+       if n <= 0 {
+               return
+       }
+       if n == 1 {
                b.AppendEmptyValue()
+               return
        }
+       b.Reserve(n)
+       b.unsafeAppendEmptyValues(b.data.Bytes(), 
arrow.Uint16Traits.BytesRequired(1), n)
 }
 
 func (b *Uint16Builder) UnsafeAppend(v uint16) {
@@ -2456,9 +2504,15 @@ func (b *Int8Builder) AppendEmptyValue() {
 }
 
 func (b *Int8Builder) AppendEmptyValues(n int) {
-       for i := 0; i < n; i++ {
+       if n <= 0 {
+               return
+       }
+       if n == 1 {
                b.AppendEmptyValue()
+               return
        }
+       b.Reserve(n)
+       b.unsafeAppendEmptyValues(b.data.Bytes(), 
arrow.Int8Traits.BytesRequired(1), n)
 }
 
 func (b *Int8Builder) UnsafeAppend(v int8) {
@@ -2764,9 +2818,15 @@ func (b *Uint8Builder) AppendEmptyValue() {
 }
 
 func (b *Uint8Builder) AppendEmptyValues(n int) {
-       for i := 0; i < n; i++ {
+       if n <= 0 {
+               return
+       }
+       if n == 1 {
                b.AppendEmptyValue()
+               return
        }
+       b.Reserve(n)
+       b.unsafeAppendEmptyValues(b.data.Bytes(), 
arrow.Uint8Traits.BytesRequired(1), n)
 }
 
 func (b *Uint8Builder) UnsafeAppend(v uint8) {
@@ -3073,9 +3133,15 @@ func (b *Time32Builder) AppendEmptyValue() {
 }
 
 func (b *Time32Builder) AppendEmptyValues(n int) {
-       for i := 0; i < n; i++ {
+       if n <= 0 {
+               return
+       }
+       if n == 1 {
                b.AppendEmptyValue()
+               return
        }
+       b.Reserve(n)
+       b.unsafeAppendEmptyValues(b.data.Bytes(), 
arrow.Time32Traits.BytesRequired(1), n)
 }
 
 func (b *Time32Builder) UnsafeAppend(v arrow.Time32) {
@@ -3333,9 +3399,15 @@ func (b *Time64Builder) AppendEmptyValue() {
 }
 
 func (b *Time64Builder) AppendEmptyValues(n int) {
-       for i := 0; i < n; i++ {
+       if n <= 0 {
+               return
+       }
+       if n == 1 {
                b.AppendEmptyValue()
+               return
        }
+       b.Reserve(n)
+       b.unsafeAppendEmptyValues(b.data.Bytes(), 
arrow.Time64Traits.BytesRequired(1), n)
 }
 
 func (b *Time64Builder) UnsafeAppend(v arrow.Time64) {
@@ -3583,9 +3655,15 @@ func (b *Date32Builder) AppendEmptyValue() {
 }
 
 func (b *Date32Builder) AppendEmptyValues(n int) {
-       for i := 0; i < n; i++ {
+       if n <= 0 {
+               return
+       }
+       if n == 1 {
                b.AppendEmptyValue()
+               return
        }
+       b.Reserve(n)
+       b.unsafeAppendEmptyValues(b.data.Bytes(), 
arrow.Date32Traits.BytesRequired(1), n)
 }
 
 func (b *Date32Builder) UnsafeAppend(v arrow.Date32) {
@@ -3842,9 +3920,15 @@ func (b *Date64Builder) AppendEmptyValue() {
 }
 
 func (b *Date64Builder) AppendEmptyValues(n int) {
-       for i := 0; i < n; i++ {
+       if n <= 0 {
+               return
+       }
+       if n == 1 {
                b.AppendEmptyValue()
+               return
        }
+       b.Reserve(n)
+       b.unsafeAppendEmptyValues(b.data.Bytes(), 
arrow.Date64Traits.BytesRequired(1), n)
 }
 
 func (b *Date64Builder) UnsafeAppend(v arrow.Date64) {
@@ -4093,9 +4177,15 @@ func (b *DurationBuilder) AppendEmptyValue() {
 }
 
 func (b *DurationBuilder) AppendEmptyValues(n int) {
-       for i := 0; i < n; i++ {
+       if n <= 0 {
+               return
+       }
+       if n == 1 {
                b.AppendEmptyValue()
+               return
        }
+       b.Reserve(n)
+       b.unsafeAppendEmptyValues(b.data.Bytes(), 
arrow.DurationTraits.BytesRequired(1), n)
 }
 
 func (b *DurationBuilder) UnsafeAppend(v arrow.Duration) {
diff --git a/arrow/array/numericbuilder.gen.go.tmpl 
b/arrow/array/numericbuilder.gen.go.tmpl
index 1f7d433e..0a25279c 100644
--- a/arrow/array/numericbuilder.gen.go.tmpl
+++ b/arrow/array/numericbuilder.gen.go.tmpl
@@ -98,9 +98,15 @@ func (b *{{.Name}}Builder) AppendEmptyValue() {
 }
 
 func (b *{{.Name}}Builder) AppendEmptyValues(n int) {
-       for i := 0; i < n; i ++ {
+       if n <= 0 {
+               return
+       }
+       if n == 1 {
                b.AppendEmptyValue()
+               return
        }
+       b.Reserve(n)
+       b.unsafeAppendEmptyValues(b.data.Bytes(), 
arrow.{{.Name}}Traits.BytesRequired(1), n)
 }
 
 func (b *{{.Name}}Builder) UnsafeAppend(v {{or .QualifiedType .Type}}) {
diff --git a/arrow/array/timestamp.go b/arrow/array/timestamp.go
index 523c3a01..c3d9f190 100644
--- a/arrow/array/timestamp.go
+++ b/arrow/array/timestamp.go
@@ -215,9 +215,15 @@ func (b *TimestampBuilder) AppendEmptyValue() {
 }
 
 func (b *TimestampBuilder) AppendEmptyValues(n int) {
-       for i := 0; i < n; i++ {
+       if n <= 0 {
+               return
+       }
+       if n == 1 {
                b.AppendEmptyValue()
+               return
        }
+       b.Reserve(n)
+       b.unsafeAppendEmptyValues(b.data.Bytes(), 
arrow.TimestampTraits.BytesRequired(1), n)
 }
 
 func (b *TimestampBuilder) UnsafeAppend(v arrow.Timestamp) {

Reply via email to