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) {