Skip to content

feat(arrow/compute): sort support#749

Open
hamilton-earthscope wants to merge 6 commits intoapache:mainfrom
hamilton-earthscope:sort
Open

feat(arrow/compute): sort support#749
hamilton-earthscope wants to merge 6 commits intoapache:mainfrom
hamilton-earthscope:sort

Conversation

@hamilton-earthscope
Copy link
Copy Markdown
Contributor

@hamilton-earthscope hamilton-earthscope commented Apr 4, 2026

Summary

Implements stable sort_indices (and sort via take) for arrays, chunked arrays, record batches, and tables using logical row indices over Chunked data without concatenating chunks. The control flow and ordering rules are modeled on Apache Arrow C++ vector_sort.cc / vector_sort_internal.h, with a few Go- and performance-driven differences called out below.

Parity with Arrow C++ (vector_sort.cc / vector_sort_internal.h)

Same overall structure

  • Single sort key, one column

    • Multiple chunks: per-chunk sort then pairwise merge of sorted spans (C++ ChunkedArraySorter / ChunkedMergeImpl idea).
    • Single chunk, no validity nulls and no null-likes: direct stable sort on indices (C++ skips null partitioning when null_count == 0 and there are no null-likes).
    • Otherwise: partition validity nulls, partition float null-likes (NaN), stable sort of finite values, then VisitConstantRanges-style handling of ties (vector_sort_internal.go).
  • Multiple sort keys

    • len(keys) <= kMaxRadixSortKeys (8): MSD radix path per record-batch range (radixRecordBatchSortRangeConcreteRecordBatchColumnSorter::SortRange).
    • More than 8 keys: MultipleKeyRecordBatchSorter-style global stable sort with lexicographic compare across keys (multipleKeyRecordBatchSortRange).
    • Aligned chunk boundaries across all keyed columns (typical table): sort each chunk slice with the same strategy, then merge spans like C++ TableSorter batch merge.

Same ordering semantics (intended match to C++)

  • Per-key ascending / descending and null placement (including NaN as null-like for floats).
  • Stable ordering: merge and slices.SortStableFunc are used so tie-breaking matches the C++ “left before right” stable merge behavior where documented in code.

Same “column comparator” role

  • Go columnComparator interface ↔ C++ ColumnComparator: compareRowsForKey, null / null-like metadata, columnHasValidityNulls (skip PartitionNullsOnly when there are no validity nulls).

Physical types

  • One monomorphic comparator type per supported physical pattern in vector_sort_physical.go, analogous to C++ ConcreteColumnComparator<T> (concrete *array.T + direct Value / Cmp / special cases for bool and intervals).

Intentional differences and rationale

Area C++ This Go port
Resolving logical row → (chunk, offset) Chunk / resolver machinery in C++ Dense logicalRowMap: one rowMapCell{chunk, local} per logical row when len(chunks) > 1; pair(i,j) resolves two rows in one shot. Why: random compares during sort/merge need O(1) resolution; a flat table + co-located fields beats repeated resolver work and improves locality vs separate chunk/local slices.
physicalColumnBase methods N/A (different language) Pointer receivers on pair / isNullAtGlobal / cell. Why: value receivers would copy slice headers (and map state) on every compare.
Stable sort primitive std::stable_sort slices.SortStableFunc (Go 1.21+). Why: library primitive; semantics aligned with stable weak ordering used elsewhere in the port.
Column dispatch at runtime Templates + virtuals columnComparator interface for “which column” in multi-key and merge loops. Why: idiomatic Go; per-type work stays in concrete compareRowsForKey implementations.
Chunked merge with null-likes (e.g. float) C++ can split merge for null-like vs non-null-like regions (ChunkedMergeImpl) Single less over full row order after per-chunk partitioning/sort. Why: simpler merge while preserving order as long as per-chunk phases match C++; documented in vector_sort.go comments.
Generics for physical columns Templates instantiate fully Explicit monomorphs only for the hot compare path. Why: measured regression vs Go generics on this hot path (inlining / assertions); verbosity traded for performance.

File Layout

  • arrow/compute/vector_sort.gosort_indices / sort registration and datum dispatch.
  • arrow/compute/vector_sort_test.go — functional tests.
  • arrow/compute/internal/kernels/vector_sort.go — orchestration, merge, SortIndices kernel.
  • arrow/compute/internal/kernels/vector_sort_internal.go — null partitions, radix / multi-key batch sort.
  • arrow/compute/internal/kernels/vector_sort_support.gologicalRowMap and ordering helpers.
  • arrow/compute/internal/kernels/vector_sort_physical.go — per-type column comparators.
  • arrow/compute/internal/kernels/vector_sort_bench_test.go — benchmarks.

Testing

  • go test ./arrow/compute -run TestSort -count=1
  • Benchmarks: go test ./arrow/compute/internal/kernels -bench=BenchmarkSortIndices -benchmem .

References

Related Issues

@hamilton-earthscope hamilton-earthscope changed the title new: support for sorting array, chunked, record batch and table new(arrow/compute): sort support Apr 4, 2026
@zeroshade zeroshade changed the title new(arrow/compute): sort support feat(arrow/compute): sort support Apr 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Go][Table] Implement sort function

1 participant