From cfb66589f71e79574ac553a055fffb231c409796 Mon Sep 17 00:00:00 2001 From: Patrick Schneider Date: Thu, 25 Sep 2025 20:32:42 +0200 Subject: [PATCH] first working arbitrary msd radix sort --- result.txt | 5 +++ src/sorter.cpp | 107 ++++++++++++++++++++++++++++++++++++++++++++++--- src/sorter.hpp | 12 +++++- 3 files changed, 117 insertions(+), 7 deletions(-) diff --git a/result.txt b/result.txt index e69de29..e881d50 100644 --- a/result.txt +++ b/result.txt @@ -0,0 +1,5 @@ +RESULT name=sort n=100 t=1 iterations=301 durationNanoseconds=3324 totalDurationNanoseconds=1000561 constructorNanoseconds=274 totalConstructorNanoseconds=82660 +RESULT name=sort n=1000 t=1 iterations=13 durationNanoseconds=77557 totalDurationNanoseconds=1008241 constructorNanoseconds=2057 totalConstructorNanoseconds=26750 +RESULT name=sort n=10001 t=1 iterations=1 durationNanoseconds=1509911 totalDurationNanoseconds=1509911 constructorNanoseconds=108831 totalConstructorNanoseconds=108831 +RESULT name=sort n=100000 t=1 iterations=1 durationNanoseconds=8070546 totalDurationNanoseconds=8070546 constructorNanoseconds=488620 totalConstructorNanoseconds=488620 +RESULT name=sort n=10000000 t=1 iterations=1 durationNanoseconds=723407878 totalDurationNanoseconds=723407878 constructorNanoseconds=51148616 totalConstructorNanoseconds=51148616 diff --git a/src/sorter.cpp b/src/sorter.cpp index 4f78d38..6ea00ac 100755 --- a/src/sorter.cpp +++ b/src/sorter.cpp @@ -17,23 +17,25 @@ void sorter::sort(container& data) { data.placeholder_[i].clear(); } #if DEBUG + for (int i = 0; i < data.placeholder_[0].size(); i++) { + // if (copy[i] != data.placeholder_[0][i]) + std::cerr << i << " before:" << data.placeholder_[0][i] << std::endl; + } + std::vector copy; std::ranges::copy(data.placeholder_[0], std::back_inserter(copy)); std::sort(copy.begin(), copy.end()); #endif - // std::sort(data.placeholder_[0].begin(), data.placeholder_[0].end()); sorter::msd_inplace_radix_sort(data.placeholder_[0], 0, [&](auto span) {sorter::robin_hood_sort(span);}); - #if DEBUG for (int i = 0; i < copy.size(); i++) { if (copy[i] != data.placeholder_[0][i]) std::cerr << i << " " << "sorted: " << copy[i] << " actual:" << data.placeholder_[0][i] << std::endl; } #endif - } -void sorter::msd_inplace_radix_sort( +void sorter::msd_inplace_radix_sort_binary( std::span range, size_t passes, const std::function bucket)>& bucket_sort @@ -74,8 +76,101 @@ void sorter::msd_inplace_radix_sort( #if DEBUG std::cerr << "pass: " << passes << " begin: " << &*std::begin(range) << " end: " << &*std::end(range) << " lower: " << &*lower << std::endl; #endif - sorter::msd_inplace_radix_sort(std::span (std::begin(range), lower), passes + 1, bucket_sort); - sorter::msd_inplace_radix_sort(std::span (lower, std::end(range)), passes + 1, bucket_sort); + sorter::msd_inplace_radix_sort_binary(std::span (std::begin(range), lower), passes + 1, bucket_sort); + sorter::msd_inplace_radix_sort_binary(std::span (lower, std::end(range)), passes + 1, bucket_sort); +} + +void sorter::msd_inplace_radix_sort( + std::span range, + size_t passes, + const std::function bucket)>& bucket_sort +) { + if (std::begin(range) >= std::end(range)) { + return; + } + + if (range.size() <= sorter::SMALL_SORT_THRESHHOLD) { + bucket_sort(range); + return; + } + + // We first determine the number of elements per bucket + // This is one pass additional pass over the elements and needs O(buckets) additional space, so in one configuration constant overhead + uint32_t bucket_sizes[sorter::RADIX_BUCKETS] = { 0 }; + auto upper_bucket_mask = ((1L << sorter::RADIX_SIZE) - 1) << (sizeof(container::element_type) * CHAR_BIT - sorter::RADIX_SIZE * (1 + passes)); + + auto mask_bucket = [&](container::element_type* element){ return (*element & upper_bucket_mask) >> (sizeof(container::element_type) * CHAR_BIT - sorter::RADIX_SIZE) * (1 + passes); }; + + for (auto element : range) { + auto bucket = mask_bucket(&element); + bucket_sizes[bucket]++; + } + #if DEBUG + std::cerr << "Bucket sizes: "; + for (auto bucket : bucket_sizes) { + std::cerr << bucket << " "; + } + std::cerr << std::endl; + #endif + + // We now point each bucket to its start location in the range + container::element_type* buckets_end[sorter::RADIX_BUCKETS]; + container::element_type* buckets_start[sorter::RADIX_BUCKETS]; + #if DEBUG + std::cerr << "Starting bucket" << std::endl; + #endif + auto count = 0; + for (int i = 0; i < sorter::RADIX_BUCKETS; ++i) { + buckets_end[i] = &range[count]; + buckets_start[i] = &range[count]; + #if DEBUG + std::cerr << "bucket " << i << " at " << count << std::endl; + #endif + count += bucket_sizes[i]; + } + #if DEBUG + std::cerr << "finish" << std::endl; + #endif + // Loop over the elements and swap them into the correct buckets. + // This will look at each element exactly once. + auto element = &range[0]; + while (element < &*std::end(range)) { + uint32_t bucket = mask_bucket(element); + + // Check if we are currently in the bounds of the corresponding bucket + if (&*element >= buckets_start[bucket] && &*element < buckets_end[bucket]) { + // The element is in the correct bucket, we skip to the end of the bucket + element = buckets_end[bucket]; + } else { + // The element is not in the correct bucket; swap + std::swap(*element, *buckets_end[bucket]); + buckets_end[bucket]++; + } + } + #if DEBUG + for (int i = 0; i < range.size(); i++) { + std::cerr << i << " reordered:" << range[i] << std::endl; + } + std::cerr << "Finish reordering elements" << std::endl; + std::cerr << "Bucket elements at begin of bucket" << std::endl; + for (auto bucket : buckets_start) { + std::cerr << *bucket << " bucket " << mask_bucket(bucket) << std::endl; + } + std::cerr << std::endl; + #endif + + for (auto i = 0; i < sorter::RADIX_BUCKETS - 1; ++i) { + assert(buckets_end[i] == buckets_start[i + 1]); + } + assert(buckets_end[sorter::RADIX_BUCKETS - 1] == &*std::end(range)); + #if DEBUG + std::cerr << "Ranges of buckets are correct" << std::endl; + #endif + + // sort each bucket recursively + for (auto i = 0; i < sorter::RADIX_BUCKETS; i++) { + sorter::msd_inplace_radix_sort(std::span (buckets_start[i], buckets_end[i]), passes + 1, bucket_sort); + } } void sorter::robin_hood_sort(std::span bucket) { const auto size = bucket.size() + sorter::OVERHEAD_SIZE; diff --git a/src/sorter.hpp b/src/sorter.hpp index bcc0925..2dec15d 100755 --- a/src/sorter.hpp +++ b/src/sorter.hpp @@ -2,6 +2,7 @@ #include "container.hpp" #include "functional" +#include "math.h" namespace ae { @@ -16,7 +17,16 @@ class sorter { const std::function bucket)>& bucket_sort ); - const uint32_t OVERHEAD_SIZE = 10L; + void msd_inplace_radix_sort_binary( + std::span range, + size_t passes, + const std::function bucket)>& bucket_sort + ); + + const uint32_t OVERHEAD_SIZE = 100L; + const uint32_t SMALL_SORT_THRESHHOLD = 32; + const uint32_t RADIX_SIZE = 4; + const uint32_t RADIX_BUCKETS = std::pow(2, 4); const uint32_t RADIX_ITERATIONS = 8; void robin_hood_sort(std::span range);