Skip to content

[CUB] Fix HistogramEven bin arithmetic for level ranges wider than the common type - #10996

Open
VaggelisGian wants to merge 2 commits into
NVIDIA:mainfrom
VaggelisGian:fix-histogram-level-arithmetic
Open

[CUB] Fix HistogramEven bin arithmetic for level ranges wider than the common type#10996
VaggelisGian wants to merge 2 commits into
NVIDIA:mainfrom
VaggelisGian:fix-histogram-level-arithmetic

Conversation

@VaggelisGian

Copy link
Copy Markdown
Contributor

Description

closes #10975

cub::DeviceHistogram::HistogramEven mishandled integral level ranges wider than the common type of LevelT and SampleT. Three symptoms, all from the same root cause: the bin arithmetic subtracted levels in the narrow common type before widening.

  • Full-range int16_t levels over [-32768, 32767) put every sample in bin 0 (max_level - min_level wrapped to -1 in int16_t), returning cudaSuccess.
  • A [-1.5e9, 1.5e9) int32_t range with 100 bins was rejected with cudaErrorInvalidValue even though (upper - lower) * (num_levels - 1) = 3e11 is far below the documented uint64_t bound; the subtraction overflowed int32_t first.
  • Wider spans were rejected through signed-overflow UB rather than by a valid check.
  • Bin counts that do not fit CommonT slipped past the Fix overflow issue in histogram even benchmark when the number of bins exceeds what LevelT can represent #6908 rejection for some values because the count was narrowed through static_cast<CommonT> before the check and wrapped into a huge unsigned number there.

Changes

  • ScaleTransform stores the integral scale fraction in IntArithmeticT (the widened arithmetic type) instead of CommonT, computes range as a widened subtraction, and computes sample - min_level after widening both operands. Modular unsigned subtraction of two cast operands is exact here because samples below min_level are rejected before bin computation.
  • The custom-type/__int128 bin path casts the fraction fields back to CommonT, keeping its semantics unchanged.
  • MayOverflow now rejects bin counts above CommonT's maximum explicitly (same observable contract as Fix overflow issue in histogram even benchmark when the number of bins exceeds what LevelT can represent #6908, without relying on narrowing wrap), then checks the widened range against IntArithmeticT max divided by the bin count. Both host-init call sites pass the raw bin count instead of pre-narrowing it; the device-init paths get the same rejection they previously lacked.

Documented contract preserved: rejection iff (upper_level[i] - lower_level[i]) * (num_levels[i] - 1) exceeds the widened arithmetic type (128-bit when the common type is 128 bits wide). One deliberate gap remains: small sample/level types use a uint32_t fast arithmetic path, so products between 2^32 and 2^64 are still rejected where the docs promise acceptance. Fixing that would move those configs to 64-bit division in the per-sample hot loop; I left it alone pending maintainer input on whether the docs or the current policy should change.

Verification

Reproducers from the issue, CUDA 12.8, sm_90: full-range int16 now reports bins {0, 50, 99} for samples {-32768, 0, 32766} instead of all-in-bin-0; the int32 case returns success instead of cudaErrorInvalidValue. New regression tests cover both configurations. The existing "num_bins exceeds LevelT range" and "bin computation does not overflow" tests pin the rejection contract and still pass.

Checklist

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

…e common type

The even-bin computation subtracted levels in the narrow common type of
LevelT and SampleT before widening to the arithmetic type, so a range
like full int16 wrapped to a negative span (silently binning every
sample into bin 0), wide int32 ranges overflowed and were spuriously
rejected with cudaErrorInvalidValue despite being far below the
documented uint64_t product bound, and the num_bins representability
check from NVIDIA#6908 only fired for some counts because the count was
narrowed through CommonT first.

Store the integral scale fraction in the widened arithmetic type,
compute both differences after widening their operands, reject bin
counts above the common type maximum explicitly instead of relying on
narrowing wrap, and pass the raw bin count from all dispatch sites.
The custom-type and __int128 path keeps its previous semantics by
casting the fraction fields back to the common type.

Test Plan:
  NVIDIA#10975 reproducers, CUDA 12.8, sm_90:
  int16 full-range 100 bins: bins {0,50,99} counted (was all in bin 0)
  int32 [-1.5e9,1.5e9) 100 bins: cudaSuccess (was cudaErrorInvalidValue)
  ci/util/build_and_test_targets.sh --preset cub-cpp20 --build-targets
    cub.test.device.histogram --ctest-targets '^cub[.]test[.]device[.]histogram$'
  -> Passed (3m24s), including the existing rejection-contract tests
     and new regression tests for both repro configurations
@VaggelisGian
VaggelisGian requested a review from a team as a code owner August 25, 2026 12:09
@VaggelisGian
VaggelisGian requested a review from pauleonix August 25, 2026 12:09
@github-project-automation github-project-automation Bot moved this to Todo in CCCL Aug 25, 2026
@copy-pr-bot

copy-pr-bot Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@cccl-authenticator-app cccl-authenticator-app Bot moved this from Todo to In Review in CCCL Aug 25, 2026
@coderabbitai

coderabbitai Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 5bb169a2-108f-45fa-b302-a93c1d424887

📥 Commits

Reviewing files that changed from the base of the PR and between 4e0b254 and 2f65cb1.

📒 Files selected for processing (1)
  • cub/test/catch2_test_device_histogram.cu
🚧 Files skipped from review as they are similar to previous changes (1)
  • cub/test/catch2_test_device_histogram.cu

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


📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes

    • Improved histogram calculations for wide integer ranges.
    • Prevented incorrect overflow handling and bin placement when processing values near type limits.
    • Enhanced support for custom numeric types in histogram scaling.
  • Tests

    • Added regression coverage for full-range 16-bit and wide 32-bit histogram inputs.

Walkthrough

Changes

The integral HistogramEven path now widens overflow checks, scale initialization, and bin calculations. Regression tests cover full-range int16_t and wide-range int32_t histograms.

HistogramEven integral arithmetic

Layer / File(s) Summary
Overflow validation
cub/cub/device/dispatch/dispatch_histogram.cuh
MayOverflow validates bin counts before range arithmetic and computes level ranges after widening.
Widened bin computation
cub/cub/device/dispatch/kernels/kernel_histogram.cuh
Integral scaling and bin computation use IntArithmeticT for widened range, sample, minimum, and multiplication operations.
Wide-range regression coverage
cub/test/catch2_test_device_histogram.cu
Tests verify expected bins for full int16_t and wide-range int32_t level ranges.

Assessment against linked issues

Objective Addressed Explanation
Correctly process integral level ranges wider than intermediate sample or common types [#10975]
Reject only requests that exceed the documented overflow condition [#10975]
Reject bin counts that cannot be represented by the level type [#10975]

Suggested reviewers: pauleonix

Merge Risk: ⚪ Minimal · up to 2f65c

This change corrects histogram bin arithmetic for wider integral level ranges and adds regression coverage; no actionable merge-blocking risk remains beyond normal checks and review.


Comment @coderabbitai help to get the list of available commands.

@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.

🧹 Nitpick comments (1)
cub/test/catch2_test_device_histogram.cu (1)

841-851: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

suggestion: Declare immutable test fixtures const, and declare h_samples constexpr in both tests. Keep d_histogram mutable because histogram_even writes the output buffer. As per coding guidelines, “All variables that are not modified must be declared const” and “All variables that can be evaluated at compile time must be declared constexpr.”

Also applies to: 863-873

Source: Coding guidelines


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: e0feea89-d5ed-454c-90e0-d4d359cedc5c

📥 Commits

Reviewing files that changed from the base of the PR and between 46a37f8 and 4e0b254.

📒 Files selected for processing (3)
  • cub/cub/device/dispatch/dispatch_histogram.cuh
  • cub/cub/device/dispatch/kernels/kernel_histogram.cuh
  • cub/test/catch2_test_device_histogram.cu

Included review availability: Your plan provides up to 12 included reviews per hour; 10 remain after this review.

Per coding guidelines: variables evaluable at compile time are declared
constexpr.
@VaggelisGian

Copy link
Copy Markdown
Contributor Author

/ok to test 2f65cb1

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

Labels

None yet

Projects

Status: In Review

1 participant