nx_parallel.nx
buildroot/runtime/nx_parallel.nx
about
nx_parallel.nx -- parallel_for / parallel_map / parallel_reduce.
Built on top of nx_thread_pool: each primitive partitions its
iteration range into n_workers chunks and submits one task per
chunk. Wait until pool.tasks_completed has advanced by n_chunks
(snapshot-based, so multiple parallel_* calls compose).
Why partition rather than per-iter-submit:
* Per-iter submit pays ~20ns chan_send + dispatch per index;
for cheap bodies (e.g. element-wise add) that dwarfs the
work. Chunked submission amortises chan overhead across
~ITER_COUNT/n_workers items.
Static even partition for MVP -- range is divided into roughly
equal contiguous chunks. Dynamic / work-stealing partitioning
is the L7-evolution follow-up once we measure imbalanced
workloads suffering >5% throughput loss.
Public API (single namespace; sealed strategy enum):
nx_parallel_for(pool, begin, end, fn(i64))
nx_parallel_map_i64(pool, in_ptr, out_ptr, n, fn(i64) -> i64)
nx_parallel_reduce_i64(pool, ptr, n, init, fn(i64, i64) -> i64)
Composes against: [[nx_thread_pool_shared_queue]] (workers),
[[vyukov_mpmc_channel]] (task queue), [[atomic_intrinsics_real_amo]]
(per-chunk reduction sum), [[nx_hw_dynamic_probes]] (chunk count
defaults to nx_hw_worker_count).
dependencies 4 imports · 5 importers
imports: nx_syscalls.nxnx_atom.nxnx_thread_pool.nxnx_hw.nx
imported by: nx_dot_simd_demo.nxnx_parallel_simd_reduce.nxnx_parallel_test.nxnx_pardemo_heavy.nxnx_sum_bench_all.nx
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| 40 | struct NxPfChunk |
| 48 | struct NxMapChunk |
| 58 | struct NxReduceChunk |
consts
| 46 | const NX_PF_CHUNK_BYTES: i64 = 24 |
| 56 | const NX_MAP_CHUNK_BYTES: i64 = 40 |
| 67 | const NX_REDUCE_CHUNK_BYTES: i64 = 48 |
functions
| 70 | func _nx_pf_worker(ctx: i64) -> i64 |
| 82 | func _nx_map_worker(ctx: i64) -> i64 |
| 98 | func _nx_reduce_worker(ctx: i64) -> i64 calls 1: nx_atom_store_i64 |
| 116 | func _nx_chunk_start(begin: i64, end: i64, n_chunks: i64, idx: i64) -> i64 |
| 122 | func _nx_chunk_end(begin: i64, end: i64, n_chunks: i64, idx: i64) -> i64 |
| 132 | func nx_parallel_for(pool: *NxThreadPool, begin: i64, end: i64, called by 3: mainmainmain calls 5: sys_mmapnx_atom_load_i64_nx_chunk_start_nx_chunk_endnx_pool_submit |
| 168 | func nx_parallel_map_i64(pool: *NxThreadPool, in_ptr: *i64, out_ptr: *i64, |
| 206 | func nx_parallel_reduce_i64(pool: *NxThreadPool, arr_ptr: *i64, n: i64, called by 4: mainmainmainmain calls 5: sys_mmapnx_atom_load_i64_nx_chunk_start_nx_chunk_endnx_pool_submit |
| 258 | func _nx_reduce_worker_simd_sum(ctx: i64) -> i64 calls 1: nx_atom_store_i64 |
| 288 | func nx_parallel_reduce_sum_simd_i64(pool: *NxThreadPool, arr_ptr: *i64, called by 1: main calls 6: sys_mmapnx_atom_load_i64_nx_chunk_start_nx_chunk_endnx_pool_submitnx_thread_yield |
| 337 | func _pf_self_test_noop(i: i64) -> i64 { return i + 1 } |
| 338 | func _map_self_test_double(x: i64) -> i64 { return x * 2 } |
| 339 | func _reduce_self_test_add(a: i64, b: i64) -> i64 { return a + b } |
| 341 | func main() -> i64 |