Repository navigation
perf(buffer): portable fearless_simd kernels for bitmap rank-select - #10319
Conversation
Signed-off-by: Joe Isaacs <joe.isaacs@live.co.uk>
Merging this PR will regress 1 benchmark
|
| Mode | Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|---|
| ❌ | Simulation | bitwise_not_vortex_buffer[128] |
4 µs | 5.4 µs | -25.69% |
| ⚡ | Simulation | fsst_decompress_string |
526 µs | 188.4 µs | ×2.8 |
| ⚡ | Simulation | chunked_canonicalize_into[(20, 100, 16, 4)] |
739.9 µs | 340.6 µs | ×2.2 |
| ⚡ | Simulation | chunked_dict_fsst_into_canonical[(250, 10, 2)] |
681.9 µs | 341.1 µs | +99.91% |
| ⚡ | Simulation | chunked_canonicalize_into[(20, 50, 8, 4)] |
274.4 µs | 213.1 µs | +28.78% |
| ⚡ | Simulation | bitwise_not_vortex_buffer[2048] |
5.8 µs | 4.5 µs | +28.59% |
| ⚡ | Simulation | chunked_canonicalize_into[(20, 50, 8, 16)] |
279.5 µs | 218.3 µs | +28.05% |
| ⚡ | Simulation | chunked_canonicalize_into[(20, 50, 8, 64)] |
306.3 µs | 242.8 µs | +26.16% |
| ⚡ | Simulation | chunked_into_canonical[(20, 50, 8, 4)] |
314.3 µs | 252.2 µs | +24.59% |
| ⚡ | Simulation | chunked_into_canonical[(20, 50, 8, 16)] |
323.3 µs | 261.5 µs | +23.63% |
| ⚡ | Simulation | chunked_into_canonical[(20, 50, 8, 64)] |
348.2 µs | 285.7 µs | +21.87% |
| ⚡ | Simulation | decompress_fsst[(200, 4, 4)] |
60.8 µs | 51 µs | +19.21% |
| ⚡ | Simulation | decompress_fsst[(200, 4, 8)] |
61.6 µs | 51.9 µs | +18.76% |
| ⚡ | Simulation | chunked_canonicalize_into[(10, 150, 4, 4)] |
235.3 µs | 199.5 µs | +17.94% |
| ⚡ | Simulation | decompress_fsst[(200, 16, 4)] |
66.4 µs | 56.6 µs | +17.3% |
| ⚡ | Simulation | decompress_fsst[(200, 16, 8)] |
69.4 µs | 59.7 µs | +16.21% |
| ⚡ | Simulation | chunked_canonicalize_into[(10, 150, 16, 4)] |
266.6 µs | 230.7 µs | +15.57% |
| ⚡ | Simulation | chunked_canonicalize_into[(20, 100, 16, 16)] |
461.3 µs | 399.7 µs | +15.43% |
| ⚡ | Simulation | chunked_into_canonical[(10, 150, 4, 4)] |
264.9 µs | 230.4 µs | +15% |
| ⚡ | Simulation | chunked_into_canonical[(20, 100, 16, 4)] |
452.7 µs | 397.6 µs | +13.85% |
| ... | ... | ... | ... | ... | ... |
ℹ️ Only the first 20 benchmarks are displayed. Go to the app to view all benchmarks.
Tip
Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.
Comparing ji/fearless-simd-wins (07f1e33) with develop (3c87da0)2
Footnotes
-
518 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩
-
No successful run was found on
develop(d14fa26) during the generation of this report, so 3c87da0 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report. ↩
Signed-off-by: Claude <noreply@anthropic.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YUbVgtk4iks9jBGhqe5pTF
Signed-off-by: Claude <noreply@anthropic.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YUbVgtk4iks9jBGhqe5pTF
Replace the per-architecture chunk scan and in-chunk select (NEON, AVX-512 VPOPCNTDQ/VBMI2 intrinsics and the scalar fallback) with fearless_simd kernels used on every target, removing their cfg gating and CpuKernel dispatch. The BMI2 in-word select stays. Signed-off-by: Claude <noreply@anthropic.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YUbVgtk4iks9jBGhqe5pTF
Per-16-byte byte-sum reductions took 2.1-2.5x longer than develop on AVX2. Popcounting u64 lanes matches develop there and lowers to vpopcntq on AVX-512, like the kernel it replaces. Signed-off-by: Claude <noreply@anthropic.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YUbVgtk4iks9jBGhqe5pTF
Summary
Part of the fearless_simd migration, following #10327. Bitmap rank-select's 64-byte chunk scan and in-chunk select had separate kernels per architecture: NEON
vcntintrinsics, AVX-512vpopcntqintrinsics and a scalar fallback, all behindcfg(target_arch)andCpuKerneldispatch. This replaces them with one portable#[simd]kernel each, used on every target, including Miri.Changes
vortex-buffer/src/bit/select.rs:scan_chunkspopcounts each 64-byte chunk asu64x8lanes.select_in_chunkuses the same lane popcounts to find the word.Level::new().scan_chunks_neon,scan_chunks_avx512_vpopcntandscan_chunks_scalar;select_in_chunk_vbmi2andselect_in_chunk_scalar;CpuKernels and everycfg(target_arch).pdep/tzcntin-word select. It is a scalar bit-deposit instruction with no fearless_simd equivalent, so its one x86cfgremains.select_last_vortex_bufferbenchmark, which selects the last set bit in 512-, 65,536- and 1,048,576-bit buffers.Why
u64x8lanesThe earlier revision summed bytes per 16-byte vector. That was faster on ARM64, but on AVX2 it took 2.1–2.5× as long as
develop. Lane popcounts avoid that:develop(measured below).vpopcntq, like the removed native kernel.vcntand pairwise-add sequence as the removed NEON kernel. Expect parity withdeveloprather than the earlier 0.60–0.86×.Benchmarks
select_last_vortex_bufferon an AVX2-only x86 VM (no AVX-512, sodevelopuses the scalar chunk scan). Each figure is the fastest of 4 alternating runs × 200 samples, pinned to one core:The byte-sum revision measured 2.14× and 2.36× at 65,536 and 1,048,576 bits, which is why it was replaced. The AVX-512 and NEON results come from the CodSpeed runners on this PR.
Testing
cargo nextest run -p vortex-buffer --release: 891 passed (x86_64).cargo nextest run -p vortex-buffer --target aarch64-unknown-linux-gnu --releaseunder qemu-aarch64: 889 passed.cargo miri nextest run -p vortex-bufferwith CI'sMIRIFLAGS: 221 passed. All 23 select tests also passed under Miri with--run-ignored all.cargo clippy -p vortex-buffer -- -D warnings: clean on x86_64 (--all-targets --all-features), aarch64 and wasm32.🤖 Generated with Claude Code
https://claude.ai/code/session_01YUbVgtk4iks9jBGhqe5pTF