@@ -4,7 +4,7 @@ module Sort
4
4
5
5
using Base. Order
6
6
7
- using Base: copymutable, midpoint, require_one_based_indexing, uinttype,
7
+ using Base: copymutable, midpoint, require_one_based_indexing, uinttype, tail,
8
8
sub_with_overflow, add_with_overflow, OneTo, BitSigned, BitIntegerType, top_set_bit
9
9
10
10
import Base:
@@ -1482,8 +1482,9 @@ InitialOptimizations(next) = SubArrayOptimization(
1482
1482
`DefaultStable` is an algorithm which indicates that a fast, general purpose sorting
1483
1483
algorithm should be used, but does not specify exactly which algorithm.
1484
1484
1485
- Currently, it is composed of two parts: the [`InitialOptimizations`](@ref) and a hybrid of
1486
- Radix, Insertion, Counting, Quick sorts.
1485
+ Currently, when sorting short NTuples, this is an unrolled mergesort, and otherwise it is
1486
+ composed of two parts: the [`InitialOptimizations`](@ref) and a hybrid of Radix, Insertion,
1487
+ Counting, Quick sorts.
1487
1488
1488
1489
We begin with MissingOptimization because it has no runtime cost when it is not
1489
1490
triggered and can enable other optimizations to be applied later. For example,
@@ -1619,6 +1620,7 @@ defalg(v::AbstractArray) = DEFAULT_STABLE
1619
1620
defalg (v:: AbstractArray{<:Union{Number, Missing}} ) = DEFAULT_UNSTABLE
1620
1621
defalg (v:: AbstractArray{Missing} ) = DEFAULT_UNSTABLE # for method disambiguation
1621
1622
defalg (v:: AbstractArray{Union{}} ) = DEFAULT_UNSTABLE # for method disambiguation
1623
+ defalg (v:: NTuple ) = DEFAULT_STABLE
1622
1624
1623
1625
"""
1624
1626
sort!(v; alg::Base.Sort.Algorithm=Base.Sort.defalg(v), lt=isless, by=identity, rev::Bool=false, order::Base.Order.Ordering=Base.Order.Forward)
@@ -1757,6 +1759,41 @@ julia> v
1757
1759
"""
1758
1760
sort (v:: AbstractVector ; kws... ) = sort! (copymutable (v); kws... )
1759
1761
1762
+ function sort (x:: NTuple ;
1763
+ alg:: Algorithm = defalg (x),
1764
+ lt= isless,
1765
+ by= identity,
1766
+ rev:: Union{Bool,Nothing} = nothing ,
1767
+ order:: Ordering = Forward,
1768
+ scratch:: Union{Vector, Nothing} = nothing )
1769
+ # Can't do this check with type parameters because of https://github.com/JuliaLang/julia/issues/56698
1770
+ scratch === nothing || eltype (x) == eltype (scratch) || throw (ArgumentError (" scratch has the wrong eltype" ))
1771
+ _sort (x, alg, ord (lt,by,rev,order), (;scratch)):: typeof (x)
1772
+ end
1773
+ # Folks who want to hack internals can define a new _sort(x::NTuple, ::TheirAlg, o::Ordering)
1774
+ # or _sort(x::NTuple{N, TheirType}, ::DefaultStable, o::Ordering) where N
1775
+ function _sort (x:: NTuple , a:: Union{DefaultStable, DefaultUnstable} , o:: Ordering , kw)
1776
+ # The unrolled tuple sort is prohibitively slow to compile for length > 9.
1777
+ # See https://github.com/JuliaLang/julia/pull/46104#issuecomment-1435688502 for benchmarks
1778
+ if length (x) > 9
1779
+ v = copymutable (x)
1780
+ _sort! (v, a, o, kw)
1781
+ typeof (x)(v)
1782
+ else
1783
+ _mergesort (x, o)
1784
+ end
1785
+ end
1786
+ _mergesort (x:: Union{NTuple{0}, NTuple{1}} , o:: Ordering ) = x
1787
+ function _mergesort (x:: NTuple , o:: Ordering )
1788
+ a, b = Base. IteratorsMD. split (x, Val (length (x)>> 1 ))
1789
+ merge (_mergesort (a, o), _mergesort (b, o), o)
1790
+ end
1791
+ merge (x:: NTuple , y:: NTuple{0} , o:: Ordering ) = x
1792
+ merge (x:: NTuple{0} , y:: NTuple , o:: Ordering ) = y
1793
+ merge (x:: NTuple{0} , y:: NTuple{0} , o:: Ordering ) = x # Method ambiguity
1794
+ merge (x:: NTuple , y:: NTuple , o:: Ordering ) =
1795
+ (lt (o, y[1 ], x[1 ]) ? (y[1 ], merge (x, tail (y), o)... ) : (x[1 ], merge (tail (x), y, o)... ))
1796
+
1760
1797
# # partialsortperm: the permutation to sort the first k elements of an array ##
1761
1798
1762
1799
"""
0 commit comments