C++ · Parallelism

Parallelism

8.0.0-rc.1 · b1f866ab

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:

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:

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 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.