diff options
| author | Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> | 2026-05-08 17:22:58 +0300 |
|---|---|---|
| committer | Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> | 2026-05-08 17:22:58 +0300 |
| commit | e0b7d30fd437292c88141fb08d60681870b86c6e (patch) | |
| tree | 7d7f4e94cbec0f4f494042f7cbf39c7c8e7234fe /pixpat-native/src/threading.h | |
Squashed 'subprojects/pixpat/' content from commit d444626
git-subtree-dir: subprojects/pixpat
git-subtree-split: d444626e6ba988ec6d487800721e447f94b1eaf5
Diffstat (limited to 'pixpat-native/src/threading.h')
| -rw-r--r-- | pixpat-native/src/threading.h | 95 |
1 files changed, 95 insertions, 0 deletions
diff --git a/pixpat-native/src/threading.h b/pixpat-native/src/threading.h new file mode 100644 index 0000000..5e7fc01 --- /dev/null +++ b/pixpat-native/src/threading.h @@ -0,0 +1,95 @@ +#pragma once + +#include <unistd.h> + +#include <cassert> +#include <cstddef> +#include <exception> +#include <thread> +#include <vector> + +namespace pixpat +{ + +inline unsigned default_thread_count() +{ + long n = sysconf(_SC_NPROCESSORS_ONLN); + if (n < 1) + return 1; + // Cap to keep per-stripe work meaningful and avoid heavy + // oversubscription on large NUMA hosts. + if (n > 16) + n = 16; + return static_cast<unsigned>(n); +} + +/* + * Run `fn(start_y, end_y)` over `[0, height)` partitioned into stripes + * aligned to `v_sub`. Half-open ranges, matching the `for (by = 0; + * by < H; by += bh)` block-loop style. + * + * `fn` must be callable as `void(size_t start_y, size_t end_y)` and is + * invoked concurrently from multiple threads — it must be safe to call + * with disjoint Y-ranges in parallel. Exceptions thrown from a worker + * are captured and the first (by stripe index) is rethrown after all + * workers join. + * + * When `n_threads <= 1`, `fn` is called inline on the calling thread — + * no `std::thread` is spawned, no allocation occurs. + */ +template<typename F> +void run_stripes(size_t height, unsigned v_sub, unsigned n_threads, F&& fn) +{ + if (height == 0 || v_sub == 0) + return; + + // Callers (pixpat_convert / pixpat_draw_pattern) validate divisibility + // at the entry point. + assert(height % v_sub == 0); + + const size_t max_useful = height / v_sub; + if (n_threads == 0) + n_threads = 1; + if (static_cast<size_t>(n_threads) > max_useful) + n_threads = static_cast<unsigned>(max_useful); + + if (n_threads <= 1) { + fn(size_t{ 0 }, height); + return; + } + + // Stripe height rounded up to v_sub; last stripe absorbs the + // remainder. + size_t part_height = (height + n_threads - 1) / n_threads; + part_height = (part_height + v_sub - 1) / v_sub * v_sub; + + std::vector<std::exception_ptr> errors(n_threads); + std::vector<std::thread> workers; + workers.reserve(n_threads); + + for (unsigned i = 0; i < n_threads; i++) { + size_t start = i * part_height; + if (start >= height) + break; + size_t end = start + part_height; + if (i == n_threads - 1 || end > height) + end = height; + + workers.emplace_back([&, i, start, end] { + try { + fn(start, end); + } catch (...) { + errors[i] = std::current_exception(); + } + }); + } + + for (auto& t : workers) + t.join(); + + for (auto& e : errors) + if (e) + std::rethrow_exception(e); +} + +} // namespace pixpat |
