wgpu-primitives
Safe, composable GPU prefix scan and unsigned integer radix sort for Rust applications using wgpu.
wgpu-primitives continues the package previously published as wgpu-algorithms beginning with version 0.2.
Benchmark highlights
With data already resident on an NVIDIA RTX 4070 Ti SUPER, the GPU-buffer APIs delivered the following results at 100 million items (u32 values or KeyValue pairs):
| Primitive | Best backend | GPU time | Resident throughput | CPU baseline | Speedup |
|---|---|---|---|---|---|
| Inclusive prefix scan | DX12 | 5.568 ms | 17.96 billion elements/s | Scalar CPU | 5.02x |
| Exclusive prefix scan | DX12 | 6.238 ms | 16.03 billion elements/s | Scalar CPU | 4.58x |
| Radix sort | Vulkan | 43.724 ms | 2.287 billion elements/s | Rayon | 6.35x |
| Stable key-value radix sort | Vulkan | 110.270 ms | 906.83 million pairs/s | Stable Rayon | 6.23x |
These figures measure the composable resident-buffer path: command encoding, submission, primitive execution, and reusable workspace management are included, while host upload and readback are excluded. See Performance for smaller inputs and round-trip results.
Features
- Inclusive and exclusive
u32prefix scan. - Stable 2-bit LSD radix sort for
u32values. - Stable 2-bit LSD radix sort for
(u32 key, u32 value)pairs. - Convenience slice APIs that upload, execute, and read back.
- GPU-buffer APIs that record into an existing command encoder.
- Reusable internal scratch storage.
- No
unsafeblocks in library code.
Usage
The convenience context is useful for standalone compute programs:
use ;
async
Stable key-value sorting keeps payloads in their original order when keys compare equal:
use ;
async
Applications that already own a wgpu device should reuse it:
let mut scanner = new;
let mut sorter = new;
scanner.record_scan?;
sorter.record_sort?;
queue.submit;
record_scan requires COPY_SRC on the input and COPY_DST | STORAGE on the output. record_sort requires STORAGE on both buffers.
Installation
Version 0.3 contains inclusive and exclusive scan, key-only radix sort, and stable key-value radix sort:
[]
= "0.3"
Algorithms
The scan recursively computes per-workgroup inclusive prefixes, scans the workgroup totals, and propagates those totals back through the hierarchy.
The radix sort processes two bits per pass. Each of its 16 passes builds four per-workgroup histograms, scans them into global offsets, and stably scatters values between ping-pong buffers.
KeyValueSorter runs the same stable passes over interleaved KeyValue items. Values move with their keys during every scatter, so equal keys retain their original value order.
Performance
Criterion measurements from an RTX 4070 Ti SUPER show why the GPU-buffer API is the primary interface. Resident execution keeps data on the GPU; round-trip execution includes upload, allocation, execution, and readback. GPU acceleration becomes valuable as the workload grows enough to amortize dispatch and transfer overhead.
| Primitive | Items | CPU | Best GPU resident | Resident speedup | Best GPU round trip |
|---|---|---|---|---|---|
| Prefix scan | 1M | 0.220 ms | 0.170 ms (Vulkan) | 1.29x | 1.351 ms (DX12) |
| Prefix scan | 10M | 2.232 ms | 0.717 ms (DX12) | 3.11x | 11.108 ms (DX12) |
| Prefix scan | 100M | 27.949 ms | 5.568 ms (DX12) | 5.02x | 230.390 ms (DX12) |
| Exclusive prefix scan | 100M | 28.580 ms | 6.238 ms (DX12) | 4.58x | Not measured |
| Radix sort | 1M | 2.458 ms | 1.331 ms (Vulkan) | 1.85x | 2.453 ms (Vulkan) |
| Radix sort | 10M | 25.224 ms | 5.511 ms (Vulkan) | 4.58x | 15.783 ms (Vulkan) |
| Radix sort | 100M | 277.730 ms | 43.724 ms (Vulkan) | 6.35x | 253.760 ms (Vulkan) |
| Stable key-value sort | 10M | 51.783 ms | 12.694 ms (Vulkan) | 4.08x | 34.142 ms (Vulkan) |
| Stable key-value sort | 100M | 687.590 ms | 110.270 ms (Vulkan) | 6.23x | 576.340 ms (Vulkan) |
At 100M items, resident throughput reached 17.96 billion elements/s for inclusive scan, 16.03 billion elements/s for exclusive scan, 2.287 billion elements/s for key-only sort, and 906.83 million pairs/s for stable key-value sort. See the full methodology, confidence intervals, backend comparison, and memory accounting.
Roadmap
Version 0.2 established the public GPU-buffer APIs, deterministic GPU tests, reusable workspace, cross-backend benchmarks, and the wgpu-primitives package name. Version 0.3 adds exclusive scan and stable key-value radix sort. The next work is ordered by how much it improves the crate as a reusable primitive library:
- Measure kernels directly: add GPU timestamp-query benchmarks and per-pass profiling so optimization decisions are separated from command submission and synchronization cost.
- Reduce runtime overhead: remove the radix sort's per-invocation uniform-buffer allocation and tune workgroup/radix configurations from measurements across DX12, Vulkan, and Metal.
- Build derived primitives: implement stream compaction and selection on top of scan after the lower-level APIs and performance contracts are stable.
- Broaden hardware evidence: publish reproducible benchmark reports from integrated and discrete GPUs, including crossover sizes, memory use, and resident versus round-trip behavior.
New primitives should land with a GPU-buffer API, deterministic boundary tests, CPU-reference validation, and benchmark coverage.
Development
GPU integration tests skip when no compatible adapter is available. CI installs Mesa's Vulkan software adapter so the shader paths execute on Linux.
License
MIT