-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Specialize list.sort() comparisons for homogeneous lists #8463
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
6e1c04e
Add powersort implementation for list sorting
kangdora 5b52036
Use powersort for list.sort() and drop rust-timsort
kangdora e5a5247
Fix usize underflow in merge_hi Succeed path
kangdora 189e6e7
Fix clippy warnings and formatting in sorting.rs
kangdora 526820b
Drop redundant test_ prefixes in sorting tests
kangdora ecbda8e
Restore buffered elements when a comparison fails mid-merge
kangdora b4fda72
Copy one run-A element per gallop round in merge_lo
kangdora 6a153dd
Use type-specialized comparators for homogeneous list sorts
kangdora b92659d
Add edge-case tests for specialized list sorts
kangdora 3010dd4
Cache the richcompare slot for homogeneous list sorts
kangdora fc58bdf
Specialize tuple sorts on their first elements
kangdora File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Extract the duplicated reverse-swap prologue from the six dispatch arms.
All six arms repeat the same five-line block and differ only in the leaf comparison function. The coding guidelines require extracting the differing value and invoking the common logic once. Beyond style, the duplication is a correctness risk: one arm that loses the
reverseswap would silently produce the wrong order, and only a reverse-specific test would catch it.The ordering behavior itself is correct. Swapping the operands of a strict less-than comparator, combined with a stable sort, preserves the original relative order of equal elements, which matches CPython's reverse-sort-reverse result.
♻️ Proposed refactor
where T: Clone, K: Fn(&T) -> &PyObjectRef, { - match pre_sort_check(items.iter().map(&key), vm) { - PreSort::Str => timsort(items, &mut |a, b| { - let (a, b) = if reverse { - (key(b), key(a)) - } else { - (key(a), key(b)) - }; - Ok(str_lt(a, b)) - }), - PreSort::Int => timsort(items, &mut |a, b| { - let (a, b) = if reverse { - (key(b), key(a)) - } else { - (key(a), key(b)) - }; - Ok(int_lt(a, b)) - }), - PreSort::Float => timsort(items, &mut |a, b| { - let (a, b) = if reverse { - (key(b), key(a)) - } else { - (key(a), key(b)) - }; - Ok(float_lt(a, b)) - }), - PreSort::Object(cmp) => timsort(items, &mut |a, b| { - let (a, b) = if reverse { - (key(b), key(a)) - } else { - (key(a), key(b)) - }; - object_lt(cmp, a, b, vm) - }), - PreSort::Tuple(elem) => timsort(items, &mut |a, b| { - let (a, b) = if reverse { - (key(b), key(a)) - } else { - (key(a), key(b)) - }; - tuple_lt(&elem, a, b, vm) - }), - PreSort::Generic => timsort(items, &mut |a, b| { - let (a, b) = if reverse { - (key(b), key(a)) - } else { - (key(a), key(b)) - }; - a.rich_compare_bool(b, PyComparisonOp::Lt, vm) - }), - } + // Wraps a key-level comparison into an item-level one, applying `reverse` + // by swapping the operands. + let run = |items: &mut [T], lt: &mut dyn FnMut(&PyObjectRef, &PyObjectRef) -> PyResult<bool>| { + timsort(items, &mut |a: &T, b: &T| { + let (a, b) = if reverse { + (key(b), key(a)) + } else { + (key(a), key(b)) + }; + lt(a, b) + }) + }; + + match pre_sort_check(items.iter().map(&key), vm) { + PreSort::Str => run(items, &mut |a, b| Ok(str_lt(a, b))), + PreSort::Int => run(items, &mut |a, b| Ok(int_lt(a, b))), + PreSort::Float => run(items, &mut |a, b| Ok(float_lt(a, b))), + PreSort::Object(cmp) => run(items, &mut |a, b| object_lt(cmp, a, b, vm)), + PreSort::Tuple(elem) => run(items, &mut |a, b| tuple_lt(&elem, a, b, vm)), + PreSort::Generic => run(items, &mut |a, b| { + a.rich_compare_bool(b, PyComparisonOp::Lt, vm) + }), + } }The
dyn FnMutindirection adds one virtual call per comparison. If benchmarks show that cost is material, keep the monomorphized form but makeruna generic innerfninstead:📝 Committable suggestion
🤖 Prompt for AI Agents
Source: Coding guidelines