Three C++ primitives — async-membrane, sample-filter,
radix-select — covering K from 100 to 1M.
23x over std::priority_queue at K=1M. Single-threaded, adversarially verified.
Five normalized axes covering the dimensions that distinguish streaming selection primitives. Each approach has a different sweet spot; there is no single winner across all K regimes.
Grouped bar chart showing measured throughput (Mops/s) for each primitive at five K values from 100 to 1M. Higher is better. All runs on Apple M4, single-threaded, uniform random input.
| Approach | K=100 | K=1K | K=10K | K=100K | K=1M |
|---|---|---|---|---|---|
| std::priority_queue | 1,635 | 1,545 | 632 | 145 | 17 |
| std::nth_element | 231 | 222 | 280 | 266 | 264 |
| async-membrane ★ | 2,084 | 2,083 | 1,025 | 269 | 15 |
| sample-filter ★ | 1,510 | 1,119 | 1,211 | 613 | 225 |
| radix-select ★ | 1,010 | 909 | 1,125 | 945 | 397 |
Log-log line chart showing throughput decay as K increases. Flat curves
indicate good scaling; steep drop-offs reveal regime boundaries.
std::nth_element is flat but slow; async-membrane
dominates small K but collapses at large K.
For each K, we take the best of our three primitives and divide by
std::priority_queue. The speedup grows dramatically at
large K where the heap degrades.
Each primitive owns a K regime. The fourth card documents the nine cross-field approaches that were searched and discarded during adversarial verification.
bench output
Run ./build/bench --mode sweep --n 5000000 and paste the
output below. The dashboard re-renders with your measured numbers
overlaid against the baseline. Everything runs client-side.
our-async-membrane ... X Mops/s,
our-sample-filter ... X Mops/s,
our-radix-select ... X Mops/s.