aca-tasks/task1/mergesort.h

378 lines
13 KiB
C
Raw Normal View History

2023-10-31 20:11:31 +00:00
#pragma once
#include <vector>
#include <algorithm>
#include <thread>
#include <mutex>
2023-11-01 16:05:01 +00:00
#include <future>
#include <ranges>
2023-11-01 20:04:12 +00:00
#include <span>
2023-10-31 20:11:31 +00:00
namespace algo {
2023-11-01 09:19:25 +00:00
class MergeSort_v1 {
private:
template<typename Iterator, typename Comparator>
static auto
merge(Iterator start, Iterator middle, Iterator end, Comparator cmp, Iterator output_start) -> void {
2023-11-01 09:19:25 +00:00
Iterator start_m = start;
Iterator begin = output_start;
Iterator start2 = middle + 1;
2023-10-31 20:11:31 +00:00
2023-11-01 09:19:25 +00:00
//merge from input until one half completes
while (start <= middle && start2 <= end) {
if (cmp(*start, *start2)) {
*output_start = *start;
start++;
} else {
*output_start = *start2;
start2++;
}
output_start++;
}
//try to finish first half
while (start <= middle) {
2023-10-31 20:11:31 +00:00
*output_start = *start;
start++;
2023-11-01 09:19:25 +00:00
output_start++;
}
while (start2 <= end) {
2023-10-31 20:11:31 +00:00
*output_start = *start2;
start2++;
2023-11-01 09:19:25 +00:00
output_start++;
2023-10-31 20:11:31 +00:00
}
2023-11-01 09:19:25 +00:00
const auto size = std::distance(start_m, end);
for (auto i = 0; i <= size; i++, start_m++, begin++) {
*start_m = *begin;
}
2023-10-31 20:11:31 +00:00
}
2023-11-01 09:19:25 +00:00
template<typename Container, typename Iterator, typename Comparator>
static auto
ms_split(Container &output_vec, Iterator start, Iterator end, Comparator cmp, Iterator output_start) -> void {
2023-11-01 09:19:25 +00:00
Iterator mid = start;
Iterator begin = output_start;
2023-10-31 20:11:31 +00:00
2023-11-01 09:19:25 +00:00
if (std::distance(start, end) < 1) {
return;
} else {
//move mid iterator litterally to the mid
std::advance(mid, std::distance(start, end) / 2);
//sort the first half within an recursion
ms_split(output_vec, start, mid, cmp, output_start);
2023-10-31 20:11:31 +00:00
2023-11-01 09:19:25 +00:00
//move output iterator
std::advance(output_start, std::distance(start, mid + 1));
//sort the second half within a recursion
ms_split(output_vec, mid + 1, end, cmp, output_start);
2023-10-31 20:11:31 +00:00
2023-11-01 09:19:25 +00:00
//merge everything together starting from the complete beginning
merge(start, mid, end, cmp, begin);
}
}
2023-10-31 20:11:31 +00:00
2023-11-01 09:19:25 +00:00
public:
template<typename Iterator, typename Comparator>
static auto sort(Iterator start, Iterator end, Comparator cmp) -> void {
using valtype = typename std::iterator_traits<Iterator>::value_type;
std::vector<valtype> temporary_dataset(std::distance(start, end));
ms_split(temporary_dataset, start, end - 1, cmp, temporary_dataset.begin());
2023-11-01 09:00:36 +00:00
}
2023-11-01 09:19:25 +00:00
};
2023-10-31 20:11:31 +00:00
class MergeSort_v2 {
private:
2023-11-01 12:37:49 +00:00
2023-11-01 16:05:01 +00:00
template<typename Container, typename Comparator>
2023-11-01 12:37:49 +00:00
static auto
2023-11-01 16:05:01 +00:00
mt_merge(Container left, Container right, Comparator cmp) -> Container {
//using Iterator = typename std::iterator_traits<Container>::value_type;
Container output;
auto lefti = left.begin();
auto righti = right.begin();
while (lefti < left.end() && righti < right.end()) {
if (cmp(*lefti, *righti)) {
output.emplace_back(std::move(*lefti));
lefti++;
} else {
output.emplace_back(std::move(*righti));
righti++;
}
}
while (lefti < left.end()) {
output.emplace_back(std::move(*lefti));
lefti++;
}
while (righti < right.end()) {
output.emplace_back(std::move(*righti));
righti++;
}
2023-11-01 12:37:49 +00:00
2023-11-01 16:05:01 +00:00
return output;
2023-11-01 12:37:49 +00:00
}
template<typename Iterator, typename Comparator>
static auto
2023-11-01 16:05:01 +00:00
merge(Iterator start, Iterator middle, Iterator end, Comparator cmp, Iterator output_start,
std::recursive_mutex &dataset_guard) -> void {
Iterator start_m = start;
Iterator begin = output_start;
Iterator start2 = middle + 1;
//merge from input until one half completes
while (start <= middle && start2 <= end) {
if (cmp(*start, *start2)) {
*output_start = *start;
start++;
} else {
*output_start = *start2;
start2++;
}
output_start++;
}
//try to finish first half
while (start <= middle) {
*output_start = *start;
start++;
output_start++;
}
while (start2 <= end) {
*output_start = *start2;
start2++;
output_start++;
}
2023-11-01 11:32:52 +00:00
dataset_guard.lock();
const auto size = std::distance(start_m, end);
for (auto i = 0; i <= size; i++, start_m++, begin++) {
*start_m = *begin;
}
2023-11-01 11:32:52 +00:00
dataset_guard.unlock();
}
2023-11-01 16:05:01 +00:00
template<typename Container, typename Iterator, typename Comparator>
static auto mt_split(Container &output_vec, Iterator start, Iterator end, Comparator cmp, Iterator output_start,
int &nthreads, std::recursive_mutex &dataset_guard, std::mutex &depth_guard) -> void {
Iterator mid = start;
Iterator to_start = output_start;
if (std::distance(start, end) < 1) {
return;
}
bool rem_threads;
{
std::lock_guard guard(depth_guard); //RAII guard
rem_threads = nthreads > 1;
}
if (rem_threads) {
{
std::lock_guard guard(depth_guard); //RAII guard
nthreads -= 2;
}
std::advance(mid, std::distance(start, end) / 2);
std::thread t1([&]() {
mt_split(output_vec, start, mid, cmp, output_start, nthreads, dataset_guard, depth_guard);
});
std::advance(output_start, std::distance(start, mid + 1));
std::thread t2([&]() {
mt_split(output_vec, mid + 1, end, cmp, output_start, nthreads, dataset_guard, depth_guard);
});
//merge everything together starting from the complete beginning
t1.join();
t2.join();
std::vector<int> left;
left.assign(start, mid);
std::vector<int> right;
right.assign(mid + 1, end);
mt_merge(left, right, cmp);
} else {
//move mid iterator litterally to the mid
std::advance(mid, std::distance(start, end) / 2);
//sort the first half within an recursion
mt_split(output_vec, start, mid, cmp, output_start, nthreads, dataset_guard, depth_guard);
//move output iterator
std::advance(output_start, std::distance(start, mid + 1));
//sort the second half within a recursion
mt_split(output_vec, mid + 1, end, cmp, output_start, nthreads, dataset_guard, depth_guard);
std::vector<int> left;
left.assign(start, mid);
std::vector<int> right;
right.assign(mid + 1, end);
mt_merge(left, right, cmp);
}
}
template<typename Container, typename Iterator, typename Comparator>
static auto ms_split(Container &output_vec, Iterator start, Iterator end, Comparator cmp, Iterator output_start,
2023-11-01 11:32:52 +00:00
int &nthreads, std::recursive_mutex &dataset_guard, std::mutex &depth_guard) -> void {
Iterator mid = start;
Iterator begin = output_start;
if (std::distance(start, end) < 1) {
2023-11-01 11:32:52 +00:00
//Quit on smalles list size (one element is always sorted)
return;
} else {
if (nthreads > 1) {
depth_guard.lock();
nthreads -= 2;
depth_guard.unlock();
//move mid iterator litterally to the mid
std::advance(mid, std::distance(start, end) / 2);
//sort the first half within an recursion
2023-11-01 16:05:01 +00:00
std::thread t1([&]() {
ms_split(output_vec, start, mid, cmp, output_start, nthreads, dataset_guard, depth_guard);
});
//move output iteratoroutput_vec, start, mid, cmp, output_start
std::advance(output_start, std::distance(start, mid + 1));
//sort the second half within a recursion
2023-11-01 16:05:01 +00:00
std::thread t2([&]() {
ms_split(output_vec, mid + 1, end, cmp, output_start, nthreads, dataset_guard, depth_guard);
});
//merge everything together starting from the complete beginning
t1.join();
t2.join();
2023-11-01 11:32:52 +00:00
merge(start, mid, end, cmp, begin, dataset_guard);
} else {
//move mid iterator litterally to the mid
std::advance(mid, std::distance(start, end) / 2);
//sort the first half within an recursion
ms_split(output_vec, start, mid, cmp, output_start, nthreads, dataset_guard, depth_guard);
//move output iterator
std::advance(output_start, std::distance(start, mid + 1));
//sort the second half within a recursion
ms_split(output_vec, mid + 1, end, cmp, output_start, nthreads, dataset_guard, depth_guard);
//merge everything together starting from the complete beginning
2023-11-01 11:32:52 +00:00
merge(start, mid, end, cmp, begin, dataset_guard);
}
}
}
public:
template<typename Iterator, typename Comparator>
static auto sort(Iterator start, Iterator end, Comparator cmp, int nthreads) -> void {
using valtype = typename std::iterator_traits<Iterator>::value_type;
std::vector<valtype> temporary_dataset(std::distance(start, end));
2023-11-01 11:32:52 +00:00
std::recursive_mutex dataset_guard;
std::mutex depth_guard;
2023-11-01 16:05:01 +00:00
mt_split(temporary_dataset, start, end - 1, cmp, temporary_dataset.begin(), nthreads, dataset_guard,
depth_guard);
}
};
2023-11-01 16:05:01 +00:00
class MergeSort_mt {
template<typename T, typename Comparator>
static auto
merge(typename std::vector<T>::iterator mid, std::vector<T> &left, std::vector<T> &right,
Comparator cmp, std::mutex &mut) {
2023-11-01 20:38:09 +00:00
auto l = left.begin();
auto r = right.begin();
while (l < left.end() && r < right.end()) {
if (cmp(*l, *r)) {
2023-11-01 16:05:01 +00:00
*mid = *l;
l++;
2023-11-01 20:38:09 +00:00
} else {
2023-11-01 16:05:01 +00:00
*mid = *r;
r++;
}
2023-11-01 20:38:09 +00:00
mid++;
}
while (l < left.end()) {
*mid = *l;
mid++;
l++;
}
while (r < right.end()) {
*mid = *r;
mid++;
r++;
2023-11-01 16:05:01 +00:00
}
2023-11-01 20:38:09 +00:00
2023-11-01 16:05:01 +00:00
}
template<typename T, typename Comparator>
2023-11-01 20:38:09 +00:00
static auto split(std::vector<T> &data, std::vector<T> &output, Comparator cmp, int depth, int &num_threads,
std::mutex &mut) {
2023-11-01 16:05:01 +00:00
if (data.size() <= 1) {
return;
}
auto mid = data.begin();
std::advance(mid, std::distance(data.begin(), data.end()) / 2);
2023-11-01 20:38:09 +00:00
std::vector<T> left(data.begin(), mid);
std::vector<T> right(mid, data.end());
2023-11-01 16:05:01 +00:00
if (depth < num_threads) {
2023-11-01 20:38:09 +00:00
std::thread left_thread([&]() { split(left, output, cmp, depth + 1, num_threads, mut); });
std::thread right_thread([&]() { split(right, output, cmp, depth + 1, num_threads, mut); });
2023-11-01 16:05:01 +00:00
left_thread.join();
right_thread.join();
} else {
2023-11-01 20:38:09 +00:00
split(left, output, cmp, depth + 1, num_threads, mut);
split(right, output, cmp, depth + 1, num_threads, mut);
2023-11-01 16:05:01 +00:00
}
2023-11-01 20:38:09 +00:00
mid = output.begin();
std::advance(mid, std::distance(output.begin(), output.end()) / 2);
std::vector<T> buf;
buf.reserve(data.size());
merge(buf.begin(), left, right, cmp, mut);
{
std::lock_guard lock(mut);
auto bufi = buf.begin();
for(auto &element : output) {
element = *bufi;
bufi++;
}
}
2023-11-01 16:05:01 +00:00
}
public:
template<typename T, typename Comparator>
static auto
sort(std::vector<T> &data, Comparator cmp, int num_threads = std::thread::hardware_concurrency()) -> void {
std::vector<T> local_result = data;
std::mutex local_result_lock;
split(data, local_result, cmp, 0, num_threads, local_result_lock);
data = local_result;
}
};
2023-10-31 20:11:31 +00:00
}