← Back to C-Kernel-Engine Docs Doxygen Source Documentation
 
Loading...
Searching...
No Matches
ck_threadpool.h
Go to the documentation of this file.
1/**
2 * @file ck_threadpool.h
3 * @brief Persistent pthread thread pool for CK-Engine inference
4 *
5 * Design goals:
6 * - Sub-microsecond dispatch latency (spin-wait barriers)
7 * - Zero allocation after init (all memory pre-allocated)
8 * - Cache-line aligned atomics to avoid false sharing
9 * - Hybrid polling: spin N rounds, then fall back to condvar
10 * - Thread 0 = main thread (does serial ops + its share of parallel work)
11 *
12 * Usage:
13 * ck_threadpool_t *pool = ck_threadpool_create(4); // 4 threads total
14 *
15 * // In decode loop:
16 * ck_threadpool_dispatch(pool, my_work_fn, args);
17 * // my_work_fn called on all threads with (ith, nth, args)
18 *
19 * // Between batches:
20 * ck_threadpool_pause(pool); // workers sleep (0% CPU)
21 * ck_threadpool_resume(pool); // wake workers
22 *
23 * ck_threadpool_destroy(pool);
24 *
25 * Architecture:
26 * STARTUP: Main creates N-1 worker pthreads, all spin on atomic counter
27 * DISPATCH: Main writes work desc, bumps counter, all threads execute
28 * BARRIER: Atomic counter + spin-wait with _mm_pause()
29 * PAUSE: Workers sleep on pthread_cond_t (0% CPU between batches)
30 */
31
32#ifndef CK_THREADPOOL_H
33#define CK_THREADPOOL_H
34
35#include <stdint.h>
36#include <stdatomic.h>
37#include <pthread.h>
38
39#ifdef __cplusplus
40extern "C" {
41#endif
42
43/* ============================================================================
44 * Configuration
45 * ============================================================================ */
46
47/** Maximum threads supported (main + workers) */
48#define CK_THREADPOOL_MAX_THREADS 64
49
50/** Number of spin iterations before falling back to condvar wait */
51#define CK_THREADPOOL_SPIN_COUNT 1024
52
53/** Cache line size for alignment (x86-64) */
54#define CK_CACHE_LINE 64
55
56/* ============================================================================
57 * Types
58 * ============================================================================ */
59
60/**
61 * Work function signature.
62 * Called on ALL threads (including main thread 0).
63 *
64 * @param ith Thread index (0 = main thread)
65 * @param nth Total number of threads
66 * @param args Opaque argument pointer (set via dispatch)
67 */
68typedef void (*ck_work_fn_t)(int ith, int nth, void *args);
69
70/**
71 * Thread pool state (opaque).
72 *
73 * All atomics are cache-line aligned to prevent false sharing.
74 * Workers spin on n_dispatch, checking for new work or shutdown.
75 */
76typedef struct ck_threadpool ck_threadpool_t;
77
78typedef struct {
81 uint64_t main_work_ns;
84
85/** Process the half-open interval [begin, end). */
86typedef void (*ck_range_fn_t)(int begin, int end, void *args);
87
88/** Scheduling policy for independent GEMM output tiles. */
94
95/**
96 * Set the process-wide GEMM tile scheduling policy.
97 *
98 * AUTO is the production default and currently selects dynamic work claiming
99 * for providers whose jobs write independent output tiles. Providers with
100 * ordered/shared reductions do not consult this policy.
101 *
102 * @return 0 on success, -1 for an invalid policy.
103 */
104int ck_set_gemm_schedule(int policy);
105
106/** Return the configured process-wide GEMM scheduling policy. */
107int ck_get_gemm_schedule(void);
108
109/** Return non-zero when independent GEMM tiles should use dynamic claiming. */
111
112/* ============================================================================
113 * Lifecycle
114 * ============================================================================ */
115
116/**
117 * Create a thread pool with `n_threads` total threads.
118 * Thread 0 is the calling (main) thread; n_threads-1 workers are spawned.
119 *
120 * @param n_threads Total thread count (including main). Must be >= 1.
121 * Pass 0 for auto-detect (physical cores).
122 * @return Pool handle, or NULL on failure.
123 */
124ck_threadpool_t *ck_threadpool_create(int n_threads);
125
126/**
127 * Create a pool whose ordinary dispatch width is smaller than its worker
128 * capacity. Exact providers may opt into the additional workers with
129 * ck_threadpool_dispatch_n(); ordinary dispatch remains at default_threads.
130 */
131ck_threadpool_t *ck_threadpool_create_capacity(int default_threads,
132 int capacity_threads);
133
134/**
135 * Compute the bounded capacity for an SMT-safe provider. The default width is
136 * preserved and at most half of the additional logical CPUs are reserved.
137 */
138int ck_threadpool_bounded_capacity(int default_threads, int logical_threads);
139
140/**
141 * Destroy the thread pool. Signals all workers to exit and joins them.
142 * Safe to call with NULL.
143 */
144void ck_threadpool_destroy(ck_threadpool_t *pool);
145
146/* ============================================================================
147 * Dispatch & Synchronization
148 * ============================================================================ */
149
150/**
151 * Dispatch work to all threads and wait for completion.
152 *
153 * 1. Sets the work function and args
154 * 2. Bumps the dispatch counter (wakes workers)
155 * 3. Main thread (ith=0) executes its share
156 * 4. Waits for all threads to complete via barrier
157 *
158 * This is a blocking call — returns when ALL threads have finished.
159 *
160 * @param pool Thread pool
161 * @param fn Work function (called on each thread)
162 * @param args Argument passed to fn
163 */
164void ck_threadpool_dispatch(ck_threadpool_t *pool, ck_work_fn_t fn, void *args);
165
166/**
167 * Dispatch work to a subset of the pool and wait for completion.
168 *
169 * Threads with ith >= active_threads remain idle for this dispatch.
170 * The work function sees nth == active_threads.
171 *
172 * @param pool Thread pool
173 * @param active_threads Number of active threads including main thread
174 * @param fn Work function
175 * @param args Argument passed to fn
176 */
177void ck_threadpool_dispatch_n(ck_threadpool_t *pool,
178 int active_threads,
179 ck_work_fn_t fn,
180 void *args);
181
182/**
183 * Dynamically distribute independent ranges through the persistent pool.
184 *
185 * Workers claim `grain_size` consecutive indices until [begin, end) is empty.
186 * This changes ownership only; callers remain responsible for ensuring that
187 * ranges write disjoint outputs and preserve each output's reduction order.
188 * Do not use this helper for unordered shared reductions.
189 */
190void ck_threadpool_parallel_for_n(ck_threadpool_t *pool,
191 int active_threads,
192 int begin,
193 int end,
194 int grain_size,
195 ck_range_fn_t fn,
196 void *args);
197
198/**
199 * Barrier synchronization within a dispatched work function.
200 *
201 * ALL threads must call this at the same point. Threads spin-wait
202 * until all have arrived, then proceed.
203 *
204 * Must only be called from within a work function (during dispatch).
205 *
206 * @param pool Thread pool
207 */
208void ck_threadpool_barrier(ck_threadpool_t *pool);
209
210/* ============================================================================
211 * Power Management
212 * ============================================================================ */
213
214/**
215 * Pause workers — they sleep on condvar (0% CPU).
216 * Call between batches or during interactive waiting.
217 * Workers wake on next dispatch or resume.
218 */
219void ck_threadpool_pause(ck_threadpool_t *pool);
220
221/**
222 * Resume workers — transition from sleep to spin-wait.
223 * Call before starting a new batch of work.
224 */
225void ck_threadpool_resume(ck_threadpool_t *pool);
226
227/* ============================================================================
228 * Queries
229 * ============================================================================ */
230
231/** Get the ordinary/default dispatch width (including the main thread). */
232int ck_threadpool_n_threads(const ck_threadpool_t *pool);
233
234/** Get the maximum worker capacity available to explicit dispatch_n calls. */
235int ck_threadpool_capacity(const ck_threadpool_t *pool);
236
237/** Get thread index for current thread (0 = main, -1 if not in pool) */
238int ck_threadpool_thread_id(const ck_threadpool_t *pool);
239
240/** Enable profiling and reset cumulative dispatch timing counters. */
241void ck_threadpool_profile_reset(ck_threadpool_t *pool);
242
243/** Snapshot cumulative dispatch timing counters without stopping workers. */
245 const ck_threadpool_t *pool, ck_threadpool_profile_t *profile);
246
247/* ============================================================================
248 * Global Thread Pool (convenience)
249 * ============================================================================ */
250
251/**
252 * Get or create the global thread pool.
253 * Thread-safe (uses pthread_once internally).
254 * Uses ck_get_num_threads() for the default width. In automatic mode the pool
255 * may reserve a bounded subset of SMT siblings as explicit provider capacity;
256 * ordinary dispatch remains at the default width.
257 *
258 * @return Global pool, never NULL after successful first call.
259 */
260ck_threadpool_t *ck_threadpool_global(void);
261
262/**
263 * Destroy the global thread pool.
264 * Called during engine shutdown.
265 */
267
268#ifdef __cplusplus
269}
270#endif
271
272#endif /* CK_THREADPOOL_H */
void ck_threadpool_pause(ck_threadpool_t *pool)
int ck_threadpool_capacity(const ck_threadpool_t *pool)
void ck_threadpool_parallel_for_n(ck_threadpool_t *pool, int active_threads, int begin, int end, int grain_size, ck_range_fn_t fn, void *args)
void ck_threadpool_profile_reset(ck_threadpool_t *pool)
void ck_threadpool_resume(ck_threadpool_t *pool)
ck_gemm_schedule_t
@ CK_GEMM_SCHEDULE_STATIC
@ CK_GEMM_SCHEDULE_AUTO
@ CK_GEMM_SCHEDULE_DYNAMIC
int ck_set_gemm_schedule(int policy)
void ck_threadpool_dispatch_n(ck_threadpool_t *pool, int active_threads, ck_work_fn_t fn, void *args)
ck_threadpool_t * ck_threadpool_global(void)
void ck_threadpool_global_destroy(void)
void ck_threadpool_destroy(ck_threadpool_t *pool)
void ck_threadpool_barrier(ck_threadpool_t *pool)
void ck_threadpool_profile_snapshot(const ck_threadpool_t *pool, ck_threadpool_profile_t *profile)
void(* ck_range_fn_t)(int begin, int end, void *args)
ck_threadpool_t * ck_threadpool_create(int n_threads)
ck_threadpool_t * ck_threadpool_create_capacity(int default_threads, int capacity_threads)
int ck_gemm_dynamic_schedule_enabled(void)
void(* ck_work_fn_t)(int ith, int nth, void *args)
void ck_threadpool_dispatch(ck_threadpool_t *pool, ck_work_fn_t fn, void *args)
int ck_threadpool_bounded_capacity(int default_threads, int logical_threads)
int ck_threadpool_thread_id(const ck_threadpool_t *pool)
int ck_get_gemm_schedule(void)
int ck_threadpool_n_threads(const ck_threadpool_t *pool)
uint32_t end
Definition utf8.c:215