Skip to content

[CUB] Fix silent sample loss in DeviceHistogram for signed bytes and wide histograms - #10993

Open
VaggelisGian wants to merge 2 commits into
NVIDIA:mainfrom
VaggelisGian:fix-histogram-neg-int8-samples
Open

[CUB] Fix silent sample loss in DeviceHistogram for signed bytes and wide histograms#10993
VaggelisGian wants to merge 2 commits into
NVIDIA:mainfrom
VaggelisGian:fix-histogram-neg-int8-samples

Conversation

@VaggelisGian

Copy link
Copy Markdown
Contributor

Description

closes #10977
closes #10976

cub::DeviceHistogram silently lost samples and corrupted counts in two related ways, both returning cudaSuccess:

  1. Negative signed byte samples vanished ([BUG]: cub::DeviceHistogram silently drops all negative int8_t samples (Even and Range) #10977). Byte-sized sample types take a fast path that uses the raw sample value as a privatized bin index into a 256-bin shared-memory histogram (PassThruTransform). A negative int8_t sample produced a negative bin index, which the agent treats as "outside the histogram", so every negative sample was dropped.

  2. Bin indices were rounded or wrapped after binning ([BUG]: [BUG]: cub::DeviceHistogram mis-counts bins and writes out of bounds when the number of bins exceeds what the sample type can represent exactly #10976). When folding per-block privatized histograms into the output, agent_histogram.cuh cast each privatized bin index back through the sample type before handing it to the output decode op. Any bin index not exactly representable in the sample type got altered on that round trip: __nv_bfloat16 above 256 bins merged neighboring bins and could write one element past the end of the output histogram, __half broke past 2048 bins, int16_t dropped every bin >= 32768, and float breaks past 2^24 bins. This affected HistogramEven, HistogramRange, and the Multi variants.

The two bugs compound: fixing only #10977 by routing signed bytes to the generic path still drops bins 128..255 for full-range int8_t histograms, because those bin indices wrap negative in the same round trip.

Changes

  • Restrict the byte-sample fast path to unsigned 8-bit sample types: the dispatch condition becomes sizeof(SampleT) == 1 && !is_signed_v<SampleT> at all four public entry points. Signed byte types take the generic ScaleTransform/SearchTransform path, which handles negative samples and levels correctly.
  • Stop feeding the output decode op a value that round-trips through the sample type: templatize ScaleTransform::BinSelect's sample parameter and pass the privatized bin index from StoreOutput as int. The decode ops are exact for any representable bin index; unsigned byte paths are unaffected because their indices are already <= 255.
  • Apply the same signedness gate in c/parallel/src/histogram.cu, which picked its byte-sample specialization purely from value_type.size == 1.

Unsigned byte samples keep the fast path with unchanged behavior. Plain char is signed on the
platforms we test (and on MSVC by default), so existing char-sampled histograms now take the generic
path: correctness improves wherever levels go negative, and the trade-off is a performance-only change
for those configs.

Verification

Reproducers from the two issues, CUDA 12.8, sm_90:

  • Full-range int8_t histogram (256 bins over [-128, 128)): 128 of 256 counted before, 256 of 256 after.
  • HistogramRange with 300 consecutive bf16 levels, one sample per bin: 33 wrong bins before (plus an out-of-bounds atomicAdd), 0 wrong bins after.

New regression tests in catch2_test_device_histogram.cu: negative int8 samples over [-60, 64), the full 8-bit domain with 256 bins (Even and Range), 65536-bin int16 histograms (Even and Range), and a guarded __half case with 3000 exactly representable bins. The bf16 configuration is covered by the reproducer above; adding it to the catch2 test needs bf16 support plumbing this test file does not have yet.

Note for reviewers: if the performance of signed-byte histograms matters, a follow-up could teach the pass-thru path to shift signed samples into [0, 256) together with a matching offset in the output decode op; I kept the fix minimal instead.

Checklist

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

Byte-sized samples took a fast path that indexes a 256-bin privatized
histogram with the raw sample value, so negative int8_t samples produced
negative bin indices and were never counted (NVIDIA#10977).
Separately, when folding privatized counts into the output histogram,
each bin index was cast through the sample type before the output decode
op, so indices that are not exactly representable in the sample type
were altered on the round trip: __nv_bfloat16 merged bins above 256 and
could write one element past the output histogram, __half broke above
2048 bins, and int16_t dropped every bin >= 32768 (NVIDIA#10976).

Restrict the byte-sample fast path to unsigned 8-bit sample types so
signed bytes use the generic ScaleTransform/SearchTransform path, pass
privatized bin indices to the output decode op as int instead of
round-tripping them through the sample type, and apply the same
signedness gate in c/parallel's byte-sample selection.

Test Plan:
  Reproducers from both issues, CUDA 12.8, sm_90:
  full-range int8_t 256-bin histogram: 256 of 256 counted (was 128)
  bf16 HistogramRange 300 consecutive levels: 0 wrong bins (was 33 + OOB atomicAdd)
  ci/util/build_and_test_targets.sh --preset cub-cpp20 --build-targets
    cub.test.device.histogram --ctest-targets '^cub[.]test[.]device[.]histogram$'
  -> Passed (5m28s), includes new regression tests for negative int8
     samples, the full 8-bit domain, 65536-bin int16, and half > 2048 bins
  neighbor targets cub.test.device.histogram_api / _env_api /
    _custom_policy_hub / cub.test.block.histogram compile clean
@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
@VaggelisGian

VaggelisGian commented Aug 25, 2026

Copy link
Copy Markdown
Contributor Author

/ok to test

@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: b920accf-3531-4879-9138-b536c6d89086

📥 Commits

Reviewing files that changed from the base of the PR and between 6de1622 and 7cffd55.

📒 Files selected for processing (2)
  • cub/cub/device/device_histogram.cuh
  • 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; 9 remain after this review.


📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes

    • Corrected histogram classification for signed 8-bit samples.
    • Improved bin-index handling across histogram implementations, including evenly segmented and custom-range histograms.
    • Preserved accurate results for negative values, large bin counts, and half-precision ranges.
    • Restricted optimized processing to supported unsigned byte samples.
  • Tests

    • Added regression coverage for signed 8-bit, 16-bit, and half-precision histogram scenarios.

Walkthrough

Changes

Signed 8-bit samples now use the non-byte histogram path. Privatized bin indices no longer convert through the sample type. Regression tests cover negative int8_t, full-domain int8_t, large int16_t, and large half_t histograms.

Changes

Histogram correctness

Layer / File(s) Summary
Sample classification
c/parallel/src/histogram.cu, cub/cub/device/device_histogram.cuh
Byte-sample detection now applies only to unsigned one-byte samples across kernel generation and histogram dispatch paths.
Privatized bin index output
cub/cub/agent/agent_histogram.cuh, cub/cub/device/dispatch/kernels/kernel_histogram.cuh
StoreOutput passes integer bin indices directly to BinSelect, whose sample parameter is independently templated.
Regression coverage
cub/test/catch2_test_device_histogram.cu
Tests validate signed-byte ranges, all 256 int8_t values, 65,536-bin int16_t histograms, and 3,000-bin half_t range histograms.

Assessment against linked issues

Objective Addressed Explanation
Fix dropped negative int8_t samples in HistogramEven, HistogramRange, and Multi variants [#10977]
Preserve distinct bin indices when the bin count exceeds the sample type’s exact range [#10976]

Suggested reviewers: bernhardmgruber, jacobfaib

Merge Risk: 🔵 Low · up to 7cffd

The PR fixes histogram sample loss and count corruption, with the remaining risk limited to a localized naming-convention issue in the implementation; it is mergeable with owner awareness or a follow-up cleanup.


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.

Actionable comments posted: 2

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

843-844: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

suggestion: Declare immutable test-state variables const. const auto* does not make d_sample_ptr immutable. Use const auto* const d_sample_ptr and declare the h_expected vectors const. As per coding guidelines, “All variables that are not modified must be declared const.”

Also applies to: 884-885, 933-935, 1001-1001

Source: Coding guidelines


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 52a18ca5-b8b6-40b6-9681-e2d5bdb72c2a

📥 Commits

Reviewing files that changed from the base of the PR and between 46a37f8 and 6de1622.

📒 Files selected for processing (5)
  • c/parallel/src/histogram.cu
  • cub/cub/agent/agent_histogram.cuh
  • cub/cub/device/device_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; 11 remain after this review.

Comment thread cub/cub/device/device_histogram.cuh Outdated
Comment thread cub/cub/device/dispatch/kernels/kernel_histogram.cuh
Make the dispatch-time bool_constant objects constexpr and the test
sample pointers fully const.
@VaggelisGian

Copy link
Copy Markdown
Contributor Author

/ok to test 7cffd55

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