first working arbitrary msd radix sort
This commit is contained in:
parent
94a092c17e
commit
cfb66589f7
@ -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
|
||||
107
src/sorter.cpp
107
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<container::element_type> 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<container::element_type> range,
|
||||
size_t passes,
|
||||
const std::function<void(std::span<container::element_type> 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<container::element_type> (std::begin(range), lower), passes + 1, bucket_sort);
|
||||
sorter::msd_inplace_radix_sort(std::span<container::element_type> (lower, std::end(range)), passes + 1, bucket_sort);
|
||||
sorter::msd_inplace_radix_sort_binary(std::span<container::element_type> (std::begin(range), lower), passes + 1, bucket_sort);
|
||||
sorter::msd_inplace_radix_sort_binary(std::span<container::element_type> (lower, std::end(range)), passes + 1, bucket_sort);
|
||||
}
|
||||
|
||||
void sorter::msd_inplace_radix_sort(
|
||||
std::span<container::element_type> range,
|
||||
size_t passes,
|
||||
const std::function<void(std::span<container::element_type> 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<container::element_type> (buckets_start[i], buckets_end[i]), passes + 1, bucket_sort);
|
||||
}
|
||||
}
|
||||
void sorter::robin_hood_sort(std::span<container::element_type> bucket) {
|
||||
const auto size = bucket.size() + sorter::OVERHEAD_SIZE;
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
#include "container.hpp"
|
||||
#include "functional"
|
||||
#include "math.h"
|
||||
|
||||
namespace ae {
|
||||
|
||||
@ -16,7 +17,16 @@ class sorter {
|
||||
const std::function<void(std::span<container::element_type> bucket)>& bucket_sort
|
||||
);
|
||||
|
||||
const uint32_t OVERHEAD_SIZE = 10L;
|
||||
void msd_inplace_radix_sort_binary(
|
||||
std::span<container::element_type> range,
|
||||
size_t passes,
|
||||
const std::function<void(std::span<container::element_type> 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<container::element_type> range);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user