From 8c5db8635bef26dee9fd287f26a5872a053853e0 Mon Sep 17 00:00:00 2001 From: Lilith Hafner Date: Fri, 6 Jan 2023 13:24:11 -0600 Subject: [PATCH 1/2] rename QuickerSort to QuickBufferSort --- base/sort.jl | 36 ++++++++++++++++++------------------ test/sorting.jl | 20 ++++++++++---------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/base/sort.jl b/base/sort.jl index 276d3f5d29727..1034c0636070a 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -86,7 +86,7 @@ issorted(itr; issorted(itr, ord(lt,by,rev,order)) function partialsort!(v::AbstractVector, k::Union{Integer,OrdinalRange}, o::Ordering) - _sort!(v, InitialOptimizations(QuickerSort(k)), o, (;)) + _sort!(v, InitialOptimizations(QuickBufferSort(k)), o, (;)) maybeview(v, k) end @@ -953,12 +953,12 @@ end """ - QuickerSort(next::Algorithm=SMALL_ALGORITHM) <: Algorithm - QuickerSort(lo::Union{Integer, Missing}, hi::Union{Integer, Missing}=lo, next::Algorithm=SMALL_ALGORITHM) <: Algorithm + QuickBufferSort(next::Algorithm=SMALL_ALGORITHM) <: Algorithm + QuickBufferSort(lo::Union{Integer, Missing}, hi::Union{Integer, Missing}=lo, next::Algorithm=SMALL_ALGORITHM) <: Algorithm -Use the `QuickerSort` algorithm with the `next` algorithm as a base case. +Use the `QuickBufferSort` algorithm with the `next` algorithm as a base case. -`QuickerSort` is like `QuickSort`, but utilizes scratch space to operate faster and allow +`QuickBufferSort` is like `QuickSort`, but utilizes scratch space to operate faster and allow for the possibility of maintaining stability. If `lo` and `hi` are provided, finds and sorts the elements in the range `lo:hi`, reordering @@ -976,17 +976,17 @@ Characteristics: * *quadratic worst case runtime* in pathological cases (vanishingly rare for non-malicious input) """ -struct QuickerSort{L<:Union{Integer,Missing}, H<:Union{Integer,Missing}, T<:Algorithm} <: Algorithm +struct QuickBufferSort{L<:Union{Integer,Missing}, H<:Union{Integer,Missing}, T<:Algorithm} <: Algorithm lo::L hi::H next::T end -QuickerSort(next::Algorithm=SMALL_ALGORITHM) = QuickerSort(missing, missing, next) -QuickerSort(lo::Union{Integer, Missing}, hi::Union{Integer, Missing}) = QuickerSort(lo, hi, SMALL_ALGORITHM) -QuickerSort(lo::Union{Integer, Missing}, next::Algorithm=SMALL_ALGORITHM) = QuickerSort(lo, lo, next) -QuickerSort(r::OrdinalRange, next::Algorithm=SMALL_ALGORITHM) = QuickerSort(first(r), last(r), next) +QuickBufferSort(next::Algorithm=SMALL_ALGORITHM) = QuickBufferSort(missing, missing, next) +QuickBufferSort(lo::Union{Integer, Missing}, hi::Union{Integer, Missing}) = QuickBufferSort(lo, hi, SMALL_ALGORITHM) +QuickBufferSort(lo::Union{Integer, Missing}, next::Algorithm=SMALL_ALGORITHM) = QuickBufferSort(lo, lo, next) +QuickBufferSort(r::OrdinalRange, next::Algorithm=SMALL_ALGORITHM) = QuickBufferSort(first(r), last(r), next) -# select a pivot for QuickerSort +# select a pivot for QuickBufferSort # # This method is redefined to rand(lo:hi) in Random.jl # We can't use rand here because it is not available in Core.Compiler and @@ -1029,7 +1029,7 @@ function partition!(t::AbstractVector, lo::Integer, hi::Integer, offset::Integer pivot_index end -function _sort!(v::AbstractVector, a::QuickerSort, o::Ordering, kw; +function _sort!(v::AbstractVector, a::QuickBufferSort, o::Ordering, kw; t=nothing, offset=nothing, swap=false, rev=false) @getkw lo hi scratch @@ -1047,7 +1047,7 @@ function _sort!(v::AbstractVector, a::QuickerSort, o::Ordering, kw; end swap = !swap - # For QuickerSort(), a.lo === a.hi === missing, so the first two branches get skipped + # For QuickBufferSort(), a.lo === a.hi === missing, so the first two branches get skipped if !ismissing(a.lo) && j <= a.lo # Skip sorting the lower part swap && copyto!(v, lo, t, lo+offset, j-lo) rev && reverse!(v, lo, j-1) @@ -1245,7 +1245,7 @@ the initial optimizations because they can change the input vector's type and or make them `UIntMappable`. If the input is not [`UIntMappable`](@ref), then we perform a presorted check and dispatch -to [`QuickerSort`](@ref). +to [`QuickBufferSort`](@ref). Otherwise, we dispatch to [`InsertionSort`](@ref) for inputs with `length <= 40` and then perform a presorted check ([`CheckSorted`](@ref)). @@ -1277,7 +1277,7 @@ Consequently, we apply [`RadixSort`](@ref) for any reasonably long inputs that r stage. Finally, if the input has length less than 80, we dispatch to [`InsertionSort`](@ref) and -otherwise we dispatch to [`QuickerSort`](@ref). +otherwise we dispatch to [`QuickBufferSort`](@ref). """ const DEFAULT_STABLE = InitialOptimizations( IsUIntMappable( @@ -1287,9 +1287,9 @@ const DEFAULT_STABLE = InitialOptimizations( ConsiderCountingSort( ConsiderRadixSort( Small{80}( - QuickerSort())))))), + QuickBufferSort())))))), StableCheckSorted( - QuickerSort()))) + QuickBufferSort()))) """ DEFAULT_UNSTABLE @@ -1494,7 +1494,7 @@ function partialsortperm!(ix::AbstractVector{<:Integer}, v::AbstractVector, end # do partial quicksort - _sort!(ix, InitialOptimizations(QuickerSort(k)), Perm(ord(lt, by, rev, order), v), (;)) + _sort!(ix, InitialOptimizations(QuickBufferSort(k)), Perm(ord(lt, by, rev, order), v), (;)) maybeview(ix, k) end diff --git a/test/sorting.jl b/test/sorting.jl index 1b79070d7e06e..4c9db999807cd 100644 --- a/test/sorting.jl +++ b/test/sorting.jl @@ -79,8 +79,8 @@ end end @testset "stability" begin - for Alg in [InsertionSort, MergeSort, Base.Sort.QuickerSort(), Base.DEFAULT_STABLE, - Base.Sort.QuickerSort(missing, 1729), Base.Sort.QuickerSort(1729, missing)] + for Alg in [InsertionSort, MergeSort, Base.Sort.QuickBufferSort(), Base.DEFAULT_STABLE, + Base.Sort.QuickBufferSort(missing, 1729), Base.Sort.QuickBufferSort(1729, missing)] @test issorted(sort(1:2000, alg=Alg, by=x->0)) @test issorted(sort(1:2000, alg=Alg, by=x->x÷100)) end @@ -333,7 +333,7 @@ end @test c == v # stable algorithms - for alg in [MergeSort, Base.Sort.QuickerSort(), Base.Sort.QuickerSort(1:n), Base.DEFAULT_STABLE] + for alg in [MergeSort, Base.Sort.QuickBufferSort(), Base.Sort.QuickBufferSort(1:n), Base.DEFAULT_STABLE] p = sortperm(v, alg=alg, rev=rev) p2 = sortperm(float(v), alg=alg, rev=rev) @test p == p2 @@ -381,7 +381,7 @@ end end v = randn_with_nans(n,0.1) - for alg in [InsertionSort, MergeSort, Base.Sort.QuickerSort(), Base.Sort.QuickerSort(1, n), Base.DEFAULT_UNSTABLE, Base.DEFAULT_STABLE], + for alg in [InsertionSort, MergeSort, Base.Sort.QuickBufferSort(), Base.Sort.QuickBufferSort(1, n), Base.DEFAULT_UNSTABLE, Base.DEFAULT_STABLE], rev in [false,true] alg === InsertionSort && n >= 3000 && continue # test float sorting with NaNs @@ -588,7 +588,7 @@ end @testset "fallback" begin @test adaptive_sort_test(rand(1:typemax(Int32), len), by=x->x^2)# fallback - @test adaptive_sort_test(rand(Int, len), by=x->0, trusted=Base.Sort.QuickerSort()) + @test adaptive_sort_test(rand(Int, len), by=x->0, trusted=Base.Sort.QuickBufferSort()) end @test adaptive_sort_test(rand(Int, 20)) # InsertionSort @@ -692,7 +692,7 @@ end # not allowed. Consequently, none of the behavior tested in this # testset is guaranteed to work in future minor versions of Julia. - safe_algs = [InsertionSort, MergeSort, Base.Sort.QuickerSort(), Base.DEFAULT_STABLE, Base.DEFAULT_UNSTABLE] + safe_algs = [InsertionSort, MergeSort, Base.Sort.QuickBufferSort(), Base.DEFAULT_STABLE, Base.DEFAULT_UNSTABLE] n = 1000 v = rand(1:5, n); @@ -899,8 +899,8 @@ end @test issorted(sort(rand(Int8, 600))) end -@testset "QuickerSort API" begin - bsqs = Base.Sort.QuickerSort +@testset "QuickBufferSort API" begin + bsqs = Base.Sort.QuickBufferSort @test bsqs(1, 2, MergeSort) === bsqs(1, 2, MergeSort) @test bsqs(missing, 2, MergeSort) === bsqs(missing, 2, MergeSort) @test bsqs(1, missing, MergeSort) === bsqs(1, missing, MergeSort) @@ -918,10 +918,10 @@ end @test bsqs() === bsqs(missing, missing, InsertionSort) end -@testset "QuickerSort allocations on non-concrete eltype" begin +@testset "QuickBufferSort allocations on non-concrete eltype" begin v = Vector{Union{Nothing, Bool}}(rand(Bool, 10000)) @test 4 == @allocations sort(v) - @test 4 == @allocations sort(v; alg=Base.Sort.QuickerSort()) + @test 4 == @allocations sort(v; alg=Base.Sort.QuickBufferSort()) # it would be nice if these numbers were lower (1 or 2), but these # test that we don't have O(n) allocations due to type instability end From 2777b5efc1647bc2a62b405de38278f3ff6d2b23 Mon Sep 17 00:00:00 2001 From: Lilith Hafner Date: Sun, 8 Jan 2023 11:40:07 -0600 Subject: [PATCH 2/2] ScratchQuickSort --- base/sort.jl | 36 ++++++++++++++++++------------------ test/sorting.jl | 20 ++++++++++---------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/base/sort.jl b/base/sort.jl index 1034c0636070a..b751d3953023b 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -86,7 +86,7 @@ issorted(itr; issorted(itr, ord(lt,by,rev,order)) function partialsort!(v::AbstractVector, k::Union{Integer,OrdinalRange}, o::Ordering) - _sort!(v, InitialOptimizations(QuickBufferSort(k)), o, (;)) + _sort!(v, InitialOptimizations(ScratchQuickSort(k)), o, (;)) maybeview(v, k) end @@ -953,12 +953,12 @@ end """ - QuickBufferSort(next::Algorithm=SMALL_ALGORITHM) <: Algorithm - QuickBufferSort(lo::Union{Integer, Missing}, hi::Union{Integer, Missing}=lo, next::Algorithm=SMALL_ALGORITHM) <: Algorithm + ScratchQuickSort(next::Algorithm=SMALL_ALGORITHM) <: Algorithm + ScratchQuickSort(lo::Union{Integer, Missing}, hi::Union{Integer, Missing}=lo, next::Algorithm=SMALL_ALGORITHM) <: Algorithm -Use the `QuickBufferSort` algorithm with the `next` algorithm as a base case. +Use the `ScratchQuickSort` algorithm with the `next` algorithm as a base case. -`QuickBufferSort` is like `QuickSort`, but utilizes scratch space to operate faster and allow +`ScratchQuickSort` is like `QuickSort`, but utilizes scratch space to operate faster and allow for the possibility of maintaining stability. If `lo` and `hi` are provided, finds and sorts the elements in the range `lo:hi`, reordering @@ -976,17 +976,17 @@ Characteristics: * *quadratic worst case runtime* in pathological cases (vanishingly rare for non-malicious input) """ -struct QuickBufferSort{L<:Union{Integer,Missing}, H<:Union{Integer,Missing}, T<:Algorithm} <: Algorithm +struct ScratchQuickSort{L<:Union{Integer,Missing}, H<:Union{Integer,Missing}, T<:Algorithm} <: Algorithm lo::L hi::H next::T end -QuickBufferSort(next::Algorithm=SMALL_ALGORITHM) = QuickBufferSort(missing, missing, next) -QuickBufferSort(lo::Union{Integer, Missing}, hi::Union{Integer, Missing}) = QuickBufferSort(lo, hi, SMALL_ALGORITHM) -QuickBufferSort(lo::Union{Integer, Missing}, next::Algorithm=SMALL_ALGORITHM) = QuickBufferSort(lo, lo, next) -QuickBufferSort(r::OrdinalRange, next::Algorithm=SMALL_ALGORITHM) = QuickBufferSort(first(r), last(r), next) +ScratchQuickSort(next::Algorithm=SMALL_ALGORITHM) = ScratchQuickSort(missing, missing, next) +ScratchQuickSort(lo::Union{Integer, Missing}, hi::Union{Integer, Missing}) = ScratchQuickSort(lo, hi, SMALL_ALGORITHM) +ScratchQuickSort(lo::Union{Integer, Missing}, next::Algorithm=SMALL_ALGORITHM) = ScratchQuickSort(lo, lo, next) +ScratchQuickSort(r::OrdinalRange, next::Algorithm=SMALL_ALGORITHM) = ScratchQuickSort(first(r), last(r), next) -# select a pivot for QuickBufferSort +# select a pivot for ScratchQuickSort # # This method is redefined to rand(lo:hi) in Random.jl # We can't use rand here because it is not available in Core.Compiler and @@ -1029,7 +1029,7 @@ function partition!(t::AbstractVector, lo::Integer, hi::Integer, offset::Integer pivot_index end -function _sort!(v::AbstractVector, a::QuickBufferSort, o::Ordering, kw; +function _sort!(v::AbstractVector, a::ScratchQuickSort, o::Ordering, kw; t=nothing, offset=nothing, swap=false, rev=false) @getkw lo hi scratch @@ -1047,7 +1047,7 @@ function _sort!(v::AbstractVector, a::QuickBufferSort, o::Ordering, kw; end swap = !swap - # For QuickBufferSort(), a.lo === a.hi === missing, so the first two branches get skipped + # For ScratchQuickSort(), a.lo === a.hi === missing, so the first two branches get skipped if !ismissing(a.lo) && j <= a.lo # Skip sorting the lower part swap && copyto!(v, lo, t, lo+offset, j-lo) rev && reverse!(v, lo, j-1) @@ -1245,7 +1245,7 @@ the initial optimizations because they can change the input vector's type and or make them `UIntMappable`. If the input is not [`UIntMappable`](@ref), then we perform a presorted check and dispatch -to [`QuickBufferSort`](@ref). +to [`ScratchQuickSort`](@ref). Otherwise, we dispatch to [`InsertionSort`](@ref) for inputs with `length <= 40` and then perform a presorted check ([`CheckSorted`](@ref)). @@ -1277,7 +1277,7 @@ Consequently, we apply [`RadixSort`](@ref) for any reasonably long inputs that r stage. Finally, if the input has length less than 80, we dispatch to [`InsertionSort`](@ref) and -otherwise we dispatch to [`QuickBufferSort`](@ref). +otherwise we dispatch to [`ScratchQuickSort`](@ref). """ const DEFAULT_STABLE = InitialOptimizations( IsUIntMappable( @@ -1287,9 +1287,9 @@ const DEFAULT_STABLE = InitialOptimizations( ConsiderCountingSort( ConsiderRadixSort( Small{80}( - QuickBufferSort())))))), + ScratchQuickSort())))))), StableCheckSorted( - QuickBufferSort()))) + ScratchQuickSort()))) """ DEFAULT_UNSTABLE @@ -1494,7 +1494,7 @@ function partialsortperm!(ix::AbstractVector{<:Integer}, v::AbstractVector, end # do partial quicksort - _sort!(ix, InitialOptimizations(QuickBufferSort(k)), Perm(ord(lt, by, rev, order), v), (;)) + _sort!(ix, InitialOptimizations(ScratchQuickSort(k)), Perm(ord(lt, by, rev, order), v), (;)) maybeview(ix, k) end diff --git a/test/sorting.jl b/test/sorting.jl index 4c9db999807cd..691f0a0e2bc39 100644 --- a/test/sorting.jl +++ b/test/sorting.jl @@ -79,8 +79,8 @@ end end @testset "stability" begin - for Alg in [InsertionSort, MergeSort, Base.Sort.QuickBufferSort(), Base.DEFAULT_STABLE, - Base.Sort.QuickBufferSort(missing, 1729), Base.Sort.QuickBufferSort(1729, missing)] + for Alg in [InsertionSort, MergeSort, Base.Sort.ScratchQuickSort(), Base.DEFAULT_STABLE, + Base.Sort.ScratchQuickSort(missing, 1729), Base.Sort.ScratchQuickSort(1729, missing)] @test issorted(sort(1:2000, alg=Alg, by=x->0)) @test issorted(sort(1:2000, alg=Alg, by=x->x÷100)) end @@ -333,7 +333,7 @@ end @test c == v # stable algorithms - for alg in [MergeSort, Base.Sort.QuickBufferSort(), Base.Sort.QuickBufferSort(1:n), Base.DEFAULT_STABLE] + for alg in [MergeSort, Base.Sort.ScratchQuickSort(), Base.Sort.ScratchQuickSort(1:n), Base.DEFAULT_STABLE] p = sortperm(v, alg=alg, rev=rev) p2 = sortperm(float(v), alg=alg, rev=rev) @test p == p2 @@ -381,7 +381,7 @@ end end v = randn_with_nans(n,0.1) - for alg in [InsertionSort, MergeSort, Base.Sort.QuickBufferSort(), Base.Sort.QuickBufferSort(1, n), Base.DEFAULT_UNSTABLE, Base.DEFAULT_STABLE], + for alg in [InsertionSort, MergeSort, Base.Sort.ScratchQuickSort(), Base.Sort.ScratchQuickSort(1, n), Base.DEFAULT_UNSTABLE, Base.DEFAULT_STABLE], rev in [false,true] alg === InsertionSort && n >= 3000 && continue # test float sorting with NaNs @@ -588,7 +588,7 @@ end @testset "fallback" begin @test adaptive_sort_test(rand(1:typemax(Int32), len), by=x->x^2)# fallback - @test adaptive_sort_test(rand(Int, len), by=x->0, trusted=Base.Sort.QuickBufferSort()) + @test adaptive_sort_test(rand(Int, len), by=x->0, trusted=Base.Sort.ScratchQuickSort()) end @test adaptive_sort_test(rand(Int, 20)) # InsertionSort @@ -692,7 +692,7 @@ end # not allowed. Consequently, none of the behavior tested in this # testset is guaranteed to work in future minor versions of Julia. - safe_algs = [InsertionSort, MergeSort, Base.Sort.QuickBufferSort(), Base.DEFAULT_STABLE, Base.DEFAULT_UNSTABLE] + safe_algs = [InsertionSort, MergeSort, Base.Sort.ScratchQuickSort(), Base.DEFAULT_STABLE, Base.DEFAULT_UNSTABLE] n = 1000 v = rand(1:5, n); @@ -899,8 +899,8 @@ end @test issorted(sort(rand(Int8, 600))) end -@testset "QuickBufferSort API" begin - bsqs = Base.Sort.QuickBufferSort +@testset "ScratchQuickSort API" begin + bsqs = Base.Sort.ScratchQuickSort @test bsqs(1, 2, MergeSort) === bsqs(1, 2, MergeSort) @test bsqs(missing, 2, MergeSort) === bsqs(missing, 2, MergeSort) @test bsqs(1, missing, MergeSort) === bsqs(1, missing, MergeSort) @@ -918,10 +918,10 @@ end @test bsqs() === bsqs(missing, missing, InsertionSort) end -@testset "QuickBufferSort allocations on non-concrete eltype" begin +@testset "ScratchQuickSort allocations on non-concrete eltype" begin v = Vector{Union{Nothing, Bool}}(rand(Bool, 10000)) @test 4 == @allocations sort(v) - @test 4 == @allocations sort(v; alg=Base.Sort.QuickBufferSort()) + @test 4 == @allocations sort(v; alg=Base.Sort.ScratchQuickSort()) # it would be nice if these numbers were lower (1 or 2), but these # test that we don't have O(n) allocations due to type instability end