Repository navigation
Conversation
Refs NVIDIA#1594. Co-authored-by: imanolgo <858793+ImanolGo@users.noreply.github.com> Signed-off-by: Canberk Pitirli <canberkpitirli@gmail.com>
ImanolGo
left a comment
There was a problem hiding this comment.
Thanks for this. I re-profiled the kernel from #1594 on sm_75 and read through the change.
Setup. GTX 1650 Max-Q (sm_75), driver 615.78.08, CUDA 13.4, LLVM NVPTX backend. The same flock kernel (murmuration-kernels, oxide/) built against this PR's base (a4a939a) and head (a48c53e), each with its own cargo-oxide and backend cache.
Code generation for the neighbour-search kernel decide (ptxas -arch=sm_75 -O3, cuobjdump):
decide |
base | this PR |
|---|---|---|
PTX max.f32 + min.f32 |
0 | 18 |
PTX setp.*.f32 / selp.f32 |
106 / 42 | 73 / 6 |
| SASS instructions | 1,440 | 1,376 |
SASS FMNMX / FSETP / FSEL |
0 / 116 / 28 | 18 / 83 / 4 |
| registers | 64 | 64 |
For reference, the CUDA C++ version of this kernel has 20 FMNMX, so this closes nearly all of that gap.
Correctness. Both builds pass the repo's parity check against its CPU reference (2,000 birds, pointwise at 1, 10, 30 and 100 steps, statistics at 1,000).
Performance. decide, mean GPU time over 20 steps after 20 warm-up, three runs per build interleaved, median:
| birds | base | this PR | change |
|---|---|---|---|
| 100,000 | 8.187 ms | 8.133 ms | -0.7 % (each PR run faster than each base run) |
| 1,000,000 | 100.92 ms | 100.77 ms | -0.1 % |
Nsight Compute, one decide launch at 100,000 birds: instructions executed 255.8 M to 253.7 M (-0.8 %), FP32 thread instructions -2.4 %, achieved occupancy unchanged (70 %).
So the fix does what #1594 asked: the compare/select pattern is gone and the SASS uses FMNMX. In #1594 I couldn't isolate what the min/max lowering cost on its own; on this kernel it turns out to be under 1 % of time and instructions, so most of the gap to CUDA C++ there is elsewhere (bounds checks were about 9 % of instructions). I couldn't run sm_89, because I don't have an Ada GPU at hand right now.
Code. The dispatch only reaches llvm_nvvm_fmax_f / llvm_nvvm_fmin_f under IntrinsicBackend::LlvmNvptx; libNVVM goes straight to libdevice_name and keeps the expansion, and the two new lowering tests pin both cases. The example's NaN checks (quiet and signaling, both signs, both operand positions, bit-exact) cover the contract that ruled out llvm.maxnum in #390. Leaving signed zeros unconstrained matches Rust. Looks good to me (I'm not a maintainer).
AI contribution: measured and drafted with Claude Code (Anthropic); reviewed and posted by me.
Summary
f32
max/minnow lower to nativemax.f32/min.f32instead ofsetp+selp. f64 and the libNVVM backend are unchanged. Refs #1594.Changes
max/mincallllvm.nvvm.fmax.f/llvm.nvvm.fmin.fon the LLVM NVPTX backend.float_minmaxexample.Testing
RTX 4050 (sm_89), CUDA 13.4:
float_minmax: all 256 pairs of NaN (quiet and signaling), infinity, signed zero and subnormal values match Rust's contract.max.f32/min.f32, no.ftz, no f32setp/selp. SASS: 8FMNMX.max(-0.0, +0.0)andmax(+0.0, -0.0)both give+0.0.mingives-0.0in both orders. The old lowering returned the second operand..ftzform is emitted.mir-loweranddialect-mirtests, clippy andcargo oxide fmt --checkpass.cargo-deny,just check-intrinsics, the fulljust check.Checklist