nk
1.0.0
C++ Numerical Computing Runtime
Toggle main menu visibility
Loading...
Searching...
No Matches
thread_pool.hpp
1
// core/include/nk/ops/thread_pool.hpp
2
//
3
// Persistent worker pool used by SIMD kernels above their parallel
4
// thresholds. The pool is built once at first use (singleton) with
5
// `std::thread::hardware_concurrency()` workers and stays alive for
6
// the lifetime of the process. Each `run()` call hands a function
7
// `fn(start, end)` to the workers, splits [0, n) evenly, and blocks
8
// until every chunk has finished.
9
//
10
// This header is only present when NK_WITH_THREADS is defined at
11
// build time. The companion `parallel_for.hpp` includes it
12
// conditionally and falls back to a sequential `fn(0, n)` otherwise,
13
// so call sites stay portable across the threaded and non-threaded
14
// builds.
15
16
#pragma once
17
18
#include <atomic>
19
#include <condition_variable>
20
#include <cstddef>
21
#include <functional>
22
#include <mutex>
23
#include <thread>
24
#include <vector>
25
26
namespace
nk::detail {
27
28
class
ThreadPool
29
{
30
public
:
31
// Process-wide singleton. Sized to hardware_concurrency() at first call.
32
static
ThreadPool &global();
33
34
explicit
ThreadPool(
int
n_workers);
35
~ThreadPool();
36
37
ThreadPool(
const
ThreadPool &) =
delete
;
38
ThreadPool &operator=(
const
ThreadPool &) =
delete
;
39
40
int
workers()
const
{
return
static_cast<
int
>
(workers_.size()); }
41
42
// Run `fn(start, end)` over an even split of [0, n) across workers,
43
// blocking until every chunk finishes. With one or zero workers this
44
// simply calls `fn(0, n)` on the calling thread.
45
//
46
// `max_workers` (when > 0) caps the number of workers actually used
47
// for this task — useful for memory-bandwidth-bound kernels where
48
// 6+ workers don't help and just add signaling noise on dual-channel
49
// DDR5. Pass 0 (the default) to use every worker.
50
void
run(std::size_t n, std::function<
void
(std::size_t, std::size_t)> fn,
51
int
max_workers = 0);
52
53
private
:
54
void
worker_loop(
int
id
);
55
56
std::vector<std::thread> workers_;
57
std::mutex mu_;
58
// Serializes concurrent run() submitters (e.g. two engines sharing this
59
// process-wide pool on different threads). Held across publish+wait; never
60
// contended by workers (they use mu_) nor by a nested run() (which the
61
// worker-thread guard short-circuits to an inline call before reaching it).
62
std::mutex submit_mu_;
63
std::condition_variable cv_start_;
64
std::condition_variable cv_done_;
65
66
// Current task. `epoch_` is bumped on every dispatch so workers
67
// can tell a new task apart from a spurious wake-up. `active_`
68
// is how many workers participate in this task (≤ workers_.size());
69
// workers with id ≥ active_ wake up on cv_start_ but skip the
70
// task body so they don't decrement task_remaining_.
71
std::function<void(std::size_t, std::size_t)> task_;
72
std::size_t task_n_ = 0;
73
std::atomic<int> task_remaining_{0};
74
int
active_ = 0;
75
std::atomic<int> epoch_{0};
76
std::atomic<bool> shutdown_{
false
};
77
};
78
79
}
// namespace nk::detail
src
ops
include
nk
ops
thread_pool.hpp
Generated by
1.17.0