[libcudacxx][fp] add fptool: fp_custom and fpmp2_stat + unit tests - #10830
Draft
akolesov-nvidia wants to merge 19 commits into
Draft
[libcudacxx][fp] add fptool: fp_custom and fpmp2_stat + unit tests#10830akolesov-nvidia wants to merge 19 commits into
akolesov-nvidia wants to merge 19 commits into
Conversation
The host branches of the double-precision primitives called fma, sqrt and round unqualified from inside cuda::experimental, relying on the name resolving to the global namespace. Any header that declares an overload of one of those names in cuda::experimental first stops that lookup, so including <cuda/fptool> before <cuda/fpmp_math> failed to compile. Spell them ::cuda::std::<fn> instead, which is the portable form used elsewhere in the file, and ::rsqrt for the CUDA builtin in the device branch.
A drop-in `double` whose exponent and mantissa sizes are template parameters, for algorithm sensitivity analysis: narrow the format, rerun, see where the result moves. Every arithmetic operation rounds its result to the requested format, so an application changes one typedef and nothing else. The sizes can also be left dynamic and set at run time, which lets one build sweep a range of formats. Sits in <cuda/fptool> next to fpemu and fpmp, and needs neither of them.
A drop-in fpmp2 that records what the arithmetic did: how many operations of each kind, the exponent range and limb gap of every operand and result, and the degenerate events - cancellation, underflow, overflow, subnormals, overlapping and inverted limb pairs. Numerically it is the wrapped type exactly, so results are bit-identical and an application swaps one typedef to measure a run. Counting happens on the device through atomics on one program-wide record; the host side is a transparent pass-through. The math functions come with the type in <cuda/fptool>, since these are analysis types that few translation units carry.
main moved to clang-format 22.1.5 in NVIDIA#10729, which aligns continuation lines differently from the 20.1.7 these files were written under.
Contributor
Contributor
Author
|
/ok to test 32ffc15 |
This comment has been minimized.
This comment has been minimized.
Contributor
Author
|
/ok to test b91307e |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Contributor
Author
|
/ok to test 11571d8 |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Contributor
Author
|
/ok to test 11571d8 |
Contributor
Author
|
/ok to test f3e5484 |
Contributor
⏱️ CCCL compile-time benchmark comparison: Public headers compile-time benchResult: 0 regression row(s), 3 improvement row(s) above threshold.
Artifacts: reports and traces Direct file processing
🟢 Direct file processing — Improvements
|
This comment has been minimized.
This comment has been minimized.
davebayer
reviewed
Aug 18, 2026
Review follow-up. The host side of the fp_custom runtime sizes and of the fpmp2_stat record went through cudaMemcpyToSymbol, which picks the current device, synchronizes with the default stream and returns a status that the callers dropped. Both now take a cuda::stream_ref: the stream names the device the state belongs to and orders the copy against the kernels that read it, and a failure throws cuda::cuda_error. fpmp2_stat_read_device_data returns the record by value. The device-code accessors stay, for a JIT program with no host side to set a size from, and are now device-only rather than host-device. Conversion out of fp_custom to float is implicit where binary32 represents the requested format and explicit anywhere the mantissa bits or the exponent range would be lost, instead of always explicit.
The record's fields carried a paragraph each while others had a line, so the reference was spread over two structs and the file comment. The file documentation now has a Metrics section covering the operation counters, the numerical events, the per-slot statistics and how to read the limb gap, with a short table naming the fields and prose where a metric needs it; the fields keep a one-line description each. Nothing is dropped: the field paragraphs move up, and the prose from the fpdev metrics note comes with them, including what each metric is good for and the paragraph the inf/NaN counters never had.
The low level built a two-word reciprocal - a Newton step, a squared reciprocal, a Dekker product against the divisor and a low_mul by the numerator - which came to more arithmetic than the mid-accuracy quotient it was meant to undercut, and in a dependent chain it divided slower than mid. It now runs the same Nagai iteration as mid, stopped after the first residual correction and without the closing renormalization, leaving the pair unnormalized as this level does throughout. That is a reciprocal plus five operations, nine SASS instructions against twenty-four, and on an L40S it divides 2.2x faster than mid for one bit less. The residual is exact: the quotient estimate is within an ulp or two of a_hi/b_hi, so the subtraction inside the fma cancels without rounding. Measured with ts/fpmp on fp32mp2: the division holds 44 of 48 bits where it used to range over 43 to 45, and the eight math functions that divide at this level gain 3 to 11 percent with no accuracy class changing. tan and atan2 ask for the mid division explicitly and are untouched.
Contributor
Author
|
/ok to test de34b2d |
This comment has been minimized.
This comment has been minimized.
The unpacked form's mantissa field carries 9 guard bits below the binary64 significand (EXTRA_BITS, placing the significand at bits 61..9), so a chain that stays unpacked computes on 62 significand bits and rounds to the storage format once, at the pack. That is why it is more accurate than double or packed fpemu in a chain - and why its chained results are not bit-identical to double even at fpemu_accuracy::high, whose individual operations are. Both properties are intentional, and neither was written down where a user would look for it.
…VRTC <cuda/stream> cannot be included under NVRTC: stream_ref.h defines stream_ref only where the CUDA runtime is available, while get_stream.h declares an operator() returning it by value regardless, so the umbrella header fails with an incomplete type. The stream-based runtime-size cases in this test were already guarded for NVRTC; the include was not, which failed the C++17 and C++20 NVRTC jobs. Guarding only the include keeps NVRTC coverage of the stream-independent fp_custom tests. Verified by compiling the test with NVRTC directly: the incomplete-type errors from get_stream.h are gone.
Contributor
Author
|
/ok to test daadb15 |
This comment has been minimized.
This comment has been minimized.
davebayer
approved these changes
Aug 19, 2026
Answers the review on the promotion PR. Drop the <cuda_runtime.h> includes from fptool_custom.h and fptool_stat.h. A CUDA compiler pre-includes that header, and the host-only path already gets cuda_runtime_api.h from the CCCL config when a CTK is present, so naming it bought nothing. Take a value into fp_custom implicitly only where the requested format holds the source exactly, which is the rank rule CCCL applies to its floating-point types, with integers counting as double. A narrower or dynamic format takes one by cast instead. The float constructor is constrained to a deduced float rather than naming one, so that a double cannot reach a narrow format through it, which would round it twice and through the narrower range of the two. What the cast reports is the format the value is entering, not a loss in the constructor: fp_custom stores the value unreduced, and the sizes are applied by the first arithmetic operation. Add CCCL_FP_CUSTOM_EXPLICIT_CASTS alongside, defaulting to 1 and mirroring CCCL_FPMP_EXPLICIT_CASTS. Setting it to 0 leaves the narrowing side implicit, for moving a codebase written against double onto the type, which is what fp_custom is for. custom.pass.cpp asserts the conversion matrix at both settings.
Contributor
Author
|
/ok to test a1489b8 |
Contributor
🥳 CI Workflow Results🟩 Finished in 1h 34m: Pass: 100%/117 | Total: 2d 04h | Max: 1h 34m | Hits: 71%/481930See results here. |
miscco
reviewed
Aug 20, 2026
| static constexpr uint16_t __mant_size = 52; | ||
| }; | ||
|
|
||
| #if __STDCPP_FLOAT64_T__ == 1 && !_CCCL_CUDA_COMPILER(NVCC) |
Contributor
There was a problem hiding this comment.
This should be a global _CCCL_HAS_FLOAT64() define
Comment on lines
+553
to
+559
| #if _CCCL_CUDA_COMPILATION() | ||
| NV_IF_ELSE_TARGET(NV_IS_DEVICE, | ||
| (return __fp_custom_device_mantissa_size<_FpType>;), | ||
| (return __fp_custom_host_mantissa_size<_FpType>;)) | ||
| #else // ^^^ _CCCL_CUDA_COMPILATION() ^^^ / vvv !_CCCL_CUDA_COMPILATION() vvv | ||
| return __fp_custom_host_mantissa_size<_FpType>; | ||
| #endif // !_CCCL_CUDA_COMPILATION() |
Contributor
There was a problem hiding this comment.
This can just be
Suggested change
| #if _CCCL_CUDA_COMPILATION() | |
| NV_IF_ELSE_TARGET(NV_IS_DEVICE, | |
| (return __fp_custom_device_mantissa_size<_FpType>;), | |
| (return __fp_custom_host_mantissa_size<_FpType>;)) | |
| #else // ^^^ _CCCL_CUDA_COMPILATION() ^^^ / vvv !_CCCL_CUDA_COMPILATION() vvv | |
| return __fp_custom_host_mantissa_size<_FpType>; | |
| #endif // !_CCCL_CUDA_COMPILATION() | |
| NV_IF_ELSE_TARGET(NV_IS_DEVICE, | |
| (return __fp_custom_device_mantissa_size<_FpType>;), | |
| (return __fp_custom_host_mantissa_size<_FpType>;)) |
Comment on lines
+641
to
+642
| int64_t __unbiased_exp = static_cast<int64_t>(__exp_bits) - __original_bias; | ||
| int64_t __new_exp_bits = __unbiased_exp + __new_bias; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Adds fptool, the analysis half of the FP feature set, behind one new umbrella
<cuda/fptool>. fp_custom is a drop-in double whose exponent and mantissa sizes are
template parameters, so an application swaps one typedef, reruns, and sees where
reduced precision moves its result; the sizes can also be left dynamic and set at
run time. fpmp2_stat is a drop-in fpmp2 that records what the arithmetic did - the
operation counts, the exponent range and limb gap of every operand and result, and
the degenerate events such as cancellation, overflow and inverted limb pairs - while
reproducing the wrapped type's results bit for bit, so measuring a run costs one
typedef as well. Both live in cuda::experimental and rest on the already-promoted
fpemu (#9777) and fpmp (#10517, #10697).
Nine files, of which four are unit tests, all run on host and device under C++17 and
C++20 and checked through NVRTC. Nothing existing changes behavior; the only edit
outside the new files is a four-line fix in fpmp_impl.h, whose host fallbacks called
fma, sqrt and round unqualified from inside cuda::experimental, which made including
<cuda/fptool> before <cuda/fpmp_math> fail to compile.
closes
Checklist