Compare commits
No commits in common. "edde2e75fb97181d44d0a1fbdd7ae57d87bf0fd7" and "c6cdc7ea6eed31e749a4eba2184235d57d95749a" have entirely different histories.
edde2e75fb
...
c6cdc7ea6e
@ -44,8 +44,6 @@ target_link_libraries(task1-randgen PRIVATE
|
|||||||
add_executable(task1-sorter)
|
add_executable(task1-sorter)
|
||||||
target_sources(task1-sorter PRIVATE
|
target_sources(task1-sorter PRIVATE
|
||||||
src/task1-sorter.cpp)
|
src/task1-sorter.cpp)
|
||||||
target_include_directories(task1-sorter PRIVATE
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include)
|
|
||||||
target_link_libraries(task1-sorter PRIVATE
|
target_link_libraries(task1-sorter PRIVATE
|
||||||
Qt6::Core)
|
Qt6::Core)
|
||||||
|
|
||||||
|
@ -4,30 +4,22 @@
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
// General purpose mergesorter with multi threading support by Robin Dietzel <robin.dietzel@iem.thm.de>
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
class MergeSorterMT {
|
class MergeSorterMT {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
template<typename C>
|
template<typename C>
|
||||||
MergeSorterMT(C cmp, int max_depth) : cmp(cmp), max_depth(max_depth) {
|
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<std::invoke_result_t<C, T, T>, bool>(), "C must be a function that returns a bool");
|
static_assert(std::is_same<std::invoke_result_t<C, T, T>, bool>(), "C must be a function that returns a bool");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start sorting process
|
|
||||||
auto sort(std::vector<T> &data) -> void {
|
auto sort(std::vector<T> &data) -> void {
|
||||||
// Create span: like a 'view' on the vector -> no unnecessary copies are made when subdividing sorting problem
|
|
||||||
std::span<T> sortable(data);
|
std::span<T> sortable(data);
|
||||||
split(sortable, 0, max_depth);
|
split(sortable, 0, max_depth);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
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<T> &output, std::span<T> left, std::span<T> right) -> void {
|
auto merge(std::span<T> &output, std::span<T> left, std::span<T> 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<T> buf;
|
std::vector<T> buf;
|
||||||
buf.reserve(left.size() + right.size());
|
buf.reserve(left.size() + right.size());
|
||||||
|
|
||||||
@ -35,7 +27,6 @@ private:
|
|||||||
auto r = right.begin();
|
auto r = right.begin();
|
||||||
auto o = buf.begin();
|
auto o = buf.begin();
|
||||||
|
|
||||||
// Insert from pre sorted half's
|
|
||||||
while (l < left.end() && r < right.end()) {
|
while (l < left.end() && r < right.end()) {
|
||||||
if (cmp(*l, *r)) {
|
if (cmp(*l, *r)) {
|
||||||
buf.insert(o, *l);
|
buf.insert(o, *l);
|
||||||
@ -46,72 +37,52 @@ private:
|
|||||||
}
|
}
|
||||||
o++;
|
o++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fill up with rest of left values
|
|
||||||
while (l < left.end()) {
|
while (l < left.end()) {
|
||||||
buf.insert(o, *l);
|
buf.insert(o, *l);
|
||||||
o++;
|
o++;
|
||||||
l++;
|
l++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fill up with rest of right values
|
|
||||||
while (r < right.end()) {
|
while (r < right.end()) {
|
||||||
buf.insert(o, *r);
|
buf.insert(o, *r);
|
||||||
o++;
|
o++;
|
||||||
r++;
|
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());
|
std::move(buf.begin(), buf.end(), output.begin());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Splitup function
|
|
||||||
auto split(std::span<T> &data, int depth, const int &mdepth) -> void {
|
auto split(std::span<T> &data, int depth, const int &mdepth) -> void {
|
||||||
|
|
||||||
if (std::distance(data.begin(), data.end()) <= 1) {
|
if (std::distance(data.begin(), data.end()) <= 1) {
|
||||||
// Quit if only one element 'insortable'
|
|
||||||
return;
|
return;
|
||||||
} else if (std::distance(data.begin(), data.end()) == 2) {
|
} 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])) {
|
if(cmp(data[1], data[0])) {
|
||||||
std::swap(data[0], data[1]);
|
std::swap(data[0], data[1]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine mid of data
|
|
||||||
auto mid = data.begin();
|
auto mid = data.begin();
|
||||||
std::advance(mid, std::distance(data.begin(), data.end()) / 2);
|
std::advance(mid, std::distance(data.begin(), data.end()) / 2);
|
||||||
|
|
||||||
// Generate left and right view on data (no copies are made here)
|
|
||||||
std::span<T> left(data.begin(), mid);
|
std::span<T> left(data.begin(), mid);
|
||||||
std::span<T> right(mid, data.end());
|
std::span<T> right(mid, data.end());
|
||||||
|
|
||||||
if (depth < mdepth) {
|
if (depth < mdepth) {
|
||||||
// Create recursive split functions if maximum depth not reached
|
|
||||||
std::thread left_thread([&]() { split(left, depth + 1, mdepth); });
|
std::thread left_thread([&]() { split(left, depth + 1, mdepth); });
|
||||||
std::thread right_thread([&]() { split(right, 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();
|
left_thread.join();
|
||||||
right_thread.join();
|
right_thread.join();
|
||||||
} else {
|
} else {
|
||||||
// Do normal recursion in a single thread if maximum depth is reached
|
|
||||||
split(left, depth + 1, mdepth);
|
split(left, depth + 1, mdepth);
|
||||||
split(right, depth + 1, mdepth);
|
split(right, depth + 1, mdepth);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Merge left and right together before returning
|
|
||||||
merge(data, left, right);
|
merge(data, left, right);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Templated comparator function
|
|
||||||
std::function<bool(T, T)> cmp;
|
std::function<bool(T, T)> cmp;
|
||||||
// Maximum depth
|
|
||||||
const int max_depth;
|
const int max_depth;
|
||||||
};
|
};
|
@ -8,7 +8,7 @@
|
|||||||
#include <ranges>
|
#include <ranges>
|
||||||
|
|
||||||
|
|
||||||
auto main(int argc, char *argv[]) -> int{
|
int main(int argc, char *argv[]) {
|
||||||
QCoreApplication app(argc, argv);
|
QCoreApplication app(argc, argv);
|
||||||
QCoreApplication::setApplicationName("Random dataset generator");
|
QCoreApplication::setApplicationName("Random dataset generator");
|
||||||
QCoreApplication::setApplicationVersion("1.0");
|
QCoreApplication::setApplicationVersion("1.0");
|
||||||
|
@ -7,11 +7,8 @@
|
|||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <ranges>
|
#include <ranges>
|
||||||
#include <chrono>
|
|
||||||
|
|
||||||
#include <mergesort_mt.h>
|
int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
auto main(int argc, char *argv[]) -> int {
|
|
||||||
QCoreApplication app(argc, argv);
|
QCoreApplication app(argc, argv);
|
||||||
QCoreApplication::setApplicationName("Multi purpose mergesort application");
|
QCoreApplication::setApplicationName("Multi purpose mergesort application");
|
||||||
QCoreApplication::setApplicationVersion("1.0.42");
|
QCoreApplication::setApplicationVersion("1.0.42");
|
||||||
@ -19,8 +16,7 @@ auto main(int argc, char *argv[]) -> int {
|
|||||||
|
|
||||||
|
|
||||||
QCommandLineParser parser;
|
QCommandLineParser parser;
|
||||||
parser.setApplicationDescription(
|
parser.setApplicationDescription("Used to run either sequential or parallel mergesort on a texfile containing ascii encoded int32s");
|
||||||
"Used to run either sequential or parallel mergesort on a texfile containing ascii encoded int32s");
|
|
||||||
parser.addHelpOption();
|
parser.addHelpOption();
|
||||||
parser.addVersionOption();
|
parser.addVersionOption();
|
||||||
|
|
||||||
@ -32,51 +28,20 @@ auto main(int argc, char *argv[]) -> int {
|
|||||||
parser.addOption(sequential);
|
parser.addOption(sequential);
|
||||||
parser.addOption(parallel);
|
parser.addOption(parallel);
|
||||||
parser.addOption(nthreads);
|
parser.addOption(nthreads);
|
||||||
parser.addOption(output);
|
|
||||||
|
|
||||||
parser.addPositionalArgument("dataset", "Filename where to load the data from");
|
parser.addPositionalArgument("dataset", "Filename where to load the data from");
|
||||||
|
|
||||||
parser.process(app);
|
parser.process(app);
|
||||||
|
|
||||||
const QStringList args = parser.positionalArguments();
|
|
||||||
|
|
||||||
if (args.length() != 1) {
|
|
||||||
parser.showHelp(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString source = args.at(0);
|
|
||||||
QFile input(source);
|
|
||||||
if (!input.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
|
||||||
print << "Could not open file " << source << " for reading" << Qt::endl;
|
|
||||||
print << input.errorString();
|
|
||||||
app.exit(-1);
|
|
||||||
return app.exec();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<int32_t> dataset;
|
|
||||||
|
|
||||||
QTextStream stream(&input);
|
|
||||||
while (!stream.atEnd()) {
|
|
||||||
QString line = stream.readLine();
|
|
||||||
bool ok;
|
|
||||||
int parsed_value = line.toUInt(&ok);
|
|
||||||
if (!ok) {
|
|
||||||
print << "Error converting value: " << line << Qt::endl;
|
|
||||||
} else {
|
|
||||||
dataset.push_back(std::move(parsed_value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
print << "Read " << dataset.size() << " values from " << source << Qt::endl;
|
|
||||||
|
|
||||||
const int threads = std::thread::hardware_concurrency();
|
const int threads = std::thread::hardware_concurrency();
|
||||||
int max_depth = std::sqrt(threads);
|
int max_depth = std::sqrt(threads);
|
||||||
|
|
||||||
print << "Hardware concurrency of " << threads << " detected" << Qt::endl;
|
print << "Hardware concurrency of " << threads << " detected" << Qt::endl;
|
||||||
|
|
||||||
if (parser.isSet(nthreads)) {
|
if(parser.isSet(nthreads)) {
|
||||||
bool ok;
|
bool ok;
|
||||||
max_depth = parser.value(nthreads).toInt(&ok);
|
max_depth = parser.value(nthreads).toInt(&ok);
|
||||||
if (!ok) {
|
if(!ok) {
|
||||||
parser.showHelp(-1);
|
parser.showHelp(-1);
|
||||||
}
|
}
|
||||||
print << "Overwriting maximum parallelized recursion depth with " << max_depth << Qt::endl;
|
print << "Overwriting maximum parallelized recursion depth with " << max_depth << Qt::endl;
|
||||||
@ -84,39 +49,7 @@ auto main(int argc, char *argv[]) -> int {
|
|||||||
print << "Assuming default parallelized recursion depth via sqrt(nthreads) of " << max_depth << Qt::endl;
|
print << "Assuming default parallelized recursion depth via sqrt(nthreads) of " << max_depth << Qt::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parser.isSet(sequential)) {
|
|
||||||
auto buf = dataset;
|
|
||||||
auto t1 = std::chrono::high_resolution_clock::now();
|
|
||||||
MergeSorterMT<int32_t> sorter(
|
|
||||||
[](int32_t a, int32_t b) {
|
|
||||||
return (a > b);
|
|
||||||
}, 0);
|
|
||||||
sorter.sort(buf);
|
|
||||||
auto t2 = std::chrono::high_resolution_clock::now();
|
|
||||||
auto diff = t2 - t1;
|
|
||||||
|
|
||||||
print << "=> Duration for sequential sort: " << std::chrono::duration_cast<std::chrono::milliseconds>(diff).count() << " ms" << Qt::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parser.isSet(parallel)) {
|
|
||||||
auto buf = dataset;
|
|
||||||
auto t1 = std::chrono::high_resolution_clock::now();
|
|
||||||
MergeSorterMT<int32_t> sorter(
|
|
||||||
[](int32_t a, int32_t b) {
|
|
||||||
return (a > b);
|
|
||||||
}, max_depth);
|
|
||||||
sorter.sort(buf);
|
|
||||||
auto t2 = std::chrono::high_resolution_clock::now();
|
|
||||||
auto diff = t2 - t1;
|
|
||||||
|
|
||||||
print << "=> Duration for parallel sort: " << std::chrono::duration_cast<std::chrono::milliseconds>(diff).count() << " ms" << Qt::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(parser.isSet(output)) {
|
|
||||||
print << "Sooory, not yet implemented :( you might do it yourself!" << Qt::endl;
|
|
||||||
app.exit(-1);
|
|
||||||
return app.exec();
|
|
||||||
}
|
|
||||||
|
|
||||||
app.exit(0);
|
app.exit(0);
|
||||||
return 0;
|
return 0;
|
||||||
|
Loading…
Reference in New Issue
Block a user