diff --git a/task1/include/mergesort_mt.h b/task1/include/mergesort_mt.h index 494b847..152bbb7 100644 --- a/task1/include/mergesort_mt.h +++ b/task1/include/mergesort_mt.h @@ -4,22 +4,30 @@ #include #include +// General purpose mergesorter with multi threading support by Robin Dietzel template class MergeSorterMT { public: template MergeSorterMT(C cmp, int max_depth) : cmp(cmp), max_depth(max_depth) { + // Assert that cmp is a function that returns bool and takes two arguments of type T static_assert(std::is_same, bool>(), "C must be a function that returns a bool"); } + // Start sorting process auto sort(std::vector &data) -> void { + // Create span: like a 'view' on the vector -> no unnecessary copies are made when subdividing sorting problem std::span sortable(data); split(sortable, 0, max_depth); } private: + // Merge function that merges left & right span into the output span + // No exclusive access on output is necessary (e.g. via mutex) because all parallel threads work on different parts of output auto merge(std::span &output, std::span left, std::span right) -> void { + // Create buffer, here we need a temporary container where we copy values to, because left and right are a view on parts + // of output std::vector buf; buf.reserve(left.size() + right.size()); @@ -27,6 +35,7 @@ private: auto r = right.begin(); auto o = buf.begin(); + // Insert from pre sorted half's while (l < left.end() && r < right.end()) { if (cmp(*l, *r)) { buf.insert(o, *l); @@ -37,52 +46,72 @@ private: } o++; } + + // Fill up with rest of left values while (l < left.end()) { buf.insert(o, *l); o++; l++; } + + // Fill up with rest of right values while (r < right.end()) { buf.insert(o, *r); o++; r++; } + // Completely move buffer to output + // IMPORTANT: left and right are still a view on the splitted output, that is now sorted std::move(buf.begin(), buf.end(), output.begin()); } + // Splitup function auto split(std::span &data, int depth, const int &mdepth) -> void { + if (std::distance(data.begin(), data.end()) <= 1) { + // Quit if only one element 'insortable' return; } else if (std::distance(data.begin(), data.end()) == 2) { + // Swap two values dependant on size for small speedup (no call to further split must be made) if(cmp(data[1], data[0])) { std::swap(data[0], data[1]); return; } } + // Determine mid of data auto mid = data.begin(); std::advance(mid, std::distance(data.begin(), data.end()) / 2); + // Generate left and right view on data (no copies are made here) std::span left(data.begin(), mid); std::span right(mid, data.end()); if (depth < mdepth) { + // Create recursive split functions if maximum depth not reached std::thread left_thread([&]() { split(left, depth + 1, mdepth); }); std::thread right_thread([&]() { split(right, depth + 1, mdepth); }); + // Both threads must join before we could further work on the data viewed + // by left and right (recursively sorted by the both calls) left_thread.join(); right_thread.join(); } else { + // Do normal recursion in a single thread if maximum depth is reached split(left, depth + 1, mdepth); split(right, depth + 1, mdepth); } + // Merge left and right together before returning merge(data, left, right); + return; } private: + // Templated comparator function std::function cmp; + // Maximum depth const int max_depth; }; \ No newline at end of file diff --git a/task1/src/task1-sorter.cpp b/task1/src/task1-sorter.cpp index 5765beb..cf4bb90 100644 --- a/task1/src/task1-sorter.cpp +++ b/task1/src/task1-sorter.cpp @@ -32,6 +32,7 @@ auto main(int argc, char *argv[]) -> int { parser.addOption(sequential); parser.addOption(parallel); parser.addOption(nthreads); + parser.addOption(output); parser.addPositionalArgument("dataset", "Filename where to load the data from"); parser.process(app);