v3: Speed, Safety, Zig, and new C APIs
ForkUnion v3 is a full redesign of the core C++ project, with rippling effects on the existing Rust and C bindings, as well as the new Zig interface. The project is now reorganized, uses token-based completion tracking — always-odd generation handles — across multiple pools, smarter NUMA-aware work-stealing from per-worker fetch_add cursors, more hardware-friendly assembly primitives for busy-waiting and cache-line promotions and demotions, and explicit "topology" and "fabric" interfaces to drive the design of your higher-level systems.
Highlights: ForkUnion is often ~3–6× faster than OpenMP and ~12–16× faster than Rayon and Taskflow. Full tables ↓
What's the same: still avoids heap allocations, syscalls, and CAS on the hot path. Still has no shared_ptr<future<T>> or std::function boxes. Still has no work-stealing deque. On the steal path this matters. A worker whose own slice runs dry drains its neighbors' private cursors in coprime order, so stealers fan out across cursors instead of dog-piling one — overshooting a slice by at most one task, and never issuing a CAS. Rayon, Taskflow, and oneTBB steal halves through a Chase-Lev CAS deque instead, and OpenMP's shared queue serializes under the same pressure. The README walks through the full claim-and-steal protocol.
What's new:
- "Topology" interface to query the placement of compute and memory domains — their levels, per-core capacity, and private cache sizes.
- "Fabric" interface to query the distance, bandwidth, and latency between any two domains — measured manually.
- Supporting more features on Windows with MSVC intrinsics and WinAPI integrations — processor groups,
VirtualAllocExNuma, large pages, and_umonitor/_umwait. - Supporting new operating systems: FreeBSD with
domainsetNUMA placement, iOS, and Android. - Productively leveraging hardware wake-ups, timed waits, and cache-line hints on x86, Arm, and RISC-V —
UMONITOR/UMWAITandTPAUSE,WFET,WRS.STO/WRS.NTO, andCLDEMOTE/DC CVAC. Yes, prefetching mostly hurts, but here it doesn't. - New Zig binding, with explicit
init/deinitlifetimes — for the love of explicit memory management! Thanks to @steeve and @zml for convincing me to finally do this! - Improved safety guarantees and test suite hardening with help from @guillaumeriousat and the broader community 🤗
Hard things are still hard, but in my not so humble opinion, ForkUnion v3 is now one of the very few systems projects with advanced hardware awareness, and it should make designing hardware-friendly software a bit easier.
The numbers
Two benchmarks, the same runtimes on deliberately equal footing — identical -O3 and target-cpu=native, no LTO, one warmup, identical kernels. N-body isolates the dispatch path: every task costs the same, so only scheduling is left. Connected components by label propagation isolates the fork-join frequency, re-paying the dispatch-and-join tax across 84 bandwidth-bound rounds per pass. Reported static / dynamic, one fixed window per cell; ➕ marks C++, 🦀 marks Rust.
N-Body — dispatch latency, µs, lower is better
| Machine | ➕ ForkUnion | 🦀 ForkUnion | ➕ OpenMP | 🦀 Rayon | ➕ Taskflow |
|---|---|---|---|---|---|
| 18× Apple M5 Pro, macOS | 24 / 26 | 28 / 32 | 115 / 137 | 222 / 339 | 83 / 94 |
| 24× Intel RPL, Windows | 148 / 143 | 112 / 91 | 360 / 627 | 151 / 151 | 812 / 1026 |
| 128× Intel SPR, Linux | 54 / 86 | 40 / 67 | 115 / 226 | 483 / 739 | 666 / 694 |
| 192× AWS Graviton 5, Linux | 47 / 136 | 42 / 137 | 266 / 216 | 490 / 580 | 602 / 639 |
Connected Components — fork-join frequency, GTEPS, higher is better
| Machine | ➕ ForkUnion | 🦀 ForkUnion | ➕ OpenMP | 🦀 Rayon | ➕ Taskflow |
|---|---|---|---|---|---|
| 18× Apple M5 Pro, macOS | 24.7 / 20.5 | 19.4 / 20.2 | 23.0 / 18.3 | 23.8 / 14.6 | 21.4 / 1.3 |
| 24× Intel RPL, Windows | 10.0 / 7.2 | 5.2 / 5.5 | 11.4 / 0.1 | 9.4 / 5.2 | 6.6 / 0.7 |
| 128× Intel SPR, Linux | 46.5 / 20.9 | 65.2 / 31.4 | 28.2 / 0.4 | 8.5 / 6.5 | 25.1 / 0.5 |
| 192× AWS Graviton 5, Linux | 192.6 / 77.6 | 258.7 / 87.3 | 53.9 / 0.4 | 8.4 / 6.2 | 53.0 / 0.4 |
Both ForkUnion columns are the same pool over a bit-identical graph; the gap between them is only how GCC and LLVM vectorize the identical kernel. The others fall off in the dynamic column because their shared queues contend at scale — ForkUnion claims one vertex at a time from private fetch_add cursors and has no queue to contend on.
Major
- Break: Measure the memory fabric in-process, replacing HMAT and kernel tiering (49290e7)
- Break: DomainAllocator, symmetric containers, SDK benchmark alignment (06bf8fe)
- Break: Explicit
fu_topology_thandle across the C ABI, no singletons (6903711) - Break: One vocabulary, one canonical order, across every surface (fa78301)
- Break: Remove every back-compatibility shim (818151a)
- Break: Layer capability macros, split the umbrella header (ec013e8)
- Break: Sound, allocation-free
RoundRobinVecbulk operations (384bef5) - Break: Split topology into compute and memory domains (64d4bdf)
- Break: Generation tokens for non-blocking multi-pool coordination (1545311)
- Break: New project name & link (6da19ed)
- Break: Name spacing/capitalization (0911619)
Minor
- Add: Bind the measured fabric to Rust and Zig (35e439b)
- Add: Enumerate per-domain cache sizes from sysfs and CPUID (c322c93)
- Add: FreeBSD reschedule, topology, memory placement, and superpages (6a84509)
- Add: Label-propagation benchmark replacing triangle counting (2604198)
- Add: Cache-line demote & promote hints for x86, Arm, and RISC-V (4145073)
- Add: Monitored waits, per-loop wait bounds, and Zawrs (d7774e5)
- Add: Owning arrays, and rebuild the topology on them (586e138)
- Add: Windows support for topology, NUMA pools, and pinning (2ae8150)
- Add: Miri coverage of the pure-Rust unsafe (6c7f62d)
- Add: Rust scope() API replacing the SafePtr smuggle (b71341f)
- Add: Memory tiers, cpuless domains, HMAT metrics, and macOS harvest (c27faf8)
- Add: Stoppable & Exception-handling
try_for(cb6b257) - Add: Reductions in Rust (e7932c4)
- Add: NUMA-aware allocator for Zig (bbb9e90)
- Add: Bench against libXEV and Spice (bf998f9)
- Add: Draft Zig binding (916e641)
Patch
- Make: Version the duplicated macros in types.hpp (6fc9312)
- Docs: Rewrite the topology guide around the declared-versus-observed split (a6e9843)
- Improve: Hoist the SplitMix64 avalanche into the shared types (3465c2d)
- Docs: Refresh the intro and complete the v3 API coverage (45e4753)
- Fix: Sync build-system references to place-* feature names (4e87654)
- Fix: Build the timed-wait monitors under -march=native (0891489)
- Make: Green the CI matrix - apt sources, tar, shellcheck, cross rows (1be2492)
- Docs: Refresh the README against the v3 APIs and slim the benchmarks (e3396ec)
- Make: Rewrite the pre-commit hook single-pass and Windows-proof (a52dfef)
- Fix: Probe FreeBSD's memory placement at runtime, not just compile time (c2eb3a7)
- Make: Restructure the pre-release CI around matrices and ctest (e3d7ff2)
- Improve: Flatten test bodies into helpers and split bundled concerns (feeaddd)
- Improve: Normalize the N-body kernels across languages (02bb703)
- Improve: Deepen test coverage across schedulers, lifecycles, and the C ABI (6b77064)
- Improve: Time-budgeted benchmarks, symmetric across C++, Rust, and Zig (45eb844)
- Make: Install git in the FreeBSD CI VM for Taskflow fetch (198b04f)
- Make: Align benchmark builds and add Taskflow as a baseline (ef68bca)
- Fix: Hoist dead work and stale docs out of the pool internals (11aa660)
- Improve: Flatten the distributed pool's colocations into one array (50d39e2)
- Improve: Dispatch curated yield-and-hints pool variants at runtime (134b7f6)
- Improve: Private cursors, read-probes, and demotions in dynamic stealing (c525460)
- Improve: Pools carry a cache-hints policy beside the yield (ea5399a)
- Make: Bump Zig to 0.16 for macOS 26 (4ce5b40)
- Make: Refresh retired CI runners, fix FreeBSD linkage (e351fd1)
- Improve: Steer Apple efficiency-core pools via QoS (c7be3fb)
- Fix: Route platform checks through the FU_* vocabulary (f30ce60)
- Fix: Robust NUMA placement under sandboxed and single-node hosts (239e3b0)
- Improve: Locate and value-print test failures, backtrace on crash (c427d6c)
- Make: Static-link the qemu-run cross-compile test binaries (ef3d11c)
- Fix: Cross-platform CI test failures (315da41)
- Make: Build the old-glibc floor on manylinux_2_28 (5f26aba)
- Improve: Model Android and FreeBSD affinity and scheduling (b903e50)
- Improve: Flatten the libc and hwprobe capability detection (7a4fd4e)
- Fix: Assemble the RISC-V pause hint on stock toolchains (1da97c7)
- Improve: Reach the CPU yield intrinsics on MSVC (a960fa3)
- Fix: Harden pool-completion tests against scheduler starvation (e85d35f)
- Make: Expand CI to validate cross-platform compatibility (d171746)
- Fix: Build clean under -Wall -Wextra -Werror (3833a67)
- Fix: Assemble the WFET capability probe on stock toolchains (05ed868)
- Improve: Full backend matrix via one compile-time-policy dispatch per demo (12bab43)
- Improve: Unified capability enumeration,
pool_kind_t, leaner ABI (90546ed) - Improve: Cleaner feature controls, one set across the SDKs (c2e9669)
- Fix: Restore Windows NUMA allocation and page-size probe (6d29828)
- Fix: Size and pin pools from the cores we were given (33497ab)
- Make: Expose each capability as an AUTO/ON/OFF tri-state (0020411)
- Fix: Seed where a stealing thread starts probing, not just its stride (c5f30cd)
- Improve: Private claim cursors for
for_n_dynamic(5ea9298) - Improve: NUMA backends for the Rust N-body, and dual-socket results (8350177)
- Improve: Compute-domain metadata across the bindings (95df7bc)
- Fix: NUMA topology harvesting on multi-socket hosts (3ebeea5)
- Docs: Reword stale topology phrasing to compute/memory domains (0272b39)
- Make: Rename allocator numa_node to memory_domain (d8e170f)
- Docs: Reword prong metadata for two-axis topology (f12d0ef)
- Make: Bump CI runners and add Arm64 + sanitizer coverage (d230ab1)
- Fix: Rust examples for by-reference
for_threads(39b007c) - Make: Bump CI (9a08a62)
- Fix: Over-aligned
AllocationResult(3bf34e9) - Improve: Validate
CacheAlignedpadding (46bb382) - Improve:
RoundRobinVeccode nesting (9982ad9) - Fix: Deprecate
spicecomparison (a707d55) - Make: Backport to Zig 0.15 (0167211)
- Improve: Shared
call_yield_with & w/out thread (541842c) - Improve: Select worker-yield overloads with
if constexpr(53e1b5d) - Fix: Route
standard_worker_yieldthroughmicro_yield()(101a955) - Improve: Add
worker_yield_thook for idle backoff (18b6d63) - Make: Apple's
libSystemcontains Blocks runtime (1e9540f) - Make: Skip C11 tests on MSVC - lacking atomics (a21ac35)
- Improve: Test C stable API (e8c6b48)
- Improve: Static slicing for
std.Thread.Pool(ca3d42b) - Make: Upgrade Zig to v0.16 (6d6283a)
- Improve: Handle
voidcontexts (0b9ed05) - Fix: Use aligned allocs for
opaque_pool_t(fb063a7) - Improve: Thread name formatting (d9455c8)
- Docs: Why no Miri or Loom? (0ec9ae9)
- Fix: Safeguard
tryforstd::thread(ddf8ea0)
