From b6a15ba8245d5caf8ab7d1e954a6734447bda154 Mon Sep 17 00:00:00 2001 From: Nicolau Leal Werneck Date: Tue, 11 Oct 2022 21:30:51 +0200 Subject: [PATCH 1/5] Bitonic mergesort --- src/SortingAlgorithms.jl | 73 +++++++++++++++++++++++++++++++++++++++- test/runtests.jl | 6 ++-- 2 files changed, 75 insertions(+), 4 deletions(-) diff --git a/src/SortingAlgorithms.jl b/src/SortingAlgorithms.jl index b9b0c8e..4bd19ae 100644 --- a/src/SortingAlgorithms.jl +++ b/src/SortingAlgorithms.jl @@ -9,12 +9,13 @@ using Base.Order import Base.Sort: sort! import DataStructures: heapify!, percolate_down! -export HeapSort, TimSort, RadixSort, CombSort +export HeapSort, TimSort, RadixSort, CombSort, BitonicSort struct HeapSortAlg <: Algorithm end struct TimSortAlg <: Algorithm end struct RadixSortAlg <: Algorithm end struct CombSortAlg <: Algorithm end +struct BitonicSortAlg <: Algorithm end const HeapSort = HeapSortAlg() const TimSort = TimSortAlg() @@ -46,6 +47,26 @@ Characteristics: """ const CombSort = CombSortAlg() +""" + BitonicSort + +Indicates that a sorting function should use the bitonic mergesort algorithm. +This algorithm performs a series of pre-determined comparisons, and tends to be very parallelizable. +The algorithm effectively implements a sorting network based on merging bitonic sequences. + +Characteristics: + - *not stable* does not preserve the ordering of elements which + compare equal (e.g. "a" and "A" in a sort of letters which + ignores case). + - *in-place* in memory. + - *parallelizable* suitable for vectorization with SIMD instructions because +it performs many independent comparisons. + +## References +- Batcher, K.E., (1968). "Sorting networks and their applications", AFIPS '68, doi: https://doi.org/10.1145/1468075.1468121. +""" +const BitonicSort = BitonicSortAlg() + ## Heap sort @@ -619,4 +640,54 @@ function sort!(v::AbstractVector, lo::Int, hi::Int, ::CombSortAlg, o::Ordering) return sort!(v, lo, hi, InsertionSort, o) end +function sort!(v::AbstractVector, lo::Int, hi::Int, ::BitonicSortAlg, o::Ordering) + return bitonicsort!(view(v, lo:hi), o::Ordering) +end + +function bitonicsort!(data, o::Ordering) + N = length(data) + for n in 1:ceil(Int, max(0, log2(N))) + bitonicfirststage!(data, Val(n), o::Ordering) + for m in n-1:-1:1 + bitonicsecondstage!(data, Val(m), o::Ordering) + end + end + return data +end + +function bitonicfirststage!(v, ::Val{Gap}, o::Ordering) where Gap + N = length(v) + @inbounds begin + gap = 1 << Gap + g2 = gap >> 1 + for i in 0:gap:N-1 + firstj = max(0, i + gap - N) + for j in firstj:(g2-1) + ia = i + j + ib = i + gap - j - 1 + a, b = v[ia + 1], v[ib + 1] + v[ia+1], v[ib+1] = lt(o, b, a) ? (b, a) : (a, b) + end + end + end +end + +function bitonicsecondstage!(v, ::Val{Gap}, o::Ordering) where Gap + N = length(v) + @inbounds begin + for n in Gap:-1:1 + gap = 1 << n + for i in 0:gap:N-1 + lastj = min(N - 1, N - (i + gap >> 1 + 1)) + for j in 0:lastj + ia = i + j + ib = i + j + gap >> 1 + a, b = v[ia + 1], v[ib + 1] + v[ia+1], v[ib+1] = lt(o, b, a) ? (b, a) : (a, b) + end + end + end + end +end + end # module diff --git a/test/runtests.jl b/test/runtests.jl index 7ea2780..0f4a558 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -5,7 +5,7 @@ using Random a = rand(1:10000, 1000) -for alg in [TimSort, HeapSort, RadixSort, CombSort] +for alg in [TimSort, HeapSort, RadixSort, CombSort, BitonicSort] b = sort(a, alg=alg) @test issorted(b) ix = sortperm(a, alg=alg) @@ -85,7 +85,7 @@ for n in [0:10..., 100, 101, 1000, 1001] end # unstable algorithms - for alg in [HeapSort, CombSort] + for alg in [HeapSort, CombSort, BitonicSort] p = sortperm(v, alg=alg, order=ord) @test isperm(p) @test v[p] == si @@ -99,7 +99,7 @@ for n in [0:10..., 100, 101, 1000, 1001] v = randn_with_nans(n,0.1) for ord in [Base.Order.Forward, Base.Order.Reverse], - alg in [TimSort, HeapSort, RadixSort, CombSort] + alg in [TimSort, HeapSort, RadixSort, CombSort, BitonicSort] # test float sorting with NaNs s = sort(v, alg=alg, order=ord) @test issorted(s, order=ord) From 55e724c736f47dd14d534b42697135d6cbd43191 Mon Sep 17 00:00:00 2001 From: Nicolau Leal Werneck Date: Thu, 13 Oct 2022 09:24:53 +0200 Subject: [PATCH 2/5] integral log2, and wp ref --- src/SortingAlgorithms.jl | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/SortingAlgorithms.jl b/src/SortingAlgorithms.jl index 4bd19ae..ac85d83 100644 --- a/src/SortingAlgorithms.jl +++ b/src/SortingAlgorithms.jl @@ -63,7 +63,8 @@ Characteristics: it performs many independent comparisons. ## References -- Batcher, K.E., (1968). "Sorting networks and their applications", AFIPS '68, doi: https://doi.org/10.1145/1468075.1468121. +- Batcher, K.E., "Sorting networks and their applications", AFIPS '68 (1968), doi: https://doi.org/10.1145/1468075.1468121. +- "Bitonic Sorter", in "Wikipedia" (Oct 2022). https://en.wikipedia.org/wiki/Bitonic_sorter """ const BitonicSort = BitonicSortAlg() @@ -646,7 +647,7 @@ end function bitonicsort!(data, o::Ordering) N = length(data) - for n in 1:ceil(Int, max(0, log2(N))) + for n in 1:intlog2(N) bitonicfirststage!(data, Val(n), o::Ordering) for m in n-1:-1:1 bitonicsecondstage!(data, Val(m), o::Ordering) @@ -690,4 +691,7 @@ function bitonicsecondstage!(v, ::Val{Gap}, o::Ordering) where Gap end end +intlog2(n) = (n > 1) ? 8sizeof(n-1)-leading_zeros(n-1) : 0 + + end # module From 463514bd1906aa56c3dd40db7e2e56f0a40049cf Mon Sep 17 00:00:00 2001 From: Nicolau Leal Werneck Date: Thu, 13 Oct 2022 09:30:32 +0200 Subject: [PATCH 3/5] comparator method --- src/SortingAlgorithms.jl | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/SortingAlgorithms.jl b/src/SortingAlgorithms.jl index ac85d83..d7d7949 100644 --- a/src/SortingAlgorithms.jl +++ b/src/SortingAlgorithms.jl @@ -631,9 +631,8 @@ function sort!(v::AbstractVector, lo::Int, hi::Int, ::CombSortAlg, o::Ordering) interval = (3 * (hi-lo+1)) >> 2 while interval > 1 - @inbounds for j in lo:hi-interval - a, b = v[j], v[j+interval] - v[j], v[j+interval] = lt(o, b, a) ? (b, a) : (a, b) + for j in lo:hi-interval + @inbounds comparator!(v, j, j+interval, o) end interval = (3 * interval) >> 2 end @@ -666,8 +665,7 @@ function bitonicfirststage!(v, ::Val{Gap}, o::Ordering) where Gap for j in firstj:(g2-1) ia = i + j ib = i + gap - j - 1 - a, b = v[ia + 1], v[ib + 1] - v[ia+1], v[ib+1] = lt(o, b, a) ? (b, a) : (a, b) + @inbounds comparator!(v, ia + 1, ib + 1, o) end end end @@ -683,8 +681,7 @@ function bitonicsecondstage!(v, ::Val{Gap}, o::Ordering) where Gap for j in 0:lastj ia = i + j ib = i + j + gap >> 1 - a, b = v[ia + 1], v[ib + 1] - v[ia+1], v[ib+1] = lt(o, b, a) ? (b, a) : (a, b) + @inbounds comparator!(v, ia + 1, ib + 1, o) end end end @@ -693,5 +690,9 @@ end intlog2(n) = (n > 1) ? 8sizeof(n-1)-leading_zeros(n-1) : 0 +Base.@propagate_inbounds function comparator!(v, i, j, o) + a, b = v[i], v[j] + v[i], v[j] = lt(o, b, a) ? (b, a) : (a, b) +end end # module From 21e191353827681e95b741a3bcebba487cc2039c Mon Sep 17 00:00:00 2001 From: Nicolau Leal Werneck Date: Fri, 14 Oct 2022 08:11:06 +0200 Subject: [PATCH 4/5] Update src/SortingAlgorithms.jl Co-authored-by: Lilith Orion Hafner --- src/SortingAlgorithms.jl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/SortingAlgorithms.jl b/src/SortingAlgorithms.jl index d7d7949..a84c118 100644 --- a/src/SortingAlgorithms.jl +++ b/src/SortingAlgorithms.jl @@ -62,9 +62,7 @@ Characteristics: - *parallelizable* suitable for vectorization with SIMD instructions because it performs many independent comparisons. -## References -- Batcher, K.E., "Sorting networks and their applications", AFIPS '68 (1968), doi: https://doi.org/10.1145/1468075.1468121. -- "Bitonic Sorter", in "Wikipedia" (Oct 2022). https://en.wikipedia.org/wiki/Bitonic_sorter +See wikipedia.org/wiki/Bitonic_sorter for more information. """ const BitonicSort = BitonicSortAlg() From d4c23d94f8060e3018bc727220538559f6861088 Mon Sep 17 00:00:00 2001 From: Nicolau Leal Werneck Date: Fri, 14 Oct 2022 08:24:34 +0200 Subject: [PATCH 5/5] removing extra outer loop on second stage --- src/SortingAlgorithms.jl | 36 +++++++++++++++--------------------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/src/SortingAlgorithms.jl b/src/SortingAlgorithms.jl index a84c118..64ed633 100644 --- a/src/SortingAlgorithms.jl +++ b/src/SortingAlgorithms.jl @@ -655,33 +655,27 @@ end function bitonicfirststage!(v, ::Val{Gap}, o::Ordering) where Gap N = length(v) - @inbounds begin - gap = 1 << Gap - g2 = gap >> 1 - for i in 0:gap:N-1 - firstj = max(0, i + gap - N) - for j in firstj:(g2-1) - ia = i + j - ib = i + gap - j - 1 - @inbounds comparator!(v, ia + 1, ib + 1, o) - end + gap = 1 << Gap + halfgap = 1 << (Gap - 1) + for i in 0:gap:N-1 + firstj = max(0, i + gap - N) + for j in firstj:(halfgap-1) + ia = i + j + ib = i + gap - j - 1 + @inbounds comparator!(v, ia + 1, ib + 1, o) end end end function bitonicsecondstage!(v, ::Val{Gap}, o::Ordering) where Gap N = length(v) - @inbounds begin - for n in Gap:-1:1 - gap = 1 << n - for i in 0:gap:N-1 - lastj = min(N - 1, N - (i + gap >> 1 + 1)) - for j in 0:lastj - ia = i + j - ib = i + j + gap >> 1 - @inbounds comparator!(v, ia + 1, ib + 1, o) - end - end + gap = 1 << Gap + for i in 0:gap:N-1 + lastj = min(N - 1, N - (i + gap >> 1 + 1)) + for j in 0:lastj + ia = i + j + ib = i + j + gap >> 1 + @inbounds comparator!(v, ia + 1, ib + 1, o) end end end