Skip to content

[libcu++] Add cuda::min, cuda::max and cuda::clamp - #12512

Open
miscco wants to merge 7 commits into
NVIDIA:mainfrom
miscco:optimize_clamp
Open

miscco wants to merge 7 commits into
NVIDIA:mainfrom
miscco:optimize_clamp

Conversation

@miscco

@miscco miscco commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

Description

closes #12510

cuda::std::min, cuda::std::max and cuda::std::clamp must take their arguments by reference and return a reference to one of them, so we cannot change them the way the first version of this PR did. Instead, this adds cuda::min, cuda::max and cuda::clamp to <cuda/cmath>, which take and return values. The compiler can then compute the result in registers instead of selecting the address of an argument and loading the result from it, so on device cuda::clamp maps to a single min and max instruction. The exception is double on GPUs with slow FP64 arithmetic, where cuda::std::fmin and cuda::std::fmax compare with integer instructions since #12231.

  • The functions accept arithmetic types and extended floating-point types, including the FP8, FP6 and FP4 types that cuda::std::fmin and cuda::std::fmax support since [libcu++] Make fmin/fmax work for all nvfp types #12612. All arguments must have the same type.
  • Floating-point values are compared like with cuda::std::fmin and cuda::std::fmax, so a NaN argument is ignored. cuda::clamp(v, lo, hi) is cuda::max(cuda::min(v, hi), lo): a NaN bound is ignored, and a NaN v is clamped to hi, or to lo if hi is NaN.
  • cuda::min and cuda::max call cuda::minimum and cuda::maximum, so there is a single implementation. Because they take their arguments by value, the references that cuda::minimum and cuda::maximum take are optimized away: the SASS for float, double, __half and __nv_bfloat16 is the same as when calling cuda::std::fmin and cuda::std::fmax directly.

Through cuda::minimum and cuda::maximum, the new functions use cuda::std::fmin and cuda::std::fmax, which this also improves:

  • The functions of <cuda_fp16.h> and <cuda_bf16.h> are called unqualified and found by ADL, so <cuda/std/__cmath/min_max.h> does not need to include those headers.
  • Before SM80, __nv_bfloat16 uses min.f32 and max.f32 instead of the emulation in __hmin and __hmax, which takes about 50 instructions with branches. The result is exact, which was verified for all 2^32 input pairs.
  • Before CTK 12.2, __hmin and __hmax are only available on device, so the host compares as float.

Tests and documentation:

  • New cuda/cmath/min_max.pass.cpp and cuda/cmath/clamp.pass.cpp, which run with the conversions and operators of the extended floating-point types disabled (__CUDA_NO_HALF_OPERATORS__ and friends).
  • A new cmath-ptx FileCheck suite in libcudacxx/test/cmath_codegen, which checks that cuda::min, cuda::max and cuda::clamp compile to min and max instructions without branches. For double, the instructions are only checked on SM80, SM90 and SM100, because the other targets in CI have slow FP64 arithmetic. It is wired into CI like simd-ptx.
  • New documentation pages for cuda::min and cuda::max, and for cuda::clamp.

Checklist

  • New or existing tests cover these changes.
  • The documentation is up to date with these changes.

@miscco
miscco requested a review from a team as a code owner November 6, 2026 18:44
@miscco
miscco requested a review from pciolkosz November 6, 2026 18:44
@github-project-automation github-project-automation Bot moved this to Todo in CCCL Nov 6, 2026
@cccl-authenticator-app cccl-authenticator-app Bot moved this from Todo to In Review in CCCL Nov 6, 2026
@coderabbitai

coderabbitai Bot commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

Explain Change →

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • Resume reviews
  • Trigger review

No actionable comments were generated in the recent review.

Summary

Summary by CodeRabbit

  • New Features
    • Added cuda::clamp, cuda::min, and cuda::max for supported arithmetic types, including extended floating-point types.
    • Floating-point min/max handle NaN inputs. cuda::clamp documents NaN behavior and validates its bounds.
    • Updated fmin and fmax behavior for NVIDIA extended floating-point formats, including FP8, FP6, and FP4.
  • Documentation
    • Added reference pages and examples for the math functions, with availability information for CCCL 3.7.0 and CUDA 13.7.

The PR adds public cuda::min, cuda::max, and cuda::clamp APIs. It updates extended floating-point fmin and fmax paths, adds functional and PTX codegen coverage, documents the APIs, and enables the cmath PTX suite in presets and CI matrices.

Changes: CUDA math APIs

Layer / File(s) Summary
Min/max and clamp APIs
libcudacxx/include/cuda/std/__cmath/min_max.h, libcudacxx/include/cuda/__cmath/min_max.h, libcudacxx/include/cuda/__cmath/clamp.h, libcudacxx/include/cuda/cmath, docs/libcudacxx/extended_api/math.rst, docs/libcudacxx/extended_api/math/*
The changes add cuda::min, cuda::max, and cuda::clamp, export their headers, and document their behavior and availability. The extended floating-point fmin and fmax paths add storage-order comparisons and target-specific handling for half and bfloat16.
API and floating-point tests
libcudacxx/test/libcudacxx/cuda/cmath/*, libcudacxx/test/libcudacxx/std/numerics/c.math/fp_min_max.pass.cpp
Tests check API constraints, result types, noexcept, and results across built-in and supported extended types. Narrow-format tests compare fmin and fmax results by storage representation.
PTX codegen coverage and CI wiring
libcudacxx/test/cmath_codegen/*, libcudacxx/test/CMakeLists.txt, CMakePresets.json, ci/matrix.yaml
The new PTX tests check instruction patterns for clamp and min/max. CMake registers the cmath codegen suite, and presets and CI matrices include its target.

Assessment against linked issues

Objective Addressed Explanation
Provide a clamp API that computes the result with min/max operations [12510] ✅

Out-of-scope changes

Code Change Explanation
Change extended floating-point fmin and fmax handling (libcudacxx/include/cuda/std/__cmath/min_max.h:70-129,183-226,342-386) The issue asks for a clamp implementation or callable clamp alternative. It does not state requirements for changing fmin and fmax behavior or implementation.
Add storage-based narrow-format fmin and fmax tests (libcudacxx/test/libcudacxx/std/numerics/c.math/fp_min_max.pass.cpp:202-380) These tests cover fmin and fmax, not the linked clamp objective.

Suggested reviewers: davebayer, fbusato, jacobfaib

Change: Feature

Merge Risk: Minimal · previous review 4ee52

The previously identified clamp concerns do not remain actionable at this head. No merge-blocking risk is established by the available evidence.

Security architecture risk: Low · up to 4ee52

The inspected changes add value-based arithmetic APIs without introducing a privileged operation or an external service entrypoint. No concrete security issue was identified, but downstream usage and cross-platform behavior are not fully covered.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The demonstrated exposure is arithmetic behavior within callers that compile these headers. No tenant, datastore, privileged service, or attacker-reachable downstream sink was established by the supplied relationships or inspected API bodies; maximum application-level exposure remains unresolved because downstream coverage is capped.

Trust Boundaries and Controls

  • observed — The specifically routed clamp symbols belong to test detection traits, value helpers, assertions, and a test main that uses a locally initialized value rather than command-line input. These ranges do not demonstrate a newly exposed service or an authorization bypass.
- [ ] Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Recent review info

Run configuration

  • Source mode: Auto
  • Repository: NVIDIA/cccl
  • Branch: main
  • File: .coderabbit.yaml
  • Commit: 89e1909
  • Layers: Repository YAML
  • Review profile: CHILL
  • Plan: Enterprise
  • Run ID: 726b4751-095f-4e7a-9163-c721a04c3908
Commits

Reviewing files that changed from the base of the PR and between 36fae63 and 4ee5289.

Files selected for processing: 3

  • docs/libcudacxx/extended_api/math/clamp.rst
  • docs/libcudacxx/extended_api/math/min_max.rst
  • libcudacxx/test/libcudacxx/cuda/cmath/clamp.pass.cpp

Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 11 remain after this review.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Review info

Run configuration

  • Source mode: Auto
  • Repository: NVIDIA/cccl
  • Branch: main
  • File: .coderabbit.yaml
  • Commit: 758ca4d
  • Layers: Repository YAML
  • Review profile: CHILL
  • Plan: Enterprise
  • Run ID: abf99e55-9259-470f-a784-dd9358f22856
Commits

Reviewing files that changed from the base of the PR and between 758ca4d and 98d8036.

Files selected for processing: 2

  • libcudacxx/include/cuda/std/__algorithm/clamp.h
  • libcudacxx/test/libcudacxx/std/algorithms/alg.sorting/alg.clamp/clamp.pass.cpp

Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 11 remain after this review.

@davebayer davebayer left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't believe this is correct

@github-project-automation github-project-automation Bot moved this from In Review to In Progress in CCCL Nov 6, 2026
@davebayer

Copy link
Copy Markdown
Contributor

@miscco thought we could do this because of:

While the signature does not need to have const&, the function must not modify the objects passed to it and must be able to accept all values of type (possibly const) Type1 and Type2 regardless of value category (thus, Type1& is not allowed, nor is Type1 unless for Type1 a move is equivalent to a copy(since C++11)).
The types Type1 and Type2 must be such that an object of type T can be implicitly converted to both of them.

Unfortunately, this is related to the Comp functor, not the function signature.

@davebayer davebayer closed this Nov 6, 2026
@github-project-automation github-project-automation Bot moved this from In Progress to Done in CCCL Nov 6, 2026
@miscco miscco reopened this Nov 9, 2026
@github-project-automation github-project-automation Bot moved this from Done to Needs Triage in CCCL Nov 9, 2026
@miscco
miscco requested review from a team as code owners November 9, 2026 12:32
@miscco miscco changed the title [libcu++] Specialize clamp for arithmetic types [libcu++] Add cuda::min, cuda::max and cuda::clamp Nov 9, 2026
@miscco
miscco force-pushed the optimize_clamp branch 2 times, most recently from f02c949 to 36fae63 Compare November 9, 2026 12:55

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Review info

Run configuration

  • Source mode: Auto
  • Repository: NVIDIA/cccl
  • Branch: main
  • File: .coderabbit.yaml
  • Commit: 89e1909
  • Layers: Repository YAML
  • Review profile: CHILL
  • Plan: Enterprise
  • Run ID: 18920491-71e0-4916-8cf9-f07131a77dcb
Commits

Reviewing files that changed from the base of the PR and between f02c949 and 36fae63.

Files selected for processing: 3

  • docs/libcudacxx/extended_api/math/clamp.rst
  • docs/libcudacxx/extended_api/math/min_max.rst
  • libcudacxx/test/cmath_codegen/clamp.cu

Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 6 remain after this review.

Comment on lines +42 to +44
_CCCL_TEMPLATE(class _Tp)
_CCCL_REQUIRES(::cuda::std::__is_extended_arithmetic_v<_Tp>)
[[nodiscard]] _CCCL_TRIVIAL_API constexpr _Tp clamp(_Tp __v, _Tp __lo, _Tp __hi) noexcept

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important: The last time we discussed this (#5516, #7325) I strongly argued to name the functions clamp_values etc. to sufficiently distinguish them from their std alternatives taking references. This comment still stands and I would prefer the function to be named clamp_values.

Same argument for min and max.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am really not in favor of inventing new names for common operations. We have a lot of standard features with non-standard extensions and we require the user to read the documentation.

This is especially the case here, because the situation where the actual result differs is an incredibly uncommon corner case

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sigh. Alright. I would still prefer another name, but I will not block the PR. I see value in having this function and we should have it.

@miscco

miscco commented Nov 9, 2026

Copy link
Copy Markdown
Contributor Author

/ok to test 4ee5289

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

Copy link
Copy Markdown
Contributor

✅ Benchmark Results

Benchmark comparison completed successfully.

Results
Artifacts

@github-actions

This comment has been minimized.

@miscco

miscco commented Nov 12, 2026

Copy link
Copy Markdown
Contributor Author

sm_75 benchmark for the __nv_bfloat16 min/max change

@bernhardmgruber here are the benchmarks for the three binaries with sm_75 SASS changes.

The T4 run in CI did not produce any data. The T4 stayed at its 585 MHz base clock, so NVBench's throttle check (--throttle-threshold 90, measured against the 1590 MHz boost clock) discarded every sample, and all but one of the 249 workloads timed out without a measurement. The one workload that was measured is not affected by this PR.

So I measured locally. I don't have an sm_75 GPU, so I built the benchmarks as compute_75 PTX only (CMAKE_CUDA_ARCHITECTURES=75-virtual) and ran them on an RTX A6000. The driver JIT-compiles the PTX, so the kernels take the pre-SM80 code path, and CUB selects its sm_75 tuning from the PTX version. Base is the merge-base 5060e9b7d1, test is 6d72810437, with the NVBench flags from ci/bench.yaml, restricted to BF16.

BF16 workload Base PR Change
warp_reduce_min 130.0 µs 37.9 µs 3.4× faster
reduce.min, 2^16 10.2 µs 7.2 µs 1.43× faster
reduce.min, 2^20 14.3 µs 10.2 µs 1.40× faster
reduce.min, 2^24 65.7 µs 60.4 µs 1.09× faster
reduce.min, 2^28 783 µs 790 µs within noise
transform.pytorch fmin / fmax within noise at all sizes

The reduce.min rows use 32-bit offsets. 64-bit offsets gave the same results, except 1.07× at 2^24.

  • The warp benchmark isolates the min operation, so it shows the full effect of replacing the emulation with FMNMX.
  • The device reduction gains wherever it is not bandwidth-bound. At 2^28 both versions run at 88–89% of peak memory bandwidth.
  • The PyTorch fmin and fmax transforms are bandwidth-bound at every size. They move by at most one timer tick (1.024 µs), the same as unchanged ops like mul and div.

There are no regressions. This runs the sm_75 code on Ampere hardware, so absolute numbers will differ from a T4. The A6000 has a similar ratio of instruction throughput to memory bandwidth, so I would expect the same trend there.

@github-actions

This comment has been minimized.

Comment thread libcudacxx/include/cuda/__cmath/clamp.h Resolved
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

`cuda::std::min`, `cuda::std::max` and `cuda::std::clamp` must take their
arguments by reference and return a reference to one of them, so we cannot
change them. Add versions to `<cuda/cmath>` that take and return values and
compare floating point values like `cuda::std::fmin` and `cuda::std::fmax`,
so that `cuda::clamp` maps to a single `min` and `max` instruction.

Fixes NVIDIA#12510
nvcc with MSVC considers the global `::min` and `::max` overloads of the CUDA
headers when checking calls like `cuda::min(float, double)` in SFINAE contexts,
because `cuda::min` is a single function template in an inline namespace. Add
deleted overloads for mixed argument types, so that such calls select a deleted
function instead of failing deduction.

Also silence `bugprone-branch-clone` in `cuda::clamp`, whose branches are
identical when assertions are disabled.
Move the storage order key into the floating-point helpers and compare the sub-half formats with a widened key so the NaN sentinel fits.
`cuda::std::isless` handles NaN in constant expressions and supports all
extended floating-point types, so the special cases for them are no longer
needed. Integers are still compared with `<`, because `cuda::std::isless`
would convert them to `double`.
…DIA#12612

NVIDIA#12612 made `cuda::std::fmin` and `cuda::std::fmax` work for all extended
floating-point types by comparing with `cuda::std::isless` and
`cuda::std::isgreater`, and tests them in `fp_min_max.pass.cpp`. Drop our
storage order key and its tests. Values that compare equal, like -0 and +0,
now return the first argument instead of ordering -0 before +0.

Also test `cuda::min`, `cuda::max` and `cuda::clamp` with `__nv_fp8_ue5m3`,
which is supported since NVIDIA#12570.
@github-actions

This comment has been minimized.

@bernhardmgruber

Copy link
Copy Markdown
Contributor

/sass diff ok 7118c84

@github-actions

Copy link
Copy Markdown
Contributor

@bernhardmgruber, your SASS approval is recorded, but GitHub rejects CI rerun requests while it is running. After the current CI run finishes, rerun its failed jobs to apply the approval.

@miscco miscco added the review meeting Candidate for review in live team review meeting. label Nov 12, 2026
Comment on lines +43 to +50
if constexpr (::cuda::is_floating_point_v<_Tp>)
{
return ::cuda::std::fmin(__a, __b);
}
else
{
return (__b < __a) ? __b : __a;
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We want to make sure there is one source of truth , so we should reuse cuda::minimum

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think cuda::(minimum|maximum) should rather reuse this implementation

…d `cuda::max`

There should be only one implementation of the minimum and maximum of two
values. `cuda::min` and `cuda::max` take their arguments by value, so the
references that `cuda::minimum` and `cuda::maximum` take are optimized away:
the SASS for `float`, `double`, `__half` and `__nv_bfloat16` does not change.
Integers only differ in the operand order, because `cuda::minimum` compares
`a < b` instead of `b < a`.

template <typename T>
[[nodiscard]] __host__ __device__ constexpr
T clamp(T v, T lo, T hi) noexcept;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
T clamp(T v, T lo, T hi) noexcept;
T clamp(T value, T low, T high) noexcept;


**Performance considerations**

- On device, the function maps to a ``min`` and a ``max`` instruction for integer types of up to 64 bits and ``float``, for ``double`` on GPUs with fast FP64 arithmetic, and for ``__half`` and ``__nv_bfloat16`` on ``SM80`` and newer. On GPUs with slow FP64 arithmetic, ``double`` values are compared with integer instructions instead.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we at least mention that these are PTX instructions?

}
}

// Mixed argument types must select a deleted overload rather than fail deduction, otherwise nvcc with MSVC considers

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we add an MVSC guard instead?

@github-actions

Copy link
Copy Markdown
Contributor

🔬 Benchmark SASS comparison

⚠️ The SASS changed for 3 of 101 CUB and 0 of 75 Thrust benchmark target(s). A benchmark run may be necessary

How to request a benchmark run
Request a benchmark run for this PR:

1. Replace the `benchmarks:` block of ci/bench.yaml with exactly this:

benchmarks:
  filters:
    cpp:
      - '^cub\.bench\.reduce\.min\.base$'
      - '^cub\.bench\.reduce\.warp_reduce_min\.base$'
      - '^cub\.bench\.transform\.pytorch\.base$'
  gpus:
    - "h100"   # pick the GPUs that this change can affect

2. Commit with `[bench-only]` at the end of the commit summary, so that
   the unrelated CI jobs are skipped. Then push.

ci/bench.yaml must match ci/bench.template.yaml before the PR can merge.
Reset it once the measurement is done.
Run Value
Baseline 852cde95226185d5d82b3bac7972c87711de14c6
Tested HEAD
Architectures 75-real;80-real;90-real;100-real;110-real;120-real;120-virtual
Targets with a SASS change
Target Architectures with a SASS change
cub.bench.reduce.min.base sm_75
cub.bench.reduce.warp_reduce_min.base sm_75
cub.bench.transform.pytorch.base sm_75

‼️ Summary of Differences ‼️

Showing 3/3 summaries.

cub.bench.reduce.min.base - sm_75 - ⚠️ Significant

Bfloat16 min and max kernels replace compare branches with FMNMX On sm_75, branched compare, NaN-test, select, and permutation sequences are replaced by FMNMX-based sequences. The signature occurs in one warp-reduce kernel, five device-reduce kernels, and the transform fmin and fmax kernels; instruction counts and control-flow structures change in all three targets.

Showing 40/49166 diff lines, 48949 changes. - ⬇️ Full diff

--- base/cub.bench.reduce.min.base.sm_75
+++ test/cub.bench.reduce.min.base.sm_75
@@ -9153,12390 +9153,5056 @@ void cub::_V_300700_SM_750_800_900_1000_1100_1200::detail::reduce::DeviceReduceKernel<cub::_V_300700_SM_750_800_900_1000_1100_1200::detail::reduce::policy_selector_from_types<__nv_bfloat16, unsigned int, cuda::__4::minimum<void>, (cuda::execution::__4::determinism::__determinism_t)1>, (bool)1, __nv_bfloat16 *, __nv_bfloat16 *, unsigned int, unsigned int, cuda::__4::minimum<void>, __nv_bfloat16, __nv_bfloat16, cuda::std::__4::identity>(T3, T4, T6, cub::_V_300700_SM_750_800_900_1000_1100_1200::GridEvenShare<T5>, T7, T9, T10)
 BRA <+0x0>;
 Function : void cub::_V_300700_SM_750_800_900_1000_1100_1200::detail::reduce::DeviceReduceKernel<cub::_V_300700_SM_750_800_900_1000_1100_1200::detail::reduce::policy_selector_from_types<__nv_bfloat16, unsigned int, cuda::__4::minimum<void>, (cuda::execution::__4::determinism::__determinism_t)1>, (bool)1, __nv_bfloat16 *, __nv_bfloat16 *, unsigned int, unsigned int, cuda::__4::minimum<void>, __nv_bfloat16, __nv_bfloat16, cuda::std::__4::identity>(T3, T4, T6, cub::_V_300700_SM_750_800_900_1000_1100_1200::GridEvenShare<T5>, T7, T9, T10)
 IMAD.MOV.U32 R1, RZ, RZ, c[0x0][0x0] ;
-S2R R49, SR_CTAID.X ;
-IMAD.MOV.U32 R0, RZ, RZ, 0x1 ;
+S2R R0, SR_CTAID.X ;
+IMAD.MOV.U32 R2, RZ, RZ, 0x1 ;
 ULDC UR4, c[0x0][0x164] ;
 BMOV.32.CLEAR RZ, B0 ;
-S2R R52, SR_TID.X ;
+S2R R53, SR_TID.X ;
 USHF.L.U32 UR4, UR4, 0xd, URZ ;
-LOP3.LUT R0, R0, c[0x0][0x138], RZ, 0xc0, !PT ;
-BSSY B0, <+0x18140> ;
-ISETP.NE.U32.AND P0, PT, R0, 0x1, PT ;
+LOP3.LUT R2, R2, c[0x0][0x138], RZ, 0xc0, !PT ;
+BSSY B0, <+0x9ec0> ;
+ISETP.NE.U32.AND P0, PT, R2, 0x1, PT ;
 ISETP.NE.U32.AND.EX P0, PT, RZ, RZ, PT, P0 ;
-IMAD.SHL.U32 R55, R49, 0x2000, RZ ;
-IADD3 R3, -R55, c[0x0][0x160], RZ ;
-@P0 BRA <+0xca40> ;
-ISETP.GE.U32.AND P0, PT, R3, 0x2000, PT ;
-@!P0 BRA <+0x9460> ;
-IADD3 R0, R52.reuse, 0x100, RZ ;
-IADD3 R2, R52.reuse, 0x200, RZ ;
-IADD3 R6, P0, R55.reuse, R52, RZ ;
-IADD3 R4, P1, R55.reuse, R0, RZ ;
-IADD3 R3, P2, R55, R2, RZ ;
-LEA.HI.X.SX32 R7, R52, RZ, 0x1, P0 ;
+IMAD.SHL.U32 R50, R0, 0x2000, RZ ;
+IADD3 R46, -R50, c[0x0][0x160], RZ ;
+@P0 BRA <+0x5910> ;
+ISETP.GE.U32.AND P0, PT, R46, 0x2000, PT ;
+@!P0 BRA <+0x3d40> ;
+IADD3 R3, R53.reuse, 0x200, RZ ;
+IADD3 R6, P0, R50.reuse, R53, RZ ;
cub.bench.reduce.warp_reduce_min.base - sm_75 - ⚠️ Significant

Bfloat16 min and max kernels replace compare branches with FMNMX On sm_75, branched compare, NaN-test, select, and permutation sequences are replaced by FMNMX-based sequences. The signature occurs in one warp-reduce kernel, five device-reduce kernels, and the transform fmin and fmax kernels; instruction counts and control-flow structures change in all three targets.

Showing 40/166 diff lines, 156 changes. - ⬇️ Full diff

--- base/cub.bench.reduce.warp_reduce_min.base.sm_75
+++ test/cub.bench.reduce.warp_reduce_min.base.sm_75
@@ -126,110 +126,60 @@ void benchmark_kernel<(int)256, (int)128, benchmark_op_t, __nv_bfloat16, (bool)0>(T3)
 S2UR UR4, SR_CLOCKLO ;
 DEPBAR.LE SB0, 0x0 ;
 IMAD.U32 R0, RZ, RZ, UR4 ;
-IMAD.MOV.U32 R7, RZ, RZ, RZ ;
-LOP3.LUT R5, R0, 0xffff, RZ, 0xc0, !PT ;
-PRMT R2, RZ, 0x5410, R0.reuse ;
-PRMT R3, RZ, 0x5410, R5 ;
-PRMT R4, R4, 0x5410, R0 ;
-FSETP.LE.AND P0, PT, R2, R3, PT ;
-@P0 BRA <+0x40> ;
-FSETP.NAN.AND P0, PT, R3, R3, PT ;
-PRMT R4, R4, 0x5410, R0 ;
-@!P0 PRMT R4, R4, 0x5410, R5 ;
-PRMT R4, RZ, 0x7610, R4 ;
-IMAD.MOV.U32 R6, RZ, RZ, 0x7fff ;
-FSETP.NAN.AND P0, PT, R4, R4, PT ;
-PRMT R4, R6, 0x7610, R4 ;
-@P0 BRA <+0x60> ;
-FSETP.EQ.AND P0, PT, R2, R3, PT ;
-PRMT R4, R4, 0x7632, R4 ;
-@P0 ISETP.GT.U32.AND P1, PT, R5, R5, PT ;
-@P0 SEL R0, R0, R5, P1 ;
-@P0 PRMT R4, R0, 0x7610, R4 ;
-LOP3.LUT R5, R4, 0xffff, RZ, 0xc0, !PT ;
-PRMT R0, RZ, 0x5410, R4.reuse ;
-PRMT R3, RZ, 0x5410, R5 ;
-PRMT R2, R2, 0x5410, R4 ;
-FSETP.LE.AND P0, PT, R0, R3, PT ;
-@P0 BRA <+0x40> ;
-FSETP.NAN.AND P0, PT, R3, R3, PT ;
-PRMT R2, R2, 0x5410, R4 ;
-@!P0 PRMT R2, R2, 0x5410, R5 ;
-PRMT R2, RZ, 0x7610, R2 ;
-IMAD.MOV.U32 R6, RZ, RZ, 0x7fff ;
-FSETP.NAN.AND P0, PT, R2, R2, PT ;
-PRMT R2, R6, 0x7610, R2 ;
-@P0 BRA <+0x60> ;
cub.bench.transform.pytorch.base - sm_75 - ⚠️ Significant

Bfloat16 min and max kernels replace compare branches with FMNMX On sm_75, branched compare, NaN-test, select, and permutation sequences are replaced by FMNMX-based sequences. The signature occurs in one warp-reduce kernel, five device-reduce kernels, and the transform fmin and fmax kernels; instruction counts and control-flow structures change in all three targets.

Showing 40/1865 diff lines, 1768 changes. - ⬇️ Full diff

--- base/cub.bench.transform.pytorch.base.sm_75
+++ test/cub.bench.transform.pytorch.base.sm_75
@@ -9460,9 +9460,9 @@ void cub::_V_300700_SM_750_800_900_1000_1100_1200::detail::transform::transform_kernel<cub::_V_300700_SM_750_800_900_1000_1100_1200::detail::transform::policy_selector_from_types<(bool)0, (bool)1, cuda::std::__4::tuple<thrust::_V_300700_SM_750_800_900_1000_1100_1200::detail::normal_iterator<thrust::_V_300700_SM_750_800_900_1000_1100_1200::device_ptr<__nv_bfloat16>>, thrust::_V_300700_SM_750_800_900_1000_1100_1200::detail::normal_iterator<thrust::_V_300700_SM_750_800_900_1000_1100_1200::device_ptr<__nv_bfloat16>>>, thrust::_V_300700_SM_750_800_900_1000_1100_1200::detail::normal_iterator<thrust::_V_300700_SM_750_800_900_1000_1100_1200::device_ptr<__nv_bfloat16>>>, long, cuda::__4::always_true, fmax_op, __nv_bfloat16 *, __nv_bfloat16 *, __nv_bfloat16 *>(T2, int, bool, T3, T4, T5, cub::_V_300700_SM_750_800_900_1000_1100_1200::detail::transform::kernel_arg<T6>...)
 BRA <+0x0>;
 Function : void cub::_V_300700_SM_750_800_900_1000_1100_1200::detail::transform::transform_kernel<cub::_V_300700_SM_750_800_900_1000_1100_1200::detail::transform::policy_selector_from_types<(bool)0, (bool)1, cuda::std::__4::tuple<thrust::_V_300700_SM_750_800_900_1000_1100_1200::detail::normal_iterator<thrust::_V_300700_SM_750_800_900_1000_1100_1200::device_ptr<__nv_bfloat16>>, thrust::_V_300700_SM_750_800_900_1000_1100_1200::detail::normal_iterator<thrust::_V_300700_SM_750_800_900_1000_1100_1200::device_ptr<__nv_bfloat16>>>, thrust::_V_300700_SM_750_800_900_1000_1100_1200::detail::normal_iterator<thrust::_V_300700_SM_750_800_900_1000_1100_1200::device_ptr<__nv_bfloat16>>>, long, cuda::__4::always_true, fmax_op, __nv_bfloat16 *, __nv_bfloat16 *, __nv_bfloat16 *>(T2, int, bool, T3, T4, T5, cub::_V_300700_SM_750_800_900_1000_1100_1200::detail::transform::kernel_arg<T6>...)
 IMAD.MOV.U32 R1, RZ, RZ, c[0x0][0x0] ;
-S2R R7, SR_CTAID.X ;
+S2R R12, SR_CTAID.X ;
 LDC.S8 R0, c[0x0][0x144] ;
-IMAD.WIDE.U32 R2, R7, 0x800, RZ ;
+IMAD.WIDE.U32 R2, R12, 0x800, RZ ;
 IADD3 R2, P0, -R2, c[0x0][0x138], RZ ;
 IADD3.X R3, ~R3, c[0x0][0x13c], RZ, P0, !PT ;
 ISETP.LT.U32.AND P0, PT, R2, 0x800, PT ;
@@ -9470,692 +9470,282 @@ void cub::_V_300700_SM_750_800_900_1000_1100_1200::detail::transform::transform_kernel<cub::_V_300700_SM_750_800_900_1000_1100_1200::detail::transform::policy_selector_from_types<(bool)0, (bool)1, cuda::std::__4::tuple<thrust::_V_300700_SM_750_800_900_1000_1100_1200::detail::normal_iterator<thrust::_V_300700_SM_750_800_900_1000_1100_1200::device_ptr<__nv_bfloat16>>, thrust::_V_300700_SM_750_800_900_1000_1100_1200::detail::normal_iterator<thrust::_V_300700_SM_750_800_900_1000_1100_1200::device_ptr<__nv_bfloat16>>>, thrust::_V_300700_SM_750_800_900_1000_1100_1200::detail::normal_iterator<thrust::_V_300700_SM_750_800_900_1000_1100_1200::device_ptr<__nv_bfloat16>>>, long, cuda::__4::always_true, fmax_op, __nv_bfloat16 *, __nv_bfloat16 *, __nv_bfloat16 *>(T2, int, bool, T3, T4, T5, cub::_V_300700_SM_750_800_900_1000_1100_1200::detail::transform::kernel_arg<T6>...)
 SEL R2, R2, 0x800, P0 ;
 ISETP.NE.AND P0, PT, R2, 0x800, PT ;
 ISETP.EQ.OR P0, PT, R0, RZ, P0 ;
-@P0 BRA <+0xf20> ;
-S2R R0, SR_TID.X ;
-IMAD.MOV.U32 R12, RZ, RZ, 0x1000 ;
-IMAD.WIDE.U32 R2, R7, R12, c[0x0][0x150] ;
-IMAD.WIDE.U32 R4, R7, R12, c[0x0][0x160] ;
-IMAD.WIDE.U32 R14, R0, 0x8, R2 ;
-IMAD.WIDE.U32 R16, R0, 0x8, R4 ;
-LDG.E.64.SYS R8, [R14] ;
-LDG.E.64.SYS R10, [R16] ;
-LDG.E.64.SYS R4, [R16+0x800] ;
-LDG.E.64.SYS R2, [R14+0x800] ;
-BMOV.32.CLEAR RZ, B0 ;
-BSSY B0, <+0xc0> ;
-PRMT R13, RZ, 0x5410, R8 ;
-PRMT R14, RZ, 0x5410, R10 ;
-FSETP.GE.AND P0, PT, R13, R14, PT ;
-SHF.R.U64 R19, R8, 0x10, R9 ;
-SHF.R.U64 R20, R10, 0x10, R11 ;
-PRMT R6, R6, 0x5410, R8 ;
-@P0 BRA <+0x40> ;
-FSETP.NAN.AND P0, PT, R14, R14, PT ;
-PRMT R6, R6, 0x5410, R8 ;

@github-actions

Copy link
Copy Markdown
Contributor

🥳 CI Workflow Results

🟩 Finished in 2h 24m: Pass: 100%/517 | Total: 5d 21h | Max: 1h 50m | Hits: 88%/1058209

See results here.

AI failure analysis unavailable. See the workflow run for details.

Comment on lines +45 to +46
// The branches are identical when assertions are disabled.
// NOLINTBEGIN(bugprone-branch-clone)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review meeting Candidate for review in live team review meeting.

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

cuda::std::clamp generates 4-6 instructions for floating-point types; min/max, xorsign, and .SAT are cheaper

5 participants