diff --git a/apps/meson.build b/apps/meson.build index 22b360affd5ae5edb962550ca86cf053ffa02a17..6ee5a2d128dc6170326fd80af9597e80edcc8156 100644 --- a/apps/meson.build +++ b/apps/meson.build @@ -34,4 +34,10 @@ echoclient_exe = executable( dependencies: emper_dep, ) +qsort = executable( + 'qsort', + 'qsort.cpp', + dependencies: emper_dep, +) + subdir('fsearch') diff --git a/apps/qsort.cpp b/apps/qsort.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8fa25ac192817f212b36199d5b2bb388e8408f87 --- /dev/null +++ b/apps/qsort.cpp @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright © 2021 Florian Fischer +/** + * qsort benchmark implementation similar to those used for this blog post: + * https://zig.news/kprotty/resource-efficient-thread-pools-with-zig-3291 + + * Comparables benchmark sources (rust, go, zig) can be found at: + * https://github.com/kprotty/zap/tree/blog/benchmarks + */ + +#include <chrono> +#include <cstdint> +#include <cstdlib> +#include <iostream> + +#include "CountingPrivateSemaphore.hpp" +#include "Fiber.hpp" +#include "Runtime.hpp" +#include "emper.hpp" + +using std::chrono::duration_cast; +using std::chrono::high_resolution_clock; +using std::chrono::milliseconds; + +static void fill(int* arr, size_t s) { + for (int i = 0; static_cast<size_t>(i) < s; ++i) { + arr[i] = i; + } +} + +static void swap(int* n, int* m) { + int tmp = *n; + *n = *m; + *m = tmp; +} + +static void shuffle(int* arr, size_t s) { + uint32_t xs = 0xdeadbeef; + for (size_t i = 0; i < s; ++i) { + xs ^= xs << 13; + xs ^= xs >> 17; + xs ^= xs << 5; + size_t j = xs % (i + 1); + swap(&arr[i], &arr[j]); + } +} + +static auto verify(const int* arr, size_t s) -> bool { + for (size_t i = 1; i < s; ++i) { + if (arr[i - 1] > arr[i]) { + return false; + } + } + return true; +} + +static void insertion_sort(int* arr, size_t s) { + for (size_t i = 1; i < s; i++) { + size_t n = i; + while (n > 0 && arr[n] < arr[n - 1]) { + swap(&arr[n], &arr[n - 1]); + n -= 1; + } + } +} + +static auto partition(int* arr, size_t s) -> size_t { + size_t pivot = s - 1; + size_t i = 0; + for (size_t j = 0; j < pivot; ++j) { + if (arr[j] <= arr[pivot]) { + swap(&arr[i], &arr[j]); + i += 1; + } + } + swap(&arr[i], &arr[pivot]); + return i; +} + +static void qsort(int* arr, size_t s) { + if (s <= 32) { + insertion_sort(arr, s); + return; + } + + size_t mid = partition(arr, s); + + CPS cps; + spawn([&] { qsort(arr, mid); }, cps); + spawn([&] { qsort(&arr[mid], s - mid); }, cps); + cps.wait(); +} + +static const size_t ARR_SIZE = 10 * 1000 * 1000; + +auto main() -> int { + int* arr = new int[ARR_SIZE]; + + std::cout << "filling" << std::endl; + fill(arr, ARR_SIZE); + + std::cout << "shuffling" << std::endl; + shuffle(arr, ARR_SIZE); + + Runtime runtime; + + auto* sorter = Fiber::from([&]() { + const auto start = std::chrono::steady_clock::now(); + qsort(arr, ARR_SIZE); + const auto end = std::chrono::steady_clock::now(); + auto ms = duration_cast<milliseconds>(end - start); + std::cout << "sorting took " << ms.count() << "ms" << std::endl; + runtime.initiateTermination(); + }); + runtime.scheduleFromAnywhere(*sorter); + + runtime.waitUntilFinished(); + + int exit_code = EXIT_SUCCESS; + if (!verify(arr, ARR_SIZE)) { + std::cerr << "Array is not sorted" << std::endl; + exit_code = EXIT_FAILURE; + } + + delete[] arr; + + return exit_code; +}