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 72e9a569 perf(compute): add ARM64 NEON compression for null-free
32-bit fixed-width filters (#1286)
72e9a569 is described below
commit 72e9a5695337bab9ac01ec1e4d58177bc4949dc3
Author: Minh Vu <[email protected]>
AuthorDate: Fri Sep 4 18:46:17 2026 +0200
perf(compute): add ARM64 NEON compression for null-free 32-bit fixed-width
filters (#1286)
## Summary
* **Add an ARM64 NEON path** for null-free 32-bit fixed-width filters.
* **Compact two groups of four values** with table-driven `VTBL`.
* **Use exact-size stores** for the selected values.
* Keep the current fallbacks for nulls, short or unaligned inputs, dense
masks, unsupported CPUs, and `noasm` builds.
## Benchmark
Hardware: Apple M1 Pro, macOS arm64, Go 1.26.3.
Command: `go test ./arrow/compute -run '^$' -bench
'^BenchmarkFilter(Int32|Float32)MixedMasks$' -benchtime=100ms -count=3
-benchmem`
The baseline used the same command with `GODEBUG=cpu.all=off`, which
forces the existing scalar path. Values are median ns/op from three
runs. Pattern order is `alternating / random25 / random50 / random75 /
clustered50 / all-selected / none-selected`.
| Type | Rows | Baseline ns/op | NEON ns/op | Speedup |
| --- | ---: | --- | --- | --- |
| int32 | 1K | 9002 / 6043 / 7167 / 6126 / 4336 / 4603 / 3376 | 4398 /
4345 / 4617 / 4679 / 4548 / 4812 / 3432 | 2.05x / 1.39x / 1.55x / 1.31x
/ 0.95x / 0.96x / 0.98x |
| int32 | 64K | 274954 / 145437 / 226114 / 192618 / 36169 / 45217 / 6176
| 37957 / 69222 / 90711 / 71447 / 35481 / 46478 / 6306 | 7.24x / 2.10x /
2.49x / 2.70x / 1.02x / 0.97x / 0.98x |
| int32 | 1M | 4016235 / 2118964 / 3441839 / 2839365 / 300915 / 232850 /
48657 | 340022 / 1273778 / 1530240 / 1312927 / 291230 / 235818 / 48973 |
11.81x / 1.66x / 2.25x / 2.16x / 1.03x / 0.99x / 0.99x |
| float32 | 1K | 8875 / 5979 / 7188 / 6157 / 4455 / 4647 / 3373 | 4428 /
4286 / 4579 / 4686 / 4441 / 4844 / 3418 | 2.00x / 1.40x / 1.57x / 1.31x
/ 1.00x / 0.96x / 0.99x |
| float32 | 64K | 267098 / 145365 / 226897 / 193153 / 35817 / 44286 /
6171 | 37340 / 71035 / 93770 / 73563 / 36372 / 44714 / 6239 | 7.15x /
2.05x / 2.42x / 2.63x / 0.98x / 0.99x / 0.99x |
| float32 | 1M | 4061853 / 2116060 / 3435942 / 2850531 / 304719 / 272701
/ 48689 | 344281 / 1268462 / 1516895 / 1300810 / 293596 / 233508 / 49292
| 11.80x / 1.67x / 2.27x / 2.19x / 1.04x / 1.17x / 0.99x |
Fallback controls stayed on the existing path:
| Control | Baseline ns/op | NEON build ns/op |
| --- | ---: | ---: |
| unaligned filter offset 3 | 280053 | 271730 |
| short input, 56 rows | 4436 | 4392 |
| non-multiple length, 65537 rows | 272665 | 268016 |
## Tests
* `go test ./arrow/compute/... -count=1`
* `go test -race ./arrow/compute/internal/kernels ./arrow/compute
-count=1`
* `go test -tags noasm ./arrow/compute/internal/kernels ./arrow/compute
-count=1`
* `GOARCH=amd64 go test -c -o /dev/null
./arrow/compute/internal/kernels`
* `GOARCH=amd64 go test -c -o /dev/null ./arrow/compute`
## Limitations
* This is limited to ARM64 ASIMD, 32-bit fixed-width values, no value
nulls, no filter nulls, aligned filter bitmap offsets, and lengths
divisible by 8.
* All other cases keep using the existing general or specialized paths.
---
.../internal/kernels/filter_uint32_neon_arm64.go | 90 +++++++++++++
.../internal/kernels/filter_uint32_neon_arm64.s | 149 +++++++++++++++++++++
.../kernels/filter_uint32_neon_arm64_test.go | 133 ++++++++++++++++++
.../internal/kernels/filter_uint32_neon_noasm.go | 23 ++++
arrow/compute/internal/kernels/vector_selection.go | 3 +-
arrow/compute/vector_selection_bench_test.go | 80 ++++++++++-
6 files changed, 474 insertions(+), 4 deletions(-)
diff --git a/arrow/compute/internal/kernels/filter_uint32_neon_arm64.go
b/arrow/compute/internal/kernels/filter_uint32_neon_arm64.go
new file mode 100644
index 00000000..c5de5a1e
--- /dev/null
+++ b/arrow/compute/internal/kernels/filter_uint32_neon_arm64.go
@@ -0,0 +1,90 @@
+// 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.
+
+//go:build go1.18 && arm64 && !noasm && !appengine
+
+package kernels
+
+import (
+ "math/bits"
+ "unsafe"
+
+ "golang.org/x/sys/cpu"
+)
+
+var filterUint32NeonTables = makeFilterUint32NeonTables()
+
+func makeFilterUint32NeonTables() (tables [352]byte) {
+ for mask := 0; mask < 16; mask++ {
+ pos := 0
+ for lane := 0; lane < 4; lane++ {
+ if mask&(1<<uint(lane)) == 0 {
+ continue
+ }
+ for byteInLane := 0; byteInLane < 4; byteInLane++ {
+ tables[mask*16+pos] = byte(lane*4 + byteInLane)
+ pos++
+ }
+ }
+ for ; pos < 16; pos++ {
+ tables[mask*16+pos] = 0x80
+ }
+ }
+
+ for mask := 0; mask < 16; mask++ {
+ tables[336+mask] = byte(bits.OnesCount8(uint8(mask)))
+ }
+ return tables
+}
+
+//go:noescape
+func _filter_uint32_neon(values, filter, output, tables unsafe.Pointer, length
int64)
+
+func filterUint32Neon(values []uint32, output []uint32, filterData []byte,
filterOffset, length int64) bool {
+ if !cpu.ARM64.HasASIMD || length < 64 || length%8 != 0 ||
filterOffset%8 != 0 {
+ return false
+ }
+
+ numBytes := length / 8
+ filterByteOffset := filterOffset / 8
+ if filterByteOffset < 0 || filterByteOffset+numBytes >
int64(len(filterData)) {
+ return false
+ }
+
+ mixedBytes := 0
+ const sampleBytes = 64
+ for i := int64(0); i < numBytes && i < sampleBytes; i++ {
+ mask := filterData[filterByteOffset+i]
+ if mask != 0 && mask != 0xff {
+ mixedBytes++
+ if mixedBytes == 4 {
+ break
+ }
+ }
+ }
+ if mixedBytes < 4 || len(output) == 0 {
+ return false
+ }
+
+ _filter_uint32_neon(
+ unsafe.Pointer(&values[0]),
+ unsafe.Pointer(&filterData[filterByteOffset]),
+ unsafe.Pointer(&output[0]),
+ unsafe.Pointer(&filterUint32NeonTables[0]),
+ length,
+ )
+ return true
+}
diff --git a/arrow/compute/internal/kernels/filter_uint32_neon_arm64.s
b/arrow/compute/internal/kernels/filter_uint32_neon_arm64.s
new file mode 100644
index 00000000..032daea7
--- /dev/null
+++ b/arrow/compute/internal/kernels/filter_uint32_neon_arm64.s
@@ -0,0 +1,149 @@
+// 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.
+
+//go:build go1.18 && arm64 && !noasm && !appengine
+
+#include "textflag.h"
+
+// Each filter byte selects up to four uint32 values from each of two
+// four-value halves. The table-driven VTBL compacts one half in-place. The
+// count-specific stores write exactly the selected number of bytes, because
+// the output allocation is sized to the filter's population count.
+
+// func _filter_uint32_neon(values, filter, output, tables unsafe.Pointer,
length int64)
+TEXT ยท_filter_uint32_neon(SB), NOSPLIT, $0-40
+ MOVD values+0(FP), R0
+ MOVD filter+8(FP), R1
+ MOVD output+16(FP), R2
+ MOVD tables+24(FP), R3
+ MOVD length+32(FP), R4
+
+ LSR $3, R4, R4
+
+filter_loop:
+ MOVBU (R1), R5
+ ADD $1, R1
+ CBZ R5, filter_next
+
+ CMPW $255, R5
+ BEQ filter_all
+
+ VLD1 (R0), [V0.B16]
+ ADD $16, R0, R8
+ VLD1 (R8), [V1.B16]
+
+ ANDW $15, R5, R6
+ ADD $336, R3, R8
+ MOVBU (R8)(R6<<0), R9
+ CBZ R9, filter_high
+
+ LSL $4, R6, R8
+ ADD R3, R8, R8
+ VLD1 (R8), [V2.B16]
+ VTBL V2.B16, [V0.B16], V0.B16
+
+ CMPW $1, R9
+ BEQ filter_low_store1
+ CMPW $2, R9
+ BEQ filter_low_store2
+ CMPW $3, R9
+ BEQ filter_low_store3
+ VST1 [V0.S4], (R2)
+ ADD $16, R2
+ B filter_high
+
+filter_low_store1:
+ VMOV V0.S[0], R8
+ MOVW R8, (R2)
+ ADD $4, R2
+ B filter_high
+
+filter_low_store2:
+ VMOV V0.S[0], R8
+ MOVW R8, (R2)
+ VMOV V0.S[1], R8
+ MOVW R8, 4(R2)
+ ADD $8, R2
+ B filter_high
+
+filter_low_store3:
+ VMOV V0.S[0], R8
+ MOVW R8, (R2)
+ VMOV V0.S[1], R8
+ MOVW R8, 4(R2)
+ VMOV V0.S[2], R8
+ MOVW R8, 8(R2)
+ ADD $12, R2
+
+filter_high:
+ LSR $4, R5, R6
+ ADD $336, R3, R8
+ MOVBU (R8)(R6<<0), R9
+ CBZ R9, filter_next
+
+ LSL $4, R6, R8
+ ADD R3, R8, R8
+ VLD1 (R8), [V2.B16]
+ VTBL V2.B16, [V1.B16], V1.B16
+
+ CMPW $1, R9
+ BEQ filter_high_store1
+ CMPW $2, R9
+ BEQ filter_high_store2
+ CMPW $3, R9
+ BEQ filter_high_store3
+ VST1 [V1.S4], (R2)
+ ADD $16, R2
+ B filter_next
+
+filter_high_store1:
+ VMOV V1.S[0], R8
+ MOVW R8, (R2)
+ ADD $4, R2
+ B filter_next
+
+filter_high_store2:
+ VMOV V1.S[0], R8
+ MOVW R8, (R2)
+ VMOV V1.S[1], R8
+ MOVW R8, 4(R2)
+ ADD $8, R2
+ B filter_next
+
+filter_high_store3:
+ VMOV V1.S[0], R8
+ MOVW R8, (R2)
+ VMOV V1.S[1], R8
+ MOVW R8, 4(R2)
+ VMOV V1.S[2], R8
+ MOVW R8, 8(R2)
+ ADD $12, R2
+ B filter_next
+
+filter_all:
+ VLD1 (R0), [V0.B16]
+ ADD $16, R0, R8
+ VLD1 (R8), [V1.B16]
+ VST1 [V0.B16], (R2)
+ ADD $16, R2
+ VST1 [V1.B16], (R2)
+ ADD $16, R2
+
+filter_next:
+ ADD $32, R0
+ SUBS $1, R4, R4
+ BNE filter_loop
+ RET
diff --git a/arrow/compute/internal/kernels/filter_uint32_neon_arm64_test.go
b/arrow/compute/internal/kernels/filter_uint32_neon_arm64_test.go
new file mode 100644
index 00000000..d2d9d6ea
--- /dev/null
+++ b/arrow/compute/internal/kernels/filter_uint32_neon_arm64_test.go
@@ -0,0 +1,133 @@
+// 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.
+
+//go:build go1.18 && arm64 && !noasm && !appengine
+
+package kernels
+
+import (
+ "testing"
+
+ "golang.org/x/sys/cpu"
+)
+
+func TestFilterUint32NeonAllMasks(t *testing.T) {
+ if !cpu.ARM64.HasASIMD {
+ t.Skip("ARM64 SIMD is not available")
+ }
+
+ const (
+ bytes = 256
+ length = bytes * 8
+ )
+ values := make([]uint32, length)
+ filter := make([]byte, bytes)
+ for i := range values {
+ values[i] = uint32(i*17 + 3)
+ }
+ for i := range filter {
+ filter[i] = byte(i)
+ }
+
+ expected := make([]uint32, 0, length)
+ for i, mask := range filter {
+ for lane := 0; lane < 8; lane++ {
+ if mask&(1<<uint(lane)) != 0 {
+ expected = append(expected, values[i*8+lane])
+ }
+ }
+ }
+
+ backing := make([]uint32, len(expected)+1)
+ backing[len(expected)] = 0xdeadbeef
+ if !filterUint32Neon(values, backing[:len(expected)], filter, 0,
length) {
+ t.Fatal("filterUint32Neon did not select the mixed-mask path")
+ }
+ for i, want := range expected {
+ if got := backing[i]; got != want {
+ t.Fatalf("output[%d] = %d, want %d", i, got, want)
+ }
+ }
+ if got := backing[len(expected)]; got != 0xdeadbeef {
+ t.Fatalf("sentinel = %#x, want %#x", got, uint32(0xdeadbeef))
+ }
+}
+
+func TestFilterUint32NeonAlignedOffset(t *testing.T) {
+ if !cpu.ARM64.HasASIMD {
+ t.Skip("ARM64 SIMD is not available")
+ }
+
+ const (
+ offset = 8
+ length = 64
+ )
+ values := make([]uint32, length)
+ for i := range values {
+ values[i] = uint32(i)
+ }
+ filter := make([]byte, offset/8+length/8)
+ for i := range filter[offset/8:] {
+ filter[offset/8+i] = 0x55
+ }
+
+ output := make([]uint32, length/2)
+ if !filterUint32Neon(values, output, filter, offset, length) {
+ t.Fatal("filterUint32Neon did not select the aligned-offset
path")
+ }
+ for i, got := range output {
+ want := uint32(2 * i)
+ if got != want {
+ t.Fatalf("output[%d] = %d, want %d", i, got, want)
+ }
+ }
+}
+
+func TestFilterUint32NeonGuards(t *testing.T) {
+ if !cpu.ARM64.HasASIMD {
+ t.Skip("ARM64 SIMD is not available")
+ }
+
+ values := make([]uint32, 128)
+ mixed := make([]byte, 16)
+ for i := range mixed {
+ mixed[i] = 0x55
+ }
+ output := make([]uint32, 64)
+
+ tests := []struct {
+ name string
+ filter []byte
+ offset int64
+ length int64
+ want bool
+ }{
+ {name: "short", filter: mixed, length: 56},
+ {name: "non-multiple-of-eight", filter: mixed, length: 72 - 1},
+ {name: "unaligned-filter-offset", filter: mixed, offset: 3,
length: 64},
+ {name: "all-false", filter: make([]byte, 8), length: 64},
+ {name: "all-true", filter: []byte{0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff}, length: 64},
+ {name: "too-few-mixed-bytes", filter: []byte{0x55, 0x55, 0x55,
0x00, 0x00, 0x00, 0x00, 0x00}, length: 64},
+ {name: "out-of-bounds-filter", filter: mixed[:4], length: 64},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if got := filterUint32Neon(values, output, tt.filter,
tt.offset, tt.length); got != tt.want {
+ t.Fatalf("filterUint32Neon() = %t, want %t",
got, tt.want)
+ }
+ })
+ }
+}
diff --git a/arrow/compute/internal/kernels/filter_uint32_neon_noasm.go
b/arrow/compute/internal/kernels/filter_uint32_neon_noasm.go
new file mode 100644
index 00000000..66e28f3a
--- /dev/null
+++ b/arrow/compute/internal/kernels/filter_uint32_neon_noasm.go
@@ -0,0 +1,23 @@
+// 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.
+
+//go:build go1.18 && (noasm || !arm64 || appengine)
+
+package kernels
+
+func filterUint32Neon([]uint32, []uint32, []byte, int64, int64) bool {
+ return false
+}
diff --git a/arrow/compute/internal/kernels/vector_selection.go
b/arrow/compute/internal/kernels/vector_selection.go
index 09454116..32a6f4b7 100644
--- a/arrow/compute/internal/kernels/vector_selection.go
+++ b/arrow/compute/internal/kernels/vector_selection.go
@@ -490,7 +490,8 @@ func PrimitiveFilter(ctx *exec.KernelCtx, batch
*exec.ExecSpan, out *exec.ExecRe
if bitWidth == 32 && values.Nulls == 0 && filter.Nulls == 0 {
valuesData := exec.GetSpanValues[uint32](values, 1)
outData := exec.GetSpanValues[uint32](out, 1)
- if filterUint32Avx2(valuesData, outData, filter.Buffers[1].Buf,
filter.Offset, values.Len) {
+ if filterUint32Avx2(valuesData, outData, filter.Buffers[1].Buf,
filter.Offset, values.Len) ||
+ filterUint32Neon(valuesData, outData,
filter.Buffers[1].Buf, filter.Offset, values.Len) {
return nil
}
}
diff --git a/arrow/compute/vector_selection_bench_test.go
b/arrow/compute/vector_selection_bench_test.go
index b5e8993e..4a14f6ad 100644
--- a/arrow/compute/vector_selection_bench_test.go
+++ b/arrow/compute/vector_selection_bench_test.go
@@ -32,6 +32,16 @@ import (
var benchmarkFilterOutputLength int
func BenchmarkFilterInt32MixedMasks(b *testing.B) {
+ benchmarkFilter32MixedMasks(b, makeFilterInt32BenchmarkInput)
+}
+
+func BenchmarkFilterFloat32MixedMasks(b *testing.B) {
+ benchmarkFilter32MixedMasks(b, makeFilterFloat32BenchmarkInput)
+}
+
+func benchmarkFilter32MixedMasks(b *testing.B,
+ makeInput func(*testing.B, int, func(int) bool) (arrow.Array,
arrow.Array),
+) {
patterns := []struct {
name string
selected func(int) bool
@@ -50,7 +60,7 @@ func BenchmarkFilterInt32MixedMasks(b *testing.B) {
for _, pattern := range patterns {
pattern := pattern
b.Run(fmt.Sprintf("size=%d/%s", size, pattern.name),
func(b *testing.B) {
- values, filter :=
makeFilterInt32BenchmarkInput(b, size, pattern.selected)
+ values, filter := makeInput(b, size,
pattern.selected)
defer values.Release()
defer filter.Release()
@@ -70,16 +80,80 @@ func BenchmarkFilterInt32MixedMasks(b *testing.B) {
}
}
+func BenchmarkFilterInt32FallbackControls(b *testing.B) {
+ controls := []struct {
+ name string
+ size int
+ offset int64
+ }{
+ {name: "unaligned-filter-offset", size: 1 << 16, offset: 3},
+ {name: "short", size: 56},
+ {name: "non-multiple-of-eight", size: 1<<16 + 1},
+ }
+
+ for _, control := range controls {
+ control := control
+ b.Run(control.name, func(b *testing.B) {
+ values, filter :=
makeFilterInt32BenchmarkInputWithOffset(b, control.size, control.offset,
+ func(i int) bool { return i%2 == 0 })
+ defer values.Release()
+ defer filter.Release()
+
+ b.ReportAllocs()
+ b.SetBytes(int64(control.size * 4))
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ result, err :=
compute.FilterArray(context.Background(), values, filter,
*compute.DefaultFilterOptions())
+ if err != nil {
+ b.Fatal(err)
+ }
+ benchmarkFilterOutputLength = result.Len()
+ result.Release()
+ }
+ })
+ }
+}
+
func makeFilterInt32BenchmarkInput(b *testing.B, size int, selected func(int)
bool) (arrow.Array, arrow.Array) {
+ return makeFilterInt32BenchmarkInputWithOffset(b, size, 0, selected)
+}
+
+func makeFilterInt32BenchmarkInputWithOffset(b *testing.B, size int, offset
int64, selected func(int) bool) (arrow.Array, arrow.Array) {
b.Helper()
mem := memory.DefaultAllocator
valuesBuilder := array.NewInt32Builder(mem)
+ valuesBuilder.Reserve(size + int(offset))
+ for i := 0; i < size+int(offset); i++ {
+ valuesBuilder.Append(int32(i))
+ }
+ valuesBase := valuesBuilder.NewInt32Array()
+ valuesBuilder.Release()
+ values := array.NewSlice(valuesBase, offset, offset+int64(size))
+ valuesBase.Release()
+
+ filterBuilder := array.NewBooleanBuilder(mem)
+ filterBuilder.Reserve(size + int(offset))
+ for i := 0; i < size+int(offset); i++ {
+ filterBuilder.Append(selected(i))
+ }
+ filterBase := filterBuilder.NewBooleanArray()
+ filterBuilder.Release()
+ filter := array.NewSlice(filterBase, offset, offset+int64(size))
+ filterBase.Release()
+ return values, filter
+}
+
+func makeFilterFloat32BenchmarkInput(b *testing.B, size int, selected
func(int) bool) (arrow.Array, arrow.Array) {
+ b.Helper()
+ mem := memory.DefaultAllocator
+
+ valuesBuilder := array.NewFloat32Builder(mem)
valuesBuilder.Reserve(size)
for i := 0; i < size; i++ {
- valuesBuilder.Append(int32(i))
+ valuesBuilder.Append(float32(i))
}
- values := valuesBuilder.NewInt32Array()
+ values := valuesBuilder.NewFloat32Array()
valuesBuilder.Release()
filterBuilder := array.NewBooleanBuilder(mem)