Add optimize_for_size variants for stable and unstable sort as well as select_nth_unstable - #129587
Conversation
…ll as select_nth_unstable - Stable sort uses a simple merge-sort that re-uses the existing - rather gnarly - merge function. - Unstable sort jumps directly to the branchless heapsort fallback. - select_nth_unstable jumps directly to the median_of_medians fallback, which is augmented with a custom tiny smallsort and partition impl. Some code is duplicated but de-duplication would bring it's own problems. For example `swap_if_less` is critical for performance, if the sorting networks don't inline it perf drops drastically, however `#[inline(always)]` is also a poor fit, if the provided comparison function is huge, it gives the compiler an out to only instantiate `swap_if_less` once and call it. Another aspect that would suffer when making `swap_if_less` pub, is having to cfg out dozens of functions in in smallsort module.
|
r? @Kobzol |
|
Could not assign reviewer from: |
This comment has been minimized.
This comment has been minimized.
|
r? libs |
|
One thing that is still outstanding is, how do we document the different implementation behavior? I think adding even more text to the 10 places that document the current implementation of That said I think with how recent and WIP |
Avoids the code duplication issue and results in smaller binary size, which after all is the purpose of the feature.
|
I think the open points have been addressed, if not, please re-open. Good to be merged from my side. |
|
Would be nice to measure the binary-size impact of this change in a real setting, here are the result of our tool that we also used in the design documents:
|
While using it results in slightly slammer binaries, it's not deemed worth it to add yet another sort algorithm to the standard library. select_nth_unstable has bigger binary-size problems.
Also skips stack alloc in stable sort if 16-bit target platform.
This regresses binary-size slightly for normal builds, but the important release_lto_thin_opt_level_s config sees a small improvement in binary-size and a larger types such as string and 1k see 2-3% run-time improvements with this change.
|
Pushed a bunch of changes, and updated the table above. |
|
LGTM, and thanks for the detailed tables and graphs as well! @bors r+ rollup=never |
|
☀️ Test successful - checks-actions |
|
Finished benchmarking commit (363ae41): comparison URL. Overall result: ❌ regressions - ACTION NEEDEDNext Steps: If you can justify the regressions found in this perf run, please indicate this with @rustbot label: +perf-regression Instruction countThis is a highly reliable metric that was used to determine the overall result at the top of this comment.
Max RSS (memory usage)Results (primary 5.7%, secondary 2.6%)This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
CyclesResults (primary -0.5%, secondary 3.8%)This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
Binary sizeResults (primary 0.0%, secondary -0.1%)This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
Bootstrap: 767.328s -> 768.432s (0.14%) |
| #[cfg(not(feature = "optimize_for_size"))] | ||
| pub(crate) mod quicksort; | ||
|
|
||
| #[cfg(feature = "optimize_for_size")] |
There was a problem hiding this comment.
This likely needs to be #[cfg(any(feature = "optimize_for_size", target_pointer_width = "16"))], see #130818
|
Not sure the rust-timer run tells us anything useful here, given that all changes here happened to code behind a libcore feature that isn't being built IIUC. |
|
I don't think that further investigation is needed. This has seemingly perturbed codegen a little bit, but otherwise it's hidden behind an optional flag. The @rustbot label: +perf-regression-triaged |


Some code is duplicated but de-duplication would bring it's own problems. For example
swap_if_lessis critical for performance, if the sorting networks don't inline it perf drops drastically, however#[inline(always)]is also a poor fit, if the provided comparison function is huge, it gives the compiler an out to only instantiateswap_if_lessonce and call it. Another aspect that would suffer when makingswap_if_lesspub, is having to cfg out dozens of functions in in smallsort module.Part of #125612
r? @Kobzol