# Parallelism

> NumKong for C

NumKong does not manage its own threads.
That is deliberate.
The library is designed to sit inside a larger scheduler.

GEMM-like packed work is usually partitioned across row ranges of `A` against one shared packed `B`:

```cpp
using nk::range, nk::all, nk::slice;
fork_union.parallel_for(0, worker_count, [&](std::size_t t) {
    auto start = t * rows_per_worker;
    auto stop = std::min(start + rows_per_worker, total_rows);
    auto a_slice = a[range(start, stop), all, slice].as_matrix();
    auto c_slice = c[range(start, stop), all, slice].as_matrix();
    statuses[t] = nk::dots_packed<value_type_>(a_slice, packed, c_slice);
});
```

SYRK-like symmetric work is partitioned by output row windows on one matrix:

```cpp
fork_union.parallel_for(0, worker_count, [&](std::size_t t) {
    auto start = t * rows_per_worker;
    auto count = std::min(rows_per_worker, total_rows - start);
    statuses[t] = nk::dots_symmetric<value_type_>(vectors.as_matrix_view(), gram.as_matrix_span(), start, count);
    if (nk::succeeded(statuses[t]))
        statuses[t] = nk::angulars_symmetric<value_type_>(vectors.as_matrix_view(), angular_dists.as_matrix_span(), start, count);
});
```

We recommend [ForkUnion](/docs/forkunion/) for that host-side orchestration.
OpenMP is still a reasonable fit if the rest of your application already uses it.
Manual thread pools and task systems also work well because the kernels have explicit row-range interfaces.

The C++26 Executors TS (`std::execution`) is a natural fit here.
NumKong kernels take explicit row-range parameters and do not own threads, so they compose directly with `std::execution::bulk` or any sender/receiver scheduler.
When executors ship in your toolchain, replacing the `parallel_for` lambda above with a `bulk` sender is a one-line change.

### Threading Model

The C library creates no threads, uses no OpenMP, and keeps no hidden thread pool.
Its shared and static builds link pthreads only for `pthread_once`, which runs capability detection once per process.
Parallelism is host-controlled: partition work across row ranges and dispatch through ForkUnion, `std::thread`, or any external scheduler.
The Python and Node bindings parallelize their batched calls through `src/parallel.c`, which the C library never links: OpenMP on Linux and FreeBSD, Grand Central Dispatch on macOS, and the system thread pool on Windows.

