Skip to content

Make device min/max propagate NaNs and order signed zeros - #1148

Merged
maleadt merged 2 commits into
tb/usub-sat-metadatafrom
tb/minmax-nan
Oct 7, 2026
Merged

maleadt merged 2 commits into
tb/usub-sat-metadatafrom
tb/minmax-nan

Conversation

@maleadt

@maleadt maleadt commented Oct 7, 2026

Copy link
Copy Markdown
Member

Stacked on #1147 (merge that first).

On the device, Base.min/Base.max for Float16/32/64 were overridden with ocml's min/max, which ignore NaN operands and leave the result for −0/+0 unspecified. Julia's min/max propagate NaNs and treat −0 as smaller than +0, so e.g. max(NaN32, 1f0) returned 1f0 in a kernel. This also affected @atomic max/min on floats, which on Julia ≤ 1.13 is a compare-exchange loop around Base.max/min, while Julia 1.14 lowers it to atomicrmw fmaximum, so results depended on the Julia version.

With Julia's LLVM 19 or later (Julia 1.13+), the overrides now emit llvm.minimum/llvm.maximum, like Base; LLVM expands these for every AMDGPU target and has native instructions on gfx12. Older LLVM versions can't lower those intrinsics in general (hence the existing Julia 1.12 FastMath workaround), so there the overrides use a comparison-based implementation. The choice is made from Julia's LLVM version, so this also applies with the external back-end. The fallback costs a few extra instructions: on gfx1036 with Julia 1.12, a Float32 max becomes four compares and a select, versus v_max_f32 + v_cmp_o_f32 + select with llvm.maximum on Julia 1.13.

Tests compare device results against the host for NaN in either or both operands, ±0 in both orders, infinities and subnormals (Float16/32/64), check atomic min/max with NaNs, and check offline codegen for gfx90a/gfx942/gfx1200 with both back-ends. Tested locally on gfx1036 with Julia 1.12 and 1.13 (external back-end), and before rebasing onto #1147 also with Julia 1.14-DEV and the in-process back-end; not on Julia 1.10/1.11.

Fixes #1142

Move compile_offline out of the atomic metadata testset so that other
codegen tests can compile for GPUs other than the local one too.
Base.min/max on Float16/32/64 were overridden with ocml's min/max, which
return the other operand when one is NaN and, like llvm.minnum/maxnum,
leave the result for -0 and +0 unspecified. Julia's min/max propagate NaNs
and treat -0 as smaller than +0, so kernels computed different results
than the host, which also leaked into Atomix's CAS-loop atomic min/max.

With LLVM 19+ (Julia 1.13+), use the same llvm.minimum/maximum intrinsics
as Base, which LLVM expands for every AMDGPU target. Older LLVM versions
can't lower them in general (the reason Julia 1.12 needs the FastMath
workaround), so use an explicit comparison-based implementation there.

Fixes #1142
@github-actions

github-actions Bot commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

AMDGPU.jl Benchmarks

Details
Benchmark suite Current: 1b16a35 Previous: 6f37fdf Ratio
amdgpu/synchronization/context/device 565 ns 547.5 ns 1.03
amdgpu/synchronization/stream/blocking 232.5 ns 222.5 ns 1.04
amdgpu/synchronization/stream/nonblocking 322.5 ns 305 ns 1.06
applications/bitonic_sort 1116487.75 ns 1121018 ns 1.00
applications/convolution 91206 ns 91856.25 ns 0.99
applications/floyd_warshall 9094040.5 ns 9088943 ns 1.00
applications/histogram 803021.5 ns 795198.5 ns 1.01
applications/prefix_sum 188049.75 ns 188172.75 ns 1.00
array/accumulate/Float32/1d 80405.75 ns 82703.5 ns 0.97
array/accumulate/Float32/dims=1 263577.5 ns 259653.5 ns 1.02
array/accumulate/Float32/dims=1L 105038.5 ns 91916.25 ns 1.14
array/accumulate/Float32/dims=2 104743.5 ns 105999 ns 0.99
array/accumulate/Float32/dims=2L 3029516 ns 3029801.75 ns 1.00
array/accumulate/Int64/1d 86945.75 ns 93226.5 ns 0.93
array/accumulate/Int64/dims=1 244262.25 ns 242948.25 ns 1.01
array/accumulate/Int64/dims=1L 111588.5 ns 112674.25 ns 0.99
array/accumulate/Int64/dims=2 106546 ns 80158.75 ns 1.33
array/accumulate/Int64/dims=2L 3391547.75 ns 3392649.25 ns 1.00
array/broadcast 29655.25 ns 29063 ns 1.02
array/construct 2170 ns 2220 ns 0.98
array/copy 32037.75 ns 38870.5 ns 0.82
array/copyto!/cpu_to_gpu 88106 ns 88961.25 ns 0.99
array/copyto!/gpu_to_cpu 89626 ns 89613.75 ns 1.00
array/copyto!/gpu_to_gpu 32960.25 ns 34700.5 ns 0.95
array/iteration/findall/bool 109521.25 ns 119901.75 ns 0.91
array/iteration/findall/int 113091.25 ns 112736.5 ns 1.00
array/iteration/findfirst/bool 153931.75 ns 152529.75 ns 1.01
array/iteration/findfirst/int 187524.75 ns 153119.5 ns 1.22
array/iteration/findmin/1d 107588.75 ns 104196.25 ns 1.03
array/iteration/findmin/2d 91266 ns 93761.25 ns 0.97
array/iteration/logical 182804.5 ns 183702.5 ns 1.00
array/iteration/scalar 310778.25 ns 309796.75 ns 1.00
array/permutedims/2d 59368 ns 59476 ns 1.00
array/permutedims/3d 57163 ns 57221 ns 1.00
array/permutedims/4d 65668 ns 43595.5 ns 1.51
array/random/rand/Float32 45462.75 ns 45258 ns 1.00
array/random/rand/Int64 43375.5 ns 37798 ns 1.15
array/random/rand!/Float32 40678 ns 41948 ns 0.97
array/random/rand!/Int64 38190.5 ns 37215.5 ns 1.03
array/random/randn/Float32 57073.25 ns 72228.25 ns 0.79
array/random/randn!/Float32 54168.25 ns 54075.75 ns 1.00
array/reductions/mapreduce/Float32/1d 108956.25 ns 108671.5 ns 1.00
array/reductions/mapreduce/Float32/dims=1 88181 ns 88146.25 ns 1.00
array/reductions/mapreduce/Float32/dims=1L 835141.25 ns 836306.5 ns 1.00
array/reductions/mapreduce/Float32/dims=2 93868.5 ns 94228.75 ns 1.00
array/reductions/mapreduce/Float32/dims=2L 135981.5 ns 135486.75 ns 1.00
array/reductions/mapreduce/Int64/1d 108563.5 ns 108869 ns 1.00
array/reductions/mapreduce/Int64/dims=1 88541 ns 88541.25 ns 1.00
array/reductions/mapreduce/Int64/dims=1L 839383.75 ns 841141.75 ns 1.00
array/reductions/mapreduce/Int64/dims=2 92748.5 ns 92663.75 ns 1.00
array/reductions/mapreduce/Int64/dims=2L 136191.5 ns 136469.5 ns 1.00
array/reductions/reduce/Float32/1d 108891 ns 108931.5 ns 1.00
array/reductions/reduce/Float32/dims=1 88318.5 ns 88088.5 ns 1.00
array/reductions/reduce/Float32/dims=1L 836026.5 ns 835606.5 ns 1.00
array/reductions/reduce/Float32/dims=2 94333.25 ns 94116.25 ns 1.00
array/reductions/reduce/Float32/dims=2L 135816.5 ns 135959.5 ns 1.00
array/reductions/reduce/Int64/1d 108416.25 ns 107759 ns 1.01
array/reductions/reduce/Int64/dims=1 88691 ns 87166.25 ns 1.02
array/reductions/reduce/Int64/dims=1L 841434 ns 837371.5 ns 1.00
array/reductions/reduce/Int64/dims=2 92703.5 ns 92491.25 ns 1.00
array/reductions/reduce/Int64/dims=2L 136036.5 ns 135859.25 ns 1.00
array/reverse/1d 43543 ns 35203 ns 1.24
array/reverse/1dL 68715.75 ns 69038.25 ns 1.00
array/reverse/1dL_inplace 54328 ns 54128.25 ns 1.00
array/reverse/1d_inplace 35277.75 ns 37770.5 ns 0.93
array/reverse/2d 38850.5 ns 46583 ns 0.83
array/reverse/2dL 91240.75 ns 91263.75 ns 1.00
array/reverse/2dL_inplace 64428 ns 65028.5 ns 0.99
array/reverse/2d_inplace 37227.75 ns 37158 ns 1.00
array/sorting/1d 329413 ns 330742 ns 1.00
gemm/tiled 2866764.5 ns 1949946.75 ns 1.47
gemm/tiled_unbounded 2904937.5 ns 2914637.75 ns 1.00
integration/byval/reference 39430 ns 39901 ns 0.99
integration/byval/slices=1 40751 ns 39770 ns 1.02
integration/byval/slices=2 158332 ns 157742 ns 1.00
integration/byval/slices=3 239693 ns 223213 ns 1.07
integration/volumerhs 4876286 ns 4883697 ns 1.00
kernel/indexing 30295.25 ns 28847.75 ns 1.05
kernel/indexing_checked 36608 ns 32488 ns 1.13
kernel/launch 1225 ns 1220 ns 1.00
kernel/rand 48615.5 ns 51218.25 ns 0.95
latency/import 1628483156 ns 1628061194 ns 1.00
latency/precompile 29033979810 ns 29168346331 ns 1.00
latency/ttfp 2177501902 ns 2181953516 ns 1.00
stencil/diffusion3d 1583072.75 ns 1583662 ns 1.00
stencil/diffusion3d_checked 1627073.25 ns 1637802.75 ns 0.99

This comment was automatically generated by workflow using github-action-benchmark.

@maleadt
maleadt merged commit 89b85df into tb/usub-sat-metadata Oct 7, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant