From 489dfceabae1d8671a39d938970f436c8b6a4a2d Mon Sep 17 00:00:00 2001 From: Lukas Bergdoll Date: Sun, 16 Jun 2024 17:17:36 +0200 Subject: [PATCH] Remove reliance on const_trait in sort implementations const_trait in conjunction with specialization was deemed not ready for usage in this scenario. So instead a two-stage trait specialization approach is used. This approach is likely worse for compile-times. Future work that enables const_trait can revert back to the previous version as outlined in the comment marked FIXME(effects). --- core/src/slice/sort/shared/smallsort.rs | 123 ++++++++++++------------ core/src/slice/sort/stable/quicksort.rs | 7 +- 2 files changed, 66 insertions(+), 64 deletions(-) diff --git a/core/src/slice/sort/shared/smallsort.rs b/core/src/slice/sort/shared/smallsort.rs index 7cd351c9f2516..9a0f7bf2f51d4 100644 --- a/core/src/slice/sort/shared/smallsort.rs +++ b/core/src/slice/sort/shared/smallsort.rs @@ -93,10 +93,62 @@ impl UnstableSmallSortTypeImpl for T { impl UnstableSmallSortTypeImpl for T { #[inline(always)] fn small_sort_threshold() -> usize { - match const { choose_unstable_small_sort::() } { - UnstableSmallSort::Fallback => SMALL_SORT_FALLBACK_THRESHOLD, - UnstableSmallSort::General => SMALL_SORT_GENERAL_THRESHOLD, - UnstableSmallSort::Network => SMALL_SORT_NETWORK_THRESHOLD, + ::small_sort_threshold() + } + + #[inline(always)] + fn small_sort(v: &mut [T], is_less: &mut F) + where + F: FnMut(&T, &T) -> bool, + { + ::small_sort(v, is_less); + } +} + +/// FIXME(effects) use original ipnsort approach with choose_unstable_small_sort, +/// as found here https://github.com/Voultapher/sort-research-rs/blob/438fad5d0495f65d4b72aa87f0b62fc96611dff3/ipnsort/src/smallsort.rs#L83C10-L83C36. +pub(crate) trait UnstableSmallSortFreezeTypeImpl: Sized + FreezeMarker { + fn small_sort_threshold() -> usize; + + fn small_sort bool>(v: &mut [Self], is_less: &mut F); +} + +impl UnstableSmallSortFreezeTypeImpl for T { + #[inline(always)] + default fn small_sort_threshold() -> usize { + if (mem::size_of::() * SMALL_SORT_GENERAL_SCRATCH_LEN) <= MAX_STACK_ARRAY_SIZE { + SMALL_SORT_GENERAL_THRESHOLD + } else { + SMALL_SORT_FALLBACK_THRESHOLD + } + } + + #[inline(always)] + default fn small_sort(v: &mut [T], is_less: &mut F) + where + F: FnMut(&T, &T) -> bool, + { + if (mem::size_of::() * SMALL_SORT_GENERAL_SCRATCH_LEN) <= MAX_STACK_ARRAY_SIZE { + small_sort_general(v, is_less); + } else { + small_sort_fallback(v, is_less); + } + } +} + +/// SAFETY: Only used for run-time optimization heuristic. +#[rustc_unsafe_specialization_marker] +trait CopyMarker {} + +impl UnstableSmallSortFreezeTypeImpl for T { + #[inline(always)] + fn small_sort_threshold() -> usize { + if has_efficient_in_place_swap::() + && (mem::size_of::() * SMALL_SORT_NETWORK_SCRATCH_LEN) <= MAX_STACK_ARRAY_SIZE + { + SMALL_SORT_NETWORK_SCRATCH_LEN + } else { + SMALL_SORT_FALLBACK_THRESHOLD } } @@ -105,9 +157,13 @@ impl UnstableSmallSortTypeImpl for T { where F: FnMut(&T, &T) -> bool, { - // This construct is used to limit the LLVM IR generated, which saves large amounts of - // compile-time by only instantiating the code that is needed. Idea by Frank Steffahn. - (const { inst_unstable_small_sort::() })(v, is_less); + if has_efficient_in_place_swap::() + && (mem::size_of::() * SMALL_SORT_NETWORK_SCRATCH_LEN) <= MAX_STACK_ARRAY_SIZE + { + small_sort_network(v, is_less); + } else { + small_sort_fallback(v, is_less); + } } } @@ -137,37 +193,6 @@ const SMALL_SORT_NETWORK_SCRATCH_LEN: usize = SMALL_SORT_NETWORK_THRESHOLD; /// within this limit. const MAX_STACK_ARRAY_SIZE: usize = 4096; -enum UnstableSmallSort { - Fallback, - General, - Network, -} - -const fn choose_unstable_small_sort() -> UnstableSmallSort { - if T::is_copy() - && has_efficient_in_place_swap::() - && (mem::size_of::() * SMALL_SORT_NETWORK_SCRATCH_LEN) <= MAX_STACK_ARRAY_SIZE - { - // Heuristic for int like types. - return UnstableSmallSort::Network; - } - - if (mem::size_of::() * SMALL_SORT_GENERAL_SCRATCH_LEN) <= MAX_STACK_ARRAY_SIZE { - return UnstableSmallSort::General; - } - - UnstableSmallSort::Fallback -} - -const fn inst_unstable_small_sort bool>() --> fn(&mut [T], &mut F) { - match const { choose_unstable_small_sort::() } { - UnstableSmallSort::Fallback => small_sort_fallback::, - UnstableSmallSort::General => small_sort_general::, - UnstableSmallSort::Network => small_sort_network::, - } -} - fn small_sort_fallback bool>(v: &mut [T], is_less: &mut F) { if v.len() >= 2 { insertion_sort_shift_left(v, 1, is_less); @@ -822,25 +847,3 @@ pub(crate) const fn has_efficient_in_place_swap() -> bool { // Heuristic that holds true on all tested 64-bit capable architectures. mem::size_of::() <= 8 // mem::size_of::() } - -/// SAFETY: Only used for run-time optimization heuristic. -#[rustc_unsafe_specialization_marker] -trait CopyMarker {} - -impl CopyMarker for T {} - -#[const_trait] -trait IsCopy { - fn is_copy() -> bool; -} - -impl const IsCopy for T { - default fn is_copy() -> bool { - false - } -} -impl const IsCopy for T { - fn is_copy() -> bool { - true - } -} diff --git a/core/src/slice/sort/stable/quicksort.rs b/core/src/slice/sort/stable/quicksort.rs index e1734ce8d8b46..181fe603d2325 100644 --- a/core/src/slice/sort/stable/quicksort.rs +++ b/core/src/slice/sort/stable/quicksort.rs @@ -233,24 +233,23 @@ impl PartitionState { } } -#[const_trait] trait IsFreeze { fn is_freeze() -> bool; } -impl const IsFreeze for T { +impl IsFreeze for T { default fn is_freeze() -> bool { false } } -impl const IsFreeze for T { +impl IsFreeze for T { fn is_freeze() -> bool { true } } #[must_use] -const fn has_direct_interior_mutability() -> bool { +fn has_direct_interior_mutability() -> bool { // If a type has interior mutability it may alter itself during comparison // in a way that must be preserved after the sort operation concludes. // Otherwise a type like Mutex>> could lead to double free.