← Back to C-Kernel-Engine Docs Doxygen Source Documentation
 
Loading...
Searching...
No Matches
ck_threadpool.h File Reference

Persistent pthread thread pool for CK-Engine inference. More...

#include <stdint.h>
#include <stdatomic.h>
#include <pthread.h>

Go to the source code of this file.

Data Structures

struct  ck_threadpool_profile_t
 

Macros

#define CK_CACHE_LINE   64
 
#define CK_THREADPOOL_MAX_THREADS   64
 
#define CK_THREADPOOL_SPIN_COUNT   1024
 

Typedefs

typedef void(* ck_range_fn_t) (int begin, int end, void *args)
 
typedef void(* ck_work_fn_t) (int ith, int nth, void *args)
 

Enumerations

enum  ck_gemm_schedule_t { CK_GEMM_SCHEDULE_AUTO = 0 , CK_GEMM_SCHEDULE_STATIC = 1 , CK_GEMM_SCHEDULE_DYNAMIC = 2 }
 

Functions

int ck_gemm_dynamic_schedule_enabled (void)
 
int ck_get_gemm_schedule (void)
 
int ck_set_gemm_schedule (int policy)
 
void ck_threadpool_barrier (ck_threadpool_t *pool)
 
int ck_threadpool_bounded_capacity (int default_threads, int logical_threads)
 
int ck_threadpool_capacity (const ck_threadpool_t *pool)
 
ck_threadpool_t * ck_threadpool_create (int n_threads)
 
ck_threadpool_t * ck_threadpool_create_capacity (int default_threads, int capacity_threads)
 
void ck_threadpool_destroy (ck_threadpool_t *pool)
 
void ck_threadpool_dispatch (ck_threadpool_t *pool, ck_work_fn_t fn, void *args)
 
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)
 
int ck_threadpool_n_threads (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_pause (ck_threadpool_t *pool)
 
void ck_threadpool_profile_reset (ck_threadpool_t *pool)
 
void ck_threadpool_profile_snapshot (const ck_threadpool_t *pool, ck_threadpool_profile_t *profile)
 
void ck_threadpool_resume (ck_threadpool_t *pool)
 
int ck_threadpool_thread_id (const ck_threadpool_t *pool)
 

Detailed Description

Persistent pthread thread pool for CK-Engine inference.

Design goals:

  • Sub-microsecond dispatch latency (spin-wait barriers)
  • Zero allocation after init (all memory pre-allocated)
  • Cache-line aligned atomics to avoid false sharing
  • Hybrid polling: spin N rounds, then fall back to condvar
  • Thread 0 = calling thread (does serial ops + its share of parallel work)

Usage: ck_threadpool_t *pool = ck_threadpool_create(4); // 4 threads total

// In decode loop: ck_threadpool_dispatch(pool, my_work_fn, args); // my_work_fn called on all threads with (ith, nth, args)

// Between batches: ck_threadpool_pause(pool); // workers sleep (0% CPU) ck_threadpool_resume(pool); // wake workers

ck_threadpool_destroy(pool);

Architecture: STARTUP: Main creates N-1 worker pthreads, all spin on atomic counter DISPATCH: Main writes work desc, bumps counter, all threads execute BARRIER: Atomic counter + spin-wait with _mm_pause() PAUSE: Workers sleep on pthread_cond_t (0% CPU between batches)

Definition in file ck_threadpool.h.

Macro Definition Documentation

◆ CK_CACHE_LINE

#define CK_CACHE_LINE   64

Cache line size for alignment (x86-64)

Definition at line 54 of file ck_threadpool.h.

◆ CK_THREADPOOL_MAX_THREADS

#define CK_THREADPOOL_MAX_THREADS   64

Maximum threads supported (main + workers)

Definition at line 48 of file ck_threadpool.h.

◆ CK_THREADPOOL_SPIN_COUNT

#define CK_THREADPOOL_SPIN_COUNT   1024

Number of spin iterations before falling back to condvar wait

Definition at line 51 of file ck_threadpool.h.

Typedef Documentation

◆ ck_range_fn_t

typedef void(* ck_range_fn_t) (int begin, int end, void *args)

Process the half-open interval [begin, end).

Definition at line 86 of file ck_threadpool.h.

◆ ck_work_fn_t

typedef void(* ck_work_fn_t) (int ith, int nth, void *args)

Work function signature. Called on ALL threads (including calling thread 0).

Parameters
ithThread index (0 = calling thread)
nthTotal number of threads
argsOpaque argument pointer (set via dispatch)

Definition at line 68 of file ck_threadpool.h.

Enumeration Type Documentation

◆ ck_gemm_schedule_t

Scheduling policy for independent GEMM output tiles.

Enumerator
CK_GEMM_SCHEDULE_AUTO 
CK_GEMM_SCHEDULE_STATIC 
CK_GEMM_SCHEDULE_DYNAMIC 

Definition at line 89 of file ck_threadpool.h.

89 {
ck_gemm_schedule_t
@ CK_GEMM_SCHEDULE_STATIC
@ CK_GEMM_SCHEDULE_AUTO
@ CK_GEMM_SCHEDULE_DYNAMIC

Function Documentation

◆ ck_gemm_dynamic_schedule_enabled()

int ck_gemm_dynamic_schedule_enabled ( void  )

Return non-zero when independent GEMM tiles should use dynamic claiming.

Definition at line 55 of file ck_threadpool.c.

56{
57 const int policy = ck_get_gemm_schedule();
58 return policy == CK_GEMM_SCHEDULE_AUTO || policy == CK_GEMM_SCHEDULE_DYNAMIC;
59}
int ck_get_gemm_schedule(void)

References CK_GEMM_SCHEDULE_AUTO, CK_GEMM_SCHEDULE_DYNAMIC, and ck_get_gemm_schedule().

Referenced by gemm_nt_q4_k_packed_vnni_x8_q8_k_split_min_threaded_4m().

◆ ck_get_gemm_schedule()

int ck_get_gemm_schedule ( void  )

Return the configured process-wide GEMM scheduling policy.

Definition at line 50 of file ck_threadpool.c.

51{
52 return atomic_load_explicit(&g_gemm_schedule, memory_order_acquire);
53}
static atomic_int g_gemm_schedule

References g_gemm_schedule.

Referenced by ck_gemm_dynamic_schedule_enabled().

◆ ck_set_gemm_schedule()

int ck_set_gemm_schedule ( int  policy)

Set the process-wide GEMM tile scheduling policy.

AUTO is the production default and currently selects dynamic work claiming for providers whose jobs write independent output tiles. Providers with ordered/shared reductions do not consult this policy.

Returns
0 on success, -1 for an invalid policy.

Definition at line 41 of file ck_threadpool.c.

42{
43 if (policy < CK_GEMM_SCHEDULE_AUTO || policy > CK_GEMM_SCHEDULE_DYNAMIC) {
44 return -1;
45 }
46 atomic_store_explicit(&g_gemm_schedule, policy, memory_order_release);
47 return 0;
48}

References CK_GEMM_SCHEDULE_DYNAMIC, and g_gemm_schedule.

◆ ck_threadpool_barrier()

void ck_threadpool_barrier ( ck_threadpool_t *  pool)

Barrier synchronization within a dispatched work function.

ALL participants in the current dispatch must call this at the same point. A single-worker dispatch returns immediately, even if the pool has more workers or another caller is executing a multi-worker dispatch.

Must only be called from within a work function (during dispatch).

Parameters
poolThread pool

Definition at line 549 of file ck_threadpool.c.

550{
551 /* Only participants in this multi-worker dispatch use its barrier.
552 * In particular, an overlapping single-worker callback must never touch
553 * the shared barrier belonging to a different dispatch. */
554 if (!pool || g_dispatch_context.pool != pool ||
555 g_dispatch_context.nth <= 1) return;
556 barrier_wait(&pool->barrier);
557}
static void barrier_wait(ck_barrier_t *b)
static _Thread_local ck_dispatch_context_t g_dispatch_context

References barrier_wait(), and g_dispatch_context.

◆ ck_threadpool_bounded_capacity()

int ck_threadpool_bounded_capacity ( int  default_threads,
int  logical_threads 
)

Compute the bounded capacity for an SMT-safe provider. The default width is preserved and at most half of the additional logical CPUs are reserved.

Definition at line 277 of file ck_threadpool.c.

278{
279 if (default_threads < 1) default_threads = 1;
280 if (default_threads > CK_THREADPOOL_MAX_THREADS) {
281 default_threads = CK_THREADPOOL_MAX_THREADS;
282 }
283 if (logical_threads <= default_threads) return default_threads;
284
285 int capacity = default_threads + (logical_threads - default_threads) / 2;
286 if (capacity > CK_THREADPOOL_MAX_THREADS) {
287 capacity = CK_THREADPOOL_MAX_THREADS;
288 }
289 return capacity;
290}
#define CK_THREADPOOL_MAX_THREADS

References CK_THREADPOOL_MAX_THREADS.

Referenced by global_pool_init().

◆ ck_threadpool_capacity()

int ck_threadpool_capacity ( const ck_threadpool_t *  pool)

Get the maximum worker capacity available to explicit dispatch_n calls.

Definition at line 589 of file ck_threadpool.c.

590{
591 return pool ? pool->n_threads : 1;
592}

Referenced by gemm_nt_q4_k_packed_vnni_x16_q8_k_split_min_threaded_16m(), and gemm_nt_q4_k_packed_vnni_x8_q8_k_split_min_threaded_4m().

◆ ck_threadpool_create()

ck_threadpool_t * ck_threadpool_create ( int  n_threads)

Create a thread pool with n_threads total threads. Thread 0 is the calling thread; n_threads-1 workers are spawned.

Parameters
n_threadsTotal thread count (including main). Must be >= 1. Pass 0 for auto-detect (physical cores).
Returns
Pool handle, or NULL on failure.

Definition at line 364 of file ck_threadpool.c.

365{
366 return ck_threadpool_create_capacity(n_threads, n_threads);
367}
ck_threadpool_t * ck_threadpool_create_capacity(int default_threads, int capacity_threads)

References ck_threadpool_create_capacity().

◆ ck_threadpool_create_capacity()

ck_threadpool_t * ck_threadpool_create_capacity ( int  default_threads,
int  capacity_threads 
)

Create a pool whose ordinary dispatch width is smaller than its worker capacity. Exact providers may opt into the additional workers with ck_threadpool_dispatch_n(); ordinary dispatch remains at default_threads.

Definition at line 292 of file ck_threadpool.c.

294{
295 if (default_threads <= 0) {
296 default_threads = ck_get_physical_cores();
297 if (default_threads <= 0) default_threads = 1;
298 /* Cap at reasonable default for memory-bound workloads */
299 if (default_threads > 8) default_threads = 8;
300 }
301 if (capacity_threads < default_threads) {
302 capacity_threads = default_threads;
303 }
304 if (capacity_threads > CK_THREADPOOL_MAX_THREADS) {
305 capacity_threads = CK_THREADPOOL_MAX_THREADS;
306 }
307 if (default_threads > capacity_threads) {
308 default_threads = capacity_threads;
309 }
310
311 ck_threadpool_t *pool = aligned_alloc(CK_CACHE_LINE, sizeof(ck_threadpool_t));
312 if (!pool) return NULL;
313 memset(pool, 0, sizeof(*pool));
314
315 pool->n_threads = capacity_threads;
316 pool->default_threads = default_threads;
317 atomic_store(&pool->n_dispatch, 0);
318 atomic_store(&pool->n_complete, 0);
319 atomic_store(&pool->active_threads, default_threads);
320 atomic_store(&pool->stop, 0);
321 atomic_store(&pool->paused, 0);
322 atomic_store(&pool->profile_enabled, 0);
323 pool->work_fn = NULL;
324 pool->work_args = NULL;
325
326 barrier_init(&pool->barrier, default_threads);
327
328 pthread_mutex_init(&pool->dispatch_mutex, NULL);
329 pthread_mutex_init(&pool->mutex, NULL);
330 pthread_cond_init(&pool->cond_dispatch, NULL);
331 pthread_cond_init(&pool->cond_done, NULL);
332
333 /* Slot 0 runs on the dispatch caller (no worker pthread created). */
334 pool->workers[0].id = 0;
335 pool->workers[0].pool = pool;
336 pool->workers[0].thread = pthread_self();
337
338 /* Spawn N-1 worker threads */
339 for (int i = 1; i < capacity_threads; i++) {
340 pool->workers[i].id = i;
341 pool->workers[i].pool = pool;
342
343 int rc = pthread_create(&pool->workers[i].thread, NULL,
344 worker_main, &pool->workers[i]);
345 if (rc != 0) {
346 fprintf(stderr, "[CK threadpool] Failed to create worker %d: %s\n",
347 i, strerror(rc));
348 /* Reduce thread count to what we managed to create */
349 pool->n_threads = i;
350 barrier_init(&pool->barrier, i);
351 break;
352 }
353 }
354
355 if (pool->n_threads > 1) {
356 fprintf(stderr,
357 "[CK threadpool] Created %d threads (default=%d, 1 main + %d workers)\n",
358 pool->n_threads, pool->default_threads, pool->n_threads - 1);
359 }
360
361 return pool;
362}
static void barrier_init(ck_barrier_t *b, int n_threads)
int ck_get_physical_cores(void)
static void * worker_main(void *arg)
#define CK_CACHE_LINE

References barrier_init(), CK_CACHE_LINE, ck_get_physical_cores(), CK_THREADPOOL_MAX_THREADS, and worker_main().

Referenced by ck_threadpool_create(), and global_pool_init().

◆ ck_threadpool_destroy()

void ck_threadpool_destroy ( ck_threadpool_t *  pool)

Destroy the thread pool. Signals all workers to exit and joins them. Safe to call with NULL.

Definition at line 369 of file ck_threadpool.c.

370{
371 if (!pool) return;
372
373 /* Signal shutdown */
374 atomic_store_explicit(&pool->stop, 1, memory_order_release);
375
376 /* Wake all sleeping workers */
377 pthread_mutex_lock(&pool->mutex);
378 pthread_cond_broadcast(&pool->cond_dispatch);
379 pthread_mutex_unlock(&pool->mutex);
380
381 /* Join all worker threads */
382 for (int i = 1; i < pool->n_threads; i++) {
383 pthread_join(pool->workers[i].thread, NULL);
384 }
385
386 pthread_cond_destroy(&pool->cond_dispatch);
387 pthread_cond_destroy(&pool->cond_done);
388 pthread_mutex_destroy(&pool->mutex);
389 pthread_mutex_destroy(&pool->dispatch_mutex);
390
391 free(pool);
392}

Referenced by ck_threadpool_global_destroy().

◆ ck_threadpool_dispatch()

void ck_threadpool_dispatch ( ck_threadpool_t *  pool,
ck_work_fn_t  fn,
void *  args 
)

Dispatch work to all threads and wait for completion.

  1. Sets the work function and args
  2. Bumps the dispatch counter (wakes workers)
  3. Main thread (ith=0) executes its share
  4. Waits for all worker callbacks to complete

This is a blocking call — returns when ALL threads have finished. Concurrent multi-worker dispatches on the same pool are serialized. Single-worker calls may execute concurrently because they do not publish a shared work descriptor. Do not destroy a pool while a call is active or recursively dispatch the same pool from one of its work functions.

Parameters
poolThread pool
fnWork function (called on each thread)
argsArgument passed to fn

Definition at line 494 of file ck_threadpool.c.

495{
496 if (!pool) return;
497 ck_threadpool_dispatch_n(pool, pool->default_threads, fn, args);
498}
void ck_threadpool_dispatch_n(ck_threadpool_t *pool, int active_threads, ck_work_fn_t fn, void *args)

References ck_threadpool_dispatch_n().

◆ ck_threadpool_dispatch_n()

void ck_threadpool_dispatch_n ( ck_threadpool_t *  pool,
int  active_threads,
ck_work_fn_t  fn,
void *  args 
)

Dispatch work to a subset of the pool and wait for completion.

Threads with ith >= active_threads remain idle for this dispatch. The work function sees nth == active_threads.

Parameters
poolThread pool
active_threadsNumber of active threads including the calling thread
fnWork function
argsArgument passed to fn

Definition at line 398 of file ck_threadpool.c.

399{
400 if (!pool || !fn) return;
401 if (active_threads <= 0) {
402 active_threads = 1;
403 }
404 if (active_threads > pool->n_threads) {
405 active_threads = pool->n_threads;
406 }
407
408 const int profile = atomic_load_explicit(
409 &pool->profile_enabled, memory_order_relaxed);
410 const uint64_t dispatch_start = profile ? monotonic_ns() : 0;
411
412 /* Single-thread fast path: just call directly */
413 if (active_threads == 1 || pool->n_threads == 1) {
414 run_work(pool, fn, 0, 1, args);
415 if (profile) {
416 const uint64_t dispatch_end = monotonic_ns();
417 atomic_fetch_add_explicit(&pool->profile_dispatch_count, 1, memory_order_relaxed);
418 atomic_fetch_add_explicit(
419 &pool->profile_dispatch_total_ns,
420 dispatch_end - dispatch_start,
421 memory_order_relaxed);
422 atomic_fetch_add_explicit(
423 &pool->profile_main_work_ns,
424 dispatch_end - dispatch_start,
425 memory_order_relaxed);
426 }
427 return;
428 }
429
430 /* The work descriptor and completion counter belong to one dispatch.
431 * Concurrent callers may share a pool, but their multi-worker dispatches
432 * must not overwrite one another's descriptor or barrier state. */
433 pthread_mutex_lock(&pool->dispatch_mutex);
434
435 /* Reset barrier phase for this dispatch */
436 barrier_init(&pool->barrier, active_threads);
437
438 /* Set work descriptor */
439 pthread_mutex_lock(&pool->mutex);
440 pool->work_fn = fn;
441 pool->work_args = args;
442 atomic_store_explicit(&pool->active_threads, active_threads, memory_order_release);
443 atomic_store_explicit(&pool->n_complete, 0, memory_order_release);
444
445 /* Wake workers by bumping dispatch counter */
446 atomic_fetch_add_explicit(&pool->n_dispatch, 1, memory_order_release);
447
448 /* Also signal condvar for sleeping workers */
449 pthread_cond_broadcast(&pool->cond_dispatch);
450 pthread_mutex_unlock(&pool->mutex);
451
452 /* Main thread (ith=0) does its share */
453 const uint64_t main_start = profile ? monotonic_ns() : 0;
454 run_work(pool, fn, 0, active_threads, args);
455 const uint64_t main_end = profile ? monotonic_ns() : 0;
456
457 /* Wait for all workers to complete */
458 if (active_threads > 1) {
459 int spins = 0;
460 while (atomic_load_explicit(&pool->n_complete, memory_order_acquire)
461 < active_threads - 1) {
463 spins++;
464 if (spins >= CK_THREADPOOL_SPIN_COUNT) {
465 pthread_mutex_lock(&pool->mutex);
466 if (atomic_load_explicit(&pool->n_complete, memory_order_acquire)
467 < active_threads - 1) {
468 pthread_cond_wait(&pool->cond_done, &pool->mutex);
469 }
470 pthread_mutex_unlock(&pool->mutex);
471 spins = 0;
472 }
473 }
474 }
475 if (profile) {
476 const uint64_t dispatch_end = monotonic_ns();
477 atomic_fetch_add_explicit(&pool->profile_dispatch_count, 1, memory_order_relaxed);
478 atomic_fetch_add_explicit(
479 &pool->profile_dispatch_total_ns,
480 dispatch_end - dispatch_start,
481 memory_order_relaxed);
482 atomic_fetch_add_explicit(
483 &pool->profile_main_work_ns,
484 main_end - main_start,
485 memory_order_relaxed);
486 atomic_fetch_add_explicit(
487 &pool->profile_completion_wait_ns,
488 dispatch_end - main_end,
489 memory_order_relaxed);
490 }
491 pthread_mutex_unlock(&pool->dispatch_mutex);
492}
static uint64_t monotonic_ns(void)
#define CK_SPIN_PAUSE()
static void run_work(ck_threadpool_t *pool, ck_work_fn_t fn, int ith, int nth, void *args)
#define CK_THREADPOOL_SPIN_COUNT

References barrier_init(), CK_SPIN_PAUSE, CK_THREADPOOL_SPIN_COUNT, monotonic_ns(), and run_work().

Referenced by adamw_clip_update_multi_f32(), adamw_update_f32(), attention_forward_causal_head_major_gqa_flash_strided_f16kv_workspace(), attention_forward_causal_head_major_gqa_flash_strided_sliding(), attention_forward_causal_head_major_gqa_flash_strided_sliding_gemma4_impl(), attention_forward_causal_head_major_gqa_llama_regular_strided_sliding_workspace(), attention_forward_causal_head_major_gqa_prefill_append_bf16cache_pytorch_contract_workspace(), attention_forward_causal_head_major_gqa_prefill_append_f16cache_gqa_reuse_config(), attention_forward_decode_head_major_gqa_flash_f16cache_split_partitioned(), attention_forward_head_major_gqa_flash_impl(), attention_forward_query_key_head_major_f32_decode_heads(), audio_conv1d_channel_major_grouped_f32(), ck_attention_f16_prefill_qtile64_dispatch(), ck_attention_forward_causal_head_major_gqa_prefill_segmented_f16cache_schedule_workspace(), ck_attention_forward_full_head_major_gqa_tiled_f16kv_fp32_strided(), ck_attention_forward_query_key_head_major_f32_run(), ck_attention_full_bf16_pytorch_flash(), ck_attention_full_bf16_sdpa_tiled(), ck_moe_q4k_mixed_parallel_workspace(), ck_moe_q4k_mixed_route_parallel(), ck_moe_q4k_q5k_route_parallel(), ck_moe_shared_gated_parallel_workspace(), ck_moe_shared_q4k_parallel_workspace(), ck_moe_swiglu_expert_forward_q4k_q5k_bucketed_impl(), ck_threadpool_dispatch(), ck_threadpool_parallel_for_n(), ds_mla_attention_parallel_dispatch(), gelu_erf_fp64_f32_parallel_dispatch(), gemm_backward_f32_train_parallel_dispatch(), gemm_backward_f32_train_parallel_dispatch_v2(), gemm_blocked_serial_train_parallel_dispatch(), gemm_f16_input_fp16_threadpool(), gemm_nt_bf16_amx_bf16_storage_workspace(), gemm_nt_bf16_native_bf16_storage(), gemm_nt_q4_k_packed_meta_q8_k_threaded(), gemm_nt_q4_k_packed_meta_q8_k_threaded_nsplit(), gemm_nt_q4_k_packed_meta_x16_gateup_swiglu_fused_vnni(), gemm_nt_q4_k_packed_meta_x16_q8_k_threaded_mreuse(), gemm_nt_q4_k_packed_meta_x16_q8_k_threaded_mtile(), gemm_nt_q4_k_packed_meta_x8_q8_k_split_min_threaded_4m(), gemm_nt_q4_k_packed_meta_x8_q8_k_split_min_threaded_8m(), gemm_nt_q4_k_packed_meta_x8_q8_k_split_min_threaded_mreuse(), gemm_nt_q4_k_packed_meta_x8_q8_k_threaded_mreuse(), gemm_nt_q4_k_packed_meta_x8_q8_k_threaded_mtile(), gemm_nt_q4_k_packed_meta_x8_q8_k_threaded_nsplit(), gemm_nt_q4_k_packed_u8_x16_q8_k_threaded_mtile(), gemm_nt_q4_k_packed_vnni_x16_q8_k_split_min_threaded_16m(), gemm_nt_q4_k_packed_vnni_x8_q8_k_split_min_threaded_4m(), gemm_nt_q4_k_q8_k_gateup_swiglu_fused_vnni(), gemm_q4_k_q8_k(), gradient_accumulate_f32(), gradient_accumulate_multi_f32(), gradient_clip_norm_f32(), gradient_global_norm_multi_f32(), gradient_scale_f32(), moe_swiglu_expert_forward_q4k_q5k_parallel_workspace(), and patch_projection_image_bf16_native_storage().

◆ ck_threadpool_global()

ck_threadpool_t * ck_threadpool_global ( void  )

Get or create the global thread pool. Thread-safe (uses pthread_once internally). Uses ck_get_num_threads() for the default width. In automatic mode the pool may reserve a bounded subset of SMT siblings as explicit provider capacity; ordinary dispatch remains at the default width.

Returns
Global pool, never NULL after successful first call.

Definition at line 685 of file ck_threadpool.c.

686{
687 pthread_once(&g_threadpool_once, global_pool_init);
688 return g_threadpool;
689}
static pthread_once_t g_threadpool_once
static void global_pool_init(void)
static ck_threadpool_t * g_threadpool

References g_threadpool, g_threadpool_once, and global_pool_init().

Referenced by adamw_clip_update_multi_f32(), adamw_update_f32(), attention_forward_causal_head_major_gqa_flash_strided_f16kv_workspace(), attention_forward_causal_head_major_gqa_flash_strided_sliding(), attention_forward_causal_head_major_gqa_flash_strided_sliding_gemma4_impl(), attention_forward_causal_head_major_gqa_llama_regular_strided_sliding_workspace(), attention_forward_causal_head_major_gqa_prefill_append_bf16cache_pytorch_contract_workspace(), attention_forward_causal_head_major_gqa_prefill_append_f16cache_auto_workspace(), attention_forward_causal_head_major_gqa_prefill_append_f16cache_gqa_reuse_config(), attention_forward_decode_head_major_gqa_flash_f16cache_split_partitioned(), attention_forward_head_major_gqa_flash_impl(), attention_forward_query_key_head_major_f32_decode_heads(), attn_gate_sigmoid_mul_forward(), audio_conformer_relative_attention_f32(), audio_conv1d_channel_major_grouped_f32(), audio_conv2d_whc_grouped_f32(), audio_fastconformer_block_workspace_bytes(), audio_glu_split_channel_major_f32(), audio_relative_shift_f32(), ck_attention_f16_prefill_qtile64_dispatch(), ck_attention_forward_causal_head_major_gqa_prefill_segmented_f16cache_schedule_workspace(), ck_attention_forward_full_head_major_gqa_tiled_f16kv_fp32_strided(), ck_attention_forward_query_key_head_major_f32_run(), ck_attention_full_bf16_pytorch_flash(), ck_attention_full_bf16_sdpa_tiled(), ck_gemma4_prepare_parallel(), ck_get_threadpool(), ck_moe_q4k_mixed_parallel_workspace(), ck_moe_shared_gated_parallel_workspace(), ck_moe_shared_q4k_parallel_workspace(), ck_moe_swiglu_expert_forward_q4k_q5k_bucketed_impl(), ck_threadpool_init(), deepseek_mla_kv_decompress_bf16_parallel_dispatch(), ds_mla_attention_parallel_dispatch(), gelu_erf_fp64_f32_parallel_dispatch(), gemm_backward_f32_train_parallel_dispatch(), gemm_backward_f32_train_parallel_dispatch_v2(), gemm_blocked_serial_train_parallel_dispatch(), gemm_f16_input_fp16_threadpool(), gemm_nt_bf16_accurate_storage_parallel_dispatch(), gemm_nt_bf16_amx_bf16_storage_workspace(), gemm_nt_bf16_bf16_storage_parallel_dispatch(), gemm_nt_bf16_native_bf16_storage(), gemm_nt_bf16_parallel_dispatch(), gemm_nt_fp32_exact_parallel_dispatch(), gemm_nt_q4_k_packed_meta_q8_k_threaded(), gemm_nt_q4_k_packed_meta_q8_k_threaded_nsplit(), gemm_nt_q4_k_packed_meta_x16_gateup_swiglu_fused_vnni(), gemm_nt_q4_k_packed_meta_x16_q8_k_threaded_mreuse(), gemm_nt_q4_k_packed_meta_x16_q8_k_threaded_mtile(), gemm_nt_q4_k_packed_meta_x8_q8_k_split_min_threaded_4m(), gemm_nt_q4_k_packed_meta_x8_q8_k_split_min_threaded_8m(), gemm_nt_q4_k_packed_meta_x8_q8_k_split_min_threaded_mreuse(), gemm_nt_q4_k_packed_meta_x8_q8_k_threaded_mreuse(), gemm_nt_q4_k_packed_meta_x8_q8_k_threaded_mtile(), gemm_nt_q4_k_packed_meta_x8_q8_k_threaded_nsplit(), gemm_nt_q4_k_packed_u8_x16_q8_k_threaded_mtile(), gemm_nt_q4_k_packed_vnni_x16_q8_k_split_min_threaded_16m(), gemm_nt_q4_k_packed_vnni_x8_q8_k_split_min_threaded_4m(), gemm_nt_q4_k_q8_k_gateup_swiglu_fused_vnni(), gemm_q4_k_q8_k(), gemma4_per_layer_embed_forward(), gemv_bf16_bf16_storage_parallel_dispatch(), gemv_bf16_parallel_dispatch(), gemv_nvfp4_q8_0_uniform(), gradient_accumulate_f32(), gradient_accumulate_multi_f32(), gradient_clip_norm_f32(), gradient_global_norm_multi_f32(), gradient_scale_f32(), moe_swiglu_expert_forward_q4k_q5k_parallel_workspace(), patch_projection_image_bf16_native_storage(), recurrent_conv_state_update_forward(), recurrent_qk_l2_norm_forward(), recurrent_silu_forward_ggml(), ssm_conv1d_forward_llama_fma(), ssm_conv1d_forward_llama_production(), swiglu_forward_exact(), and swiglu_forward_ggml().

◆ ck_threadpool_global_destroy()

void ck_threadpool_global_destroy ( void  )

Destroy the global thread pool. Called during engine shutdown.

Definition at line 691 of file ck_threadpool.c.

692{
693 if (g_threadpool) {
695 g_threadpool = NULL;
696 /* Reset once control so pool can be re-created if needed */
697 g_threadpool_once = PTHREAD_ONCE_INIT;
698 }
699}
void ck_threadpool_destroy(ck_threadpool_t *pool)

References ck_threadpool_destroy(), g_threadpool, and g_threadpool_once.

Referenced by ck_threadpool_shutdown().

◆ ck_threadpool_n_threads()

int ck_threadpool_n_threads ( const ck_threadpool_t *  pool)

Get the ordinary/default dispatch width (including the calling thread).

Definition at line 584 of file ck_threadpool.c.

585{
586 return pool ? pool->default_threads : 1;
587}

Referenced by adamw_clip_update_multi_f32(), adamw_update_f32(), attention_forward_causal_head_major_gqa_flash_strided_f16kv_workspace(), attention_forward_causal_head_major_gqa_llama_regular_strided_sliding_workspace(), attention_forward_causal_head_major_gqa_prefill_append_bf16cache_pytorch_contract_workspace(), attention_forward_causal_head_major_gqa_prefill_append_f16cache_auto_workspace(), attention_forward_causal_head_major_gqa_prefill_append_f16cache_gqa_reuse_config(), attention_forward_decode_head_major_gqa_flash_f16cache_split_partitioned(), attention_forward_query_key_head_major_f32_decode_heads(), attn_gate_sigmoid_mul_forward(), audio_conformer_relative_attention_f32(), audio_conv1d_channel_major_grouped_f32(), audio_conv2d_whc_grouped_f32(), audio_fastconformer_block_workspace_bytes(), audio_glu_split_channel_major_f32(), audio_relative_shift_f32(), ck_attention_f16_prefill_qtile64_dispatch(), ck_attention_forward_causal_head_major_gqa_prefill_segmented_f16cache_schedule_workspace(), ck_attention_forward_full_head_major_gqa_tiled_f16kv_fp32_strided(), ck_attention_forward_query_key_head_major_f32_run(), ck_attention_full_bf16_pytorch_flash(), ck_attention_full_bf16_sdpa_tiled(), ck_attention_pick_active_threads(), ck_gemm_f16_pick_active_threads(), ck_gemma4_prepare_parallel(), ck_moe_q4k_mixed_parallel_workspace(), ck_moe_q4k_mixed_route_parallel(), ck_moe_q4k_q5k_route_parallel(), ck_moe_shared_gated_parallel_workspace(), ck_moe_shared_q4k_parallel_workspace(), ck_moe_swiglu_expert_forward_q4k_q5k_bucketed_impl(), ck_sliding_attention_pick_threads(), deepseek_mla_kv_decompress_bf16_parallel_dispatch(), ds_mla_attention_parallel_dispatch(), gelu_erf_fp64_f32_parallel_dispatch(), gemm_backward_f32_train_parallel_dispatch(), gemm_backward_f32_train_parallel_dispatch_v2(), gemm_blocked_serial_train_parallel_dispatch(), gemm_nt_bf16_accurate_storage_parallel_dispatch(), gemm_nt_bf16_amx_bf16_storage_workspace(), gemm_nt_bf16_bf16_storage_parallel_dispatch(), gemm_nt_bf16_native_bf16_storage(), gemm_nt_bf16_parallel_dispatch(), gemm_nt_fp32_exact_parallel_dispatch(), gemm_nt_q4_k_packed_meta_q8_k_threaded(), gemm_nt_q4_k_packed_meta_q8_k_threaded_nsplit(), gemm_nt_q4_k_packed_meta_x16_gateup_swiglu_fused_vnni(), gemm_nt_q4_k_packed_meta_x16_q8_k_threaded_mreuse(), gemm_nt_q4_k_packed_meta_x16_q8_k_threaded_mtile(), gemm_nt_q4_k_packed_meta_x8_q8_k_split_min_threaded_4m(), gemm_nt_q4_k_packed_meta_x8_q8_k_split_min_threaded_8m(), gemm_nt_q4_k_packed_meta_x8_q8_k_split_min_threaded_mreuse(), gemm_nt_q4_k_packed_meta_x8_q8_k_threaded_mreuse(), gemm_nt_q4_k_packed_meta_x8_q8_k_threaded_mtile(), gemm_nt_q4_k_packed_meta_x8_q8_k_threaded_nsplit(), gemm_nt_q4_k_packed_u8_x16_q8_k_threaded_mtile(), gemm_nt_q4_k_q8_k_gateup_swiglu_fused_vnni(), gemm_q4_k_q8_k(), gemma4_per_layer_embed_forward(), gemv_bf16_bf16_storage_parallel_dispatch(), gemv_bf16_parallel_dispatch(), gemv_nvfp4_q8_0_uniform(), gradient_accumulate_f32(), gradient_accumulate_multi_f32(), gradient_clip_norm_f32(), gradient_global_norm_multi_f32(), gradient_scale_f32(), moe_swiglu_expert_forward_q4k_q5k_parallel_workspace(), patch_projection_image_bf16_native_storage(), recurrent_conv_state_update_forward(), recurrent_qk_l2_norm_forward(), recurrent_silu_forward_ggml(), ssm_conv1d_forward_llama_fma(), ssm_conv1d_forward_llama_production(), swiglu_forward_exact(), and swiglu_forward_ggml().

◆ ck_threadpool_parallel_for_n()

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 
)

Dynamically distribute independent ranges through the persistent pool.

Workers claim grain_size consecutive indices until [begin, end) is empty. This changes ownership only; callers remain responsible for ensuring that ranges write disjoint outputs and preserve each output's reduction order. Do not use this helper for unordered shared reductions.

Definition at line 523 of file ck_threadpool.c.

530{
531 if (!fn || begin >= end) return;
532 if (grain_size <= 0) grain_size = 1;
533 if (!pool || active_threads <= 1) {
534 fn(begin, end, args);
535 return;
536 }
537
538 ck_parallel_for_work_t work = {
539 .end = end,
540 .grain_size = grain_size,
541 .fn = fn,
542 .args = args,
543 };
544 atomic_init(&work.next, begin);
546 pool, active_threads, ck_parallel_for_worker, &work);
547}
static void ck_parallel_for_worker(int ith, int nth, void *opaque)
uint32_t end
Definition utf8.c:215

References ck_parallel_for_worker(), ck_threadpool_dispatch_n(), and end.

Referenced by attn_gate_sigmoid_mul_forward(), audio_conformer_relative_attention_f32(), audio_conv2d_whc_grouped_f32(), audio_glu_split_channel_major_f32(), audio_relative_shift_f32(), ck_gemma4_prepare_parallel(), ck_swiglu_dispatch_rows(), deepseek_mla_kv_decompress_bf16_parallel_dispatch(), gemm_nt_bf16_accurate_storage_parallel_dispatch(), gemm_nt_bf16_bf16_storage_parallel_dispatch(), gemm_nt_bf16_parallel_dispatch(), gemm_nt_fp32_exact_parallel_dispatch(), gemm_nt_q4_k_packed_vnni_x8_q8_k_split_min_threaded_4m(), gemma4_per_layer_embed_forward(), gemv_bf16_bf16_storage_parallel_dispatch(), gemv_bf16_parallel_dispatch(), gemv_nvfp4_q8_0_uniform(), recurrent_conv_state_update_forward(), recurrent_qk_l2_norm_forward(), recurrent_silu_forward_ggml(), ssm_conv1d_forward_llama_fma(), and ssm_conv1d_forward_llama_production().

◆ ck_threadpool_pause()

void ck_threadpool_pause ( ck_threadpool_t *  pool)

Pause workers — they sleep on condvar (0% CPU). Call between batches or during interactive waiting. Workers wake on next dispatch or resume.

Definition at line 563 of file ck_threadpool.c.

564{
565 if (!pool) return;
566 atomic_store_explicit(&pool->paused, 1, memory_order_release);
567}

◆ ck_threadpool_profile_reset()

void ck_threadpool_profile_reset ( ck_threadpool_t *  pool)

Enable profiling and reset cumulative dispatch timing counters.

Definition at line 607 of file ck_threadpool.c.

608{
609 if (!pool) return;
610 atomic_store_explicit(&pool->profile_dispatch_count, 0, memory_order_relaxed);
611 atomic_store_explicit(&pool->profile_dispatch_total_ns, 0, memory_order_relaxed);
612 atomic_store_explicit(&pool->profile_main_work_ns, 0, memory_order_relaxed);
613 atomic_store_explicit(&pool->profile_completion_wait_ns, 0, memory_order_relaxed);
614 atomic_store_explicit(&pool->profile_enabled, 1, memory_order_release);
615}

◆ ck_threadpool_profile_snapshot()

void ck_threadpool_profile_snapshot ( const ck_threadpool_t *  pool,
ck_threadpool_profile_t *  profile 
)

Snapshot cumulative dispatch timing counters without stopping workers.

Definition at line 617 of file ck_threadpool.c.

619{
620 if (!profile) return;
621 memset(profile, 0, sizeof(*profile));
622 if (!pool) return;
623 profile->dispatch_count = atomic_load_explicit(
624 &pool->profile_dispatch_count, memory_order_relaxed);
625 profile->dispatch_total_ns = atomic_load_explicit(
626 &pool->profile_dispatch_total_ns, memory_order_relaxed);
627 profile->main_work_ns = atomic_load_explicit(
628 &pool->profile_main_work_ns, memory_order_relaxed);
629 profile->completion_wait_ns = atomic_load_explicit(
630 &pool->profile_completion_wait_ns, memory_order_relaxed);
631}

References ck_threadpool_profile_t::completion_wait_ns, ck_threadpool_profile_t::dispatch_count, ck_threadpool_profile_t::dispatch_total_ns, and ck_threadpool_profile_t::main_work_ns.

◆ ck_threadpool_resume()

void ck_threadpool_resume ( ck_threadpool_t *  pool)

Resume workers — transition from sleep to spin-wait. Call before starting a new batch of work.

Definition at line 569 of file ck_threadpool.c.

570{
571 if (!pool) return;
572 atomic_store_explicit(&pool->paused, 0, memory_order_release);
573
574 /* Wake sleeping workers */
575 pthread_mutex_lock(&pool->mutex);
576 pthread_cond_broadcast(&pool->cond_dispatch);
577 pthread_mutex_unlock(&pool->mutex);
578}

◆ ck_threadpool_thread_id()

int ck_threadpool_thread_id ( const ck_threadpool_t *  pool)

Get the current callback's ith for this pool. Outside a callback, return the physical worker index (or the pool creator's legacy index 0), and -1 for an unrelated thread. The current dispatch caller is always index 0, including callers that did not create the pool.

Definition at line 594 of file ck_threadpool.c.

595{
596 if (!pool) return -1;
597 if (g_dispatch_context.pool == pool) return g_dispatch_context.ith;
598 pthread_t self = pthread_self();
599 for (int i = 0; i < pool->n_threads; i++) {
600 if (pthread_equal(self, pool->workers[i].thread)) {
601 return i;
602 }
603 }
604 return -1;
605}

References g_dispatch_context.

Referenced by attention_forward_causal_head_major_gqa_prefill_append_bf16cache_pytorch_contract_workspace(), attention_forward_causal_head_major_gqa_prefill_append_f16cache_gqa_reuse_config(), attn_gate_sigmoid_mul_forward(), ck_attention_f16_prefill_qtile64_dispatch(), ck_attention_forward_causal_head_major_gqa_prefill_segmented_f16cache_schedule_workspace(), ck_audio_conformer_relative_attention_f32_row_range(), ck_sliding_attention_pick_threads(), ck_swiglu_dispatch_rows(), gelu_erf_fp64_f32_parallel_dispatch(), recurrent_conv_state_update_forward(), recurrent_qk_l2_norm_forward(), and recurrent_silu_forward_ggml().