GH-50247: [C++] Reuse abstraction for null partitions in sorting functions#50248
GH-50247: [C++] Reuse abstraction for null partitions in sorting functions#50248taepper wants to merge 22 commits into
Conversation
|
|
pitrou
left a comment
There was a problem hiding this comment.
Thank you! This is excellent, and the simplification is very welcome. Just a couple minor comments below.
|
Hmm, there are some regressions in the test suite (see CI runs). Also this runtime assertion on Windows CI might give a clue: |
54c3aca to
91d6a60
Compare
67fd062 to
a91e3a5
Compare
|
Thank you for the comments. I returned from vacation and fixed the windows errors. I also added some short-hands to the struct so we have less boilerplate in getting the begin/end pointers of the partition ranges. |
pitrou
left a comment
There was a problem hiding this comment.
This is looking very good, just a couple additional comments.
| IndexType* operator()(IndexType* indices_begin, IndexType* indices_end, | ||
| Predicate&& pred) { | ||
| return std::partition(indices_begin, indices_end, std::forward<Predicate>(pred)); | ||
| template <typename Predicate> |
There was a problem hiding this comment.
Add a comment that this returns the right-hand partition?
| template <typename TargetIndexType> | ||
| GenericNullLikePartition<TargetIndexType> TranslateTo( | ||
| IndexType* indices_begin, TargetIndexType* target_indices_begin) const { | ||
| size_t non_null_offset = non_null_like_range.data() - indices_begin; |
There was a problem hiding this comment.
Can we DCHECK that indices_begin falls somewhere in the overall range?
| (nulls_end - indices_begin) + target_indices_begin, | ||
| }; | ||
| static NullPartition NullsAtStart(std::span<uint64_t> indices, | ||
| std::span<uint64_t> non_null_tail) { |
There was a problem hiding this comment.
Note: it seems a bit weird to pass the null tail to NullsAtEnd and the non-null tail to NullsAtStart. At least add a comment?
| [&values, &offset](uint64_t ind) { return values.IsNull(ind - offset); }); | ||
| return NullPartitionResult::NullsAtStart(indices_begin, indices_end, nulls_end); | ||
| auto non_null_tail = partitioner(indices, [&values, &offset](uint64_t ind) { | ||
| return values.IsNull(ind - offset); |
There was a problem hiding this comment.
ind - offset is mixed signed-unsigned arithmetic, which may error out on some compilers.
| void EmitIndices(const NullPartitionResult& p, const ArrayType& values, int64_t offset, | ||
| void EmitIndices(const NullLikePartition& p, const ArrayType& values, int64_t offset, | ||
| CounterType* counts) const { | ||
| int64_t index = offset; |
There was a problem hiding this comment.
Can we DCHECK that the nan_range is empty?
Rationale for this change
@pitrou mentioned this as a follow-up in #46926
What changes are included in this PR?
Refactoring sorting methods to reuse the helper methods avoid maintaining two abstractions for null partitions. The new abstraction was very seamless to implement in most cases, but a few spots required some care
In particular, these functions were severly simlpified by the new abstraction:
MarkDuplicates: duplicate nulls and nans were detected by checking every single row forNullone additional time, after we already had (and discarded) the nullness informationGenericMergeImpl: merging ofnull-ranges involved repartitioningnullandnanvalues in every merge invocation. Now, we track this distinction and do not need any merge function fornullandnanblocks (unless we merge by multiple sort-keys, where we will merge according to the remaining sort keys)Are these changes tested?
Yes, the compute test suite passes as before
Are there any user-facing changes?
No.