Skip to content

Report one-row Sequence arrays as sorted for any multiplier - #10602

Open
ump45nose wants to merge 3 commits into
vortex-data:developfrom
ump45nose:fix/singleton-sequence-sortedness
Open

ump45nose wants to merge 3 commits into
vortex-data:developfrom
ump45nose:fix/singleton-sequence-sortedness

Conversation

@ump45nose

Copy link
Copy Markdown
Contributor

Summary

Closes #10579.

A one-row Sequence has no adjacent pair to compare, so it is sorted and strictly sorted whatever its multiplier. Its constructor stats, its is_sorted aggregate kernel and the generic sortedness helper answered from the multiplier (or a cached hint) alone, so a negative multiplier reported unsorted and a zero multiplier reported not strictly sorted.

Changes

  • Sequence::stats now takes the length and reports both flags as exact true when it is at most 1. This covers try_new, try_new_typed and slicing, which goes through new_unchecked.
  • SequenceIsSortedKernel returns true for a one-row batch, so direct aggregate execution agrees with the stats.
  • is_sorted_impl checks len() <= 1 before the cached statistics, so a stale hint can't contradict it.
  • SerializedArray::decode sets both flags to exact true for arrays with at most one element after it restores the stored statistics. Files written before this change can store the multiplier-only answer for one-row sequences, and this makes the intrinsic fact win over those historical hints.

New tests cover the typed and untyped constructors and a one-row slice of a longer sequence (multipliers -1, 0 and 3), the kernel called directly for strict and non-strict, decoding a one-row sequence whose serialized hints say unsorted, and the generic helper with stale cached stats. Apart from the positive-multiplier cases, they all fail on develop.

AI assistance

This PR was written by an AI coding agent working under my account (code, tests and this description), and I'm the responsible human for it. Points worth a reviewer's attention: the decode change in vortex-array/src/serde.rs applies to every encoding, not only Sequence, on the basis that any array with at most one element is (strictly) sorted; and it overwrites the stored hint with exact true rather than dropping it.

@codspeed

codspeed Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

Merging this PR will degrade performance by 3.73%

2 warnings, 1 note

⚠️ Walltime results may be unreliable

Some benchmarks used the Walltime instrument outside CodSpeed Macro Runners, which can make the results inconsistent.

Learn more about Macro Runners

See affected benchmarks

⚠️ 10 benchmarks are dominated by system calls

System calls cannot be simulated consistently and are left out of the measure. The reported value understates the real cost. Switch to the Walltime instrument to measure them.

Learn more about system calls

See affected benchmarks

ℹ️ 40 benchmarks have no flamegraph

The flamegraph wasn't generated: the symbols of the profiling trace could not be resolved, because no information about the language frames was collected.

See affected benchmarks

⚡ 3 improved benchmarks
❌ 5 regressed benchmarks
✅ 1140 untouched benchmarks
⏩ 1272 skipped benchmarks1

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Mode Benchmark BASE HEAD Efficiency
❌ Walltime filter_neon[8] 915 ns 1,136 ns -19.45%
❌ CPU Simulation or_true_constant 18.5 µs 21.5 µs -14.03%
❌ Walltime filter_neon[80] 2.5 µs 2.9 µs -12%
❌ Walltime filter_neon[64] 1.8 µs 2 µs -11.82%
❌ Walltime words_gather_dispatch_avx512[65536] 891 ns 992 ns -10.18%
⚡ CPU Simulation random_i128[0.01] 5.4 µs 4.3 µs +25.7%
⚡ Walltime decode_blocked_bitpacked_chunked_avx512[i64, 524288] 19.8 µs 17.9 µs +10.31%
⚡ Walltime decode_bitpacked_chunked_avx512[i64, 524288] 19.4 µs 17.6 µs +10.26%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing ump45nose:fix/singleton-sequence-sortedness (766a174) with develop (3cc1a5f)

Open in CodSpeed

Footnotes

  1. 1272 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩

@connortsui20
connortsui20 self-requested a review October 19, 2026 13:04
Comment on lines +38 to +52
// A single element is strictly sorted whatever the multiplier.
let result = if array.len() <= 1 {
true
} else {
let m = array.multiplier();
match_each_native_ptype!(m.ptype(), |P| {
m.cast::<P>().map(|x| {
if options.strict {
x > zero::<P>()
} else {
x >= zero::<P>()
}
})
})?
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why check twice???

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The two checks cover different paths. is_sorted returns early for len <= 1, but the accumulator hands this kernel each chunk of a ChunkedArray directly, and IsSorted has no legacy stat bridge, so neither that early return nor the constructor stats are consulted there. Without the kernel check, a one-row Sequence chunk with a negative multiplier makes an otherwise sorted chunked array report unsorted. In de3e710 I added a comment in the kernel and singleton_chunk_keeps_chunked_array_sorted, which fails without the check. (AI-assisted reply.)

@connortsui20 connortsui20 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should be able to consolidate the logic into one function like:

fn is_sorted(multiplier: PValue, length: usize, strict: bool) -> bool

@ump45nose

Copy link
Copy Markdown
Contributor Author

Done in d58de2d. Sequence::is_sorted(multiplier, length, strict) now holds the length and multiplier logic, and both the constructor stats and the kernel call it. (AI-assisted reply.)

A Sequence with a single element has no adjacent pair to compare, so it is
sorted and strictly sorted whatever its multiplier. The constructor stats,
the Sequence is_sorted aggregate kernel and the generic sortedness helper
all answered from the multiplier (or a cached hint) alone, so a one-row
Sequence with a negative multiplier reported unsorted, and one with a zero
multiplier reported not strictly sorted.

Decoding also restores the stored sortedness hints, which older writers
computed the same way, so a decoded one-row array now takes the intrinsic
answer over those hints.

Closes vortex-data#10579

Signed-off-by: Vincent <1729065730@qq.com>
The Sequence is_sorted kernel keeps its own len <= 1 check even though
is_sorted already returns early for such arrays: the accumulator hands the
kernel each chunk of a ChunkedArray directly, so the outer check never
runs for a one-row chunk. Explain this in the kernel and cover it with a
chunked-array test that fails without the check.

Signed-off-by: Vincent <1729065730@qq.com>
Move the length and multiplier logic into Sequence::is_sorted(multiplier,
length, strict) and use it for the constructor stats and the is_sorted
kernel.

Signed-off-by: Vincent <1729065730@qq.com>
@connortsui20
connortsui20 force-pushed the fix/singleton-sequence-sortedness branch from d58de2d to 766a174 Compare October 23, 2026 14:18
@connortsui20 connortsui20 added the changelog/chore A trivial change label Oct 23, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

changelog/chore A trivial change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Singleton Sequence arrays report incorrect sortedness

3 participants