Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
eec8732
new: sort_indices kernel, Sort(), SortIndices() compute methods
hamilton-earthscope Apr 10, 2026
eaad043
fix: ensure description text does not exceed 78 chars per line
hamilton-earthscope Apr 10, 2026
be0e54c
fix: too many releases; don't take ownership of caller-owned memory
hamilton-earthscope Apr 10, 2026
9717046
test: mirror c++ test suite
hamilton-earthscope Apr 10, 2026
e996a0e
new: fixed-size binary sort support (and FSB extension e.g. UUID)
hamilton-earthscope Apr 10, 2026
b211fa2
fix: linter
hamilton-earthscope Apr 10, 2026
ba977db
review: remove `compute` struct tags
hamilton-earthscope Apr 10, 2026
47ec44d
review: add TODO for future extension type sort capability
hamilton-earthscope Apr 10, 2026
fde6343
review: rename variable
hamilton-earthscope Apr 10, 2026
6a486fc
review: simplify for loop
hamilton-earthscope Apr 10, 2026
92de905
review: switch to arrow.GetData[uint64]() syntax
hamilton-earthscope Apr 10, 2026
8599704
review: clarify kernel's parallel keys/columns slice inputs
hamilton-earthscope Apr 10, 2026
aba8aab
review: replace repeated code with utility function
hamilton-earthscope Apr 10, 2026
8fbc242
review: simplify indexing by passing relevant segment into visitConst…
hamilton-earthscope Apr 10, 2026
2dafe6e
review: implement Cmp(T) interface for interval types
hamilton-earthscope Apr 10, 2026
073d111
review: pass user ctx into KernelCtx
hamilton-earthscope Apr 10, 2026
1f146f3
review: switch build constraint to 1.22, use concise range syntax
hamilton-earthscope Apr 10, 2026
5c063ed
review: bump compute/registry.go build constraint to 1.22
hamilton-earthscope Apr 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
481 changes: 481 additions & 0 deletions arrow/compute/internal/kernels/vector_sort.go

Large diffs are not rendered by default.

175 changes: 175 additions & 0 deletions arrow/compute/internal/kernels/vector_sort_bench_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
// 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.22

package kernels

import (
"context"
"fmt"
"testing"

"github.com/apache/arrow-go/v18/arrow"
"github.com/apache/arrow-go/v18/arrow/array"
"github.com/apache/arrow-go/v18/arrow/compute/exec"
"github.com/apache/arrow-go/v18/arrow/memory"
)

// Benchmarks target kernels.SortIndices (chunked comparators + stable sort) without compute
// registry or CallFunction overhead. Use e.g.:
//
// go test -bench=BenchmarkSortIndices -benchmem -cpuprofile=cpu.prof ./arrow/compute/internal/kernels/
// go tool pprof -http=:8080 cpu.prof

func newBenchKernelCtx(tb testing.TB) (*exec.KernelCtx, memory.Allocator) {
tb.Helper()
mem := memory.NewGoAllocator()
ctx := &exec.KernelCtx{Ctx: exec.WithAllocator(context.Background(), mem)}
return ctx, mem
}

// makeChunkedInt64Split returns n int64 rows in numChunks contiguous arrays. Values are a
// deterministic function of global row index so the sort does non-trivial work.
func makeChunkedInt64Split(tb testing.TB, mem memory.Allocator, n, numChunks int) *arrow.Chunked {
tb.Helper()
if numChunks < 1 {
numChunks = 1
}
if n < numChunks {
numChunks = n
}
base := n / numChunks
rem := n % numChunks
chunks := make([]arrow.Array, 0, numChunks)
global := 0
for c := range numChunks {
sz := base
if c < rem {
sz++
}
bld := array.NewInt64Builder(mem)
for i := range sz {
x := int64(global + i)
bld.Append((x * 6364136223846793005) ^ (x >> 12))
}
arr := bld.NewArray()
chunks = append(chunks, arr)
global += sz
}
ch := arrow.NewChunked(arrow.PrimitiveTypes.Int64, chunks)
tb.Cleanup(func() { ch.Release() })
return ch
}

func BenchmarkSortIndices_Int64(b *testing.B) {
const rows = 65536
for _, numChunks := range []int{1, 16, 128} {
b.Run(fmt.Sprintf("rows=%d/chunks=%d", rows, numChunks), func(b *testing.B) {
ctx, mem := newBenchKernelCtx(b)
col := makeChunkedInt64Split(b, mem, rows, numChunks)
keys := []SortKey{{ColumnIndex: 0, Order: Ascending, NullPlacement: NullsAtEnd}}
columns := []*arrow.Chunked{col}

b.ReportAllocs()
b.ResetTimer()
for range b.N {
res, err := SortIndices(ctx, columns, keys)
if err != nil {
b.Fatal(err)
}
res.Release()
}
})
}
}

func BenchmarkSortIndices_Int64_TwoKeys(b *testing.B) {
const rows = 65536
const numChunks = 64
ctx, mem := newBenchKernelCtx(b)
colA := makeChunkedInt64Split(b, mem, rows, numChunks)
colB := makeChunkedInt64Split(b, mem, rows, numChunks)
keys := []SortKey{
{ColumnIndex: 0, Order: Ascending, NullPlacement: NullsAtEnd},
{ColumnIndex: 1, Order: Descending, NullPlacement: NullsAtStart},
}
columns := []*arrow.Chunked{colA, colB}

b.ReportAllocs()
b.ResetTimer()
for range b.N {
res, err := SortIndices(ctx, columns, keys)
if err != nil {
b.Fatal(err)
}
res.Release()
}
}

func makeChunkedStringSplit(tb testing.TB, mem memory.Allocator, n, numChunks int) *arrow.Chunked {
tb.Helper()
if numChunks < 1 {
numChunks = 1
}
if n < numChunks {
numChunks = n
}
base := n / numChunks
rem := n % numChunks
chunks := make([]arrow.Array, 0, numChunks)
global := 0
for c := range numChunks {
sz := base
if c < rem {
sz++
}
bld := array.NewStringBuilder(mem)
for i := range sz {
x := global + i
v := (x * 6364136223846793005) ^ (x >> 12)
bld.Append(fmt.Sprintf("%016x", v))
}
arr := bld.NewArray()
chunks = append(chunks, arr)
global += sz
}
ch := arrow.NewChunked(arrow.BinaryTypes.String, chunks)
tb.Cleanup(func() { ch.Release() })
return ch
}

func BenchmarkSortIndices_String(b *testing.B) {
const rows = 65536
for _, numChunks := range []int{1, 32} {
b.Run(fmt.Sprintf("rows=%d/chunks=%d", rows, numChunks), func(b *testing.B) {
ctx, mem := newBenchKernelCtx(b)
col := makeChunkedStringSplit(b, mem, rows, numChunks)
keys := []SortKey{{ColumnIndex: 0, Order: Ascending, NullPlacement: NullsAtEnd}}
columns := []*arrow.Chunked{col}

b.ReportAllocs()
b.ResetTimer()
for range b.N {
res, err := SortIndices(ctx, columns, keys)
if err != nil {
b.Fatal(err)
}
res.Release()
}
})
}
}
Loading