[cub] Add cooperative cached high-bin histograms - #10568
Conversation
4296072 to
81822d2
Compare
|
This PR seems a bit short. Does it contain all of the optimizations from the winning high bin path from the autoresearch branch? What about RLE and warp coalescing? I recall we played around with turning those on/off. I don't see those or an option for those here. |
81822d2 to
facac06
Compare
8a01558 to
dc68629
Compare
|
Yes. The updated branch now carries the full winning high-bin design rather than only the initial cache layer.
|
|
Just like PR #10556, this PR should have performance results and a flowchart of the dispatch logic in it. You may launch a large sweep to classify performance. Use the existing scripts for this. |
dc68629 to
f4ad49d
Compare
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughSummary by CodeRabbit
WalkthroughChangesThe change adds high-bin histogram tuning, cooperative CUDA launch helpers, a cooperative histogram kernel, hosted dispatch selection with fallback behavior, and expanded histogram tests. High-bin histogram execution
Suggested reviewers: Merge Risk: 🟠 High · up to The cooperative high-bin histogram path still has edge cases that can produce incorrect results for large uint64 ranges and potentially corrupt cached counters when no cache slots are available. These correctness risks should be fixed or explicitly guarded before merging. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (3)
cub/test/catch2_test_env_launch_helper.h (1)
172-179: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winsuggestion:
LaunchCooperativeskips the kernel allow-list check thatdoitperforms at lines 87-97. Tests that register an allowed kernel set will not catch an unexpected cooperative kernel. Consider extracting the check into a helper and calling it here too.cub/cub/device/dispatch/kernels/kernel_histogram.cuh (1)
729-729: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valuesuggestion: Add
[[nodiscard]]tohistogram_cache_probe. The coding guidelines state "Most functions with a non-void return type should use[[nodiscard]], except for functions with known side effects." This function has side effects on the cache, but the return value decides the spill path, so callers must not drop it.Source: Coding guidelines
cub/cub/device/dispatch/dispatch_histogram.cuh (1)
1215-1215: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valuesuggestion:
IsEvenis unused on the host-init path, as the comments at lines 954 and 1011 state. Flipping it totruehere and at line 1246 while the byte-sample call at line 1147 keepsfalseproduces a second, behaviorally identical instantiation ofdetail::histogram::dispatch. Pick one value for all host-init calls, or keep the/* IsEven = (unused for host-init) */annotation.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: 2f55702b-b514-478a-8c87-a8e9400e4844
📒 Files selected for processing (7)
cub/cub/detail/launcher/cuda_driver.cuhcub/cub/detail/launcher/cuda_runtime.cuhcub/cub/device/dispatch/dispatch_histogram.cuhcub/cub/device/dispatch/kernels/kernel_histogram.cuhcub/cub/device/dispatch/tuning/tuning_histogram.cuhcub/test/catch2_test_device_histogram_env.cucub/test/catch2_test_env_launch_helper.h
Included review availability: Your plan provides up to 12 included reviews per hour; 10 remain after this review.
|
Final comprehensive B200 sweep is complete and the PR description now contains the results and graphs. The production selector shows aggregate geomean gains versus current The description links all eight aggregate graphs and the complete per-cell JSON; the asset branch also contains 120 per-shape graphs. |
|
/ok to test d284878 |
Do these performance results match the performance results from the raw autoresearch branch? Did you actually port over all the optimizations? |
🔬 CUB benchmark SASS comparisonHow to request a benchmark run
Targets with a SASS change
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
cub/cub/detail/launcher/cuda_runtime.cuh (1)
119-119: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winsuggestion: Mark both cooperative-launch helpers
noexcept.
CUB_RUNTIME_FUNCTIONdoes not provide an exception specification. AddnoexceptafterconstonCooperativeLaunchSupportedandLaunchCooperative.Source: Coding guidelines
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: e1223e3d-a3d4-40b9-97ed-feef6a07ffb2
📒 Files selected for processing (3)
cub/cub/detail/launcher/cuda_runtime.cuhcub/cub/device/dispatch/tuning/tuning_histogram.cuhcub/test/catch2_test_env_launch_helper.h
Included review availability: Your plan provides up to 12 included reviews per hour; 11 remain after this review.
|
I also fixed the CI regressions exposed by the full matrix in
The six focused histogram and histogram-environment binaries rebuild cleanly, and all six test executables pass locally. The full autoresearch-equivalent performance sweep remains in progress and will replace the reduced results in the PR description. |
|
No. I audited the current PR against the final raw branch, and the existing results do not represent the final autoresearch winner. The PR currently selects cuckoo cache + direct-output spill + warp coalescing, while the raw branch ultimately selected single-probe cache + block-private GMEM spill + RLE. The current PR also omitted the final raw counter-width, cache-sizing/occupancy, pipelined single-channel, vectorized multi-channel, and classify-path optimizations. I am porting those production-relevant pieces into the existing |
d49ebae to
c91758a
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
cub/cub/device/dispatch/kernels/kernel_histogram.cuh (1)
1136-1138: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick winimportant: Confirm that
cache_slots_per_channelis never 0 when the policy selects a cache.Lines 1136-1138 treat
cache_slots_per_channel == 0as reachable. If it is 0 andpolicy.high_bin_cache != none, thencache_mask == 0andcache_log2 == 0, sohistogram_cache_probeevaluateshash >> 32(undefined behavior) and then writeskeys[0]andcounts[0]. With zero slots that key region has zero size, so the write lands in thecache_countsregion and corrupts counters.The
static_assertat lines 1108-1112 constrains the policy value only. The kernel receives the slot count from dispatch, which sizes it from available dynamic shared memory.Either assert
cache_slots_per_channel >= 32here, or skip the cache path at runtime when the slot count is 0.#!/bin/bash # Check how dispatch derives the cooperative cache slot count and whether it can reach 0 with a caching policy. set -euo pipefail rg -n -C 12 'cache_slots_per_channel|cache_slots_floor|cooperative_cache_slots_per_channel' cub/cub/device/dispatch/dispatch_histogram.cuh
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: ee8bd354-9520-4cab-aeed-16f5a81af3e9
📒 Files selected for processing (4)
cub/cub/device/dispatch/dispatch_histogram.cuhcub/cub/device/dispatch/kernels/kernel_histogram.cuhcub/cub/device/dispatch/tuning/tuning_histogram.cuhcub/test/catch2_test_device_histogram_env.cu
Included review availability: Your plan provides up to 12 included reviews per hour; 11 remain after this review.
😬 CI Workflow Results🟥 Finished in 3h 03m: Pass: 80%/284 | Total: 12d 14h | Max: 3h 02m | Hits: 16%/1252769See results here. AI failure analysis1. CUDA 12 cooperative histogram launch rejects kernel function pointers · 20 jobsExplanation: CUDA 12's overload set does not accept the deduced kernel function-pointer type directly, so both the production launcher and test launcher fail before histogram tests compile. The supplied diff proposes the matching fix by converting the kernel to `const void*`. Evidence: Copy this prompt into a coding agentJobs:
2. NVCC promotes cooperative histogram unused variables to errors · 32 jobsExplanation: NVCC's device compilation removes the host-only cooperative use of `cooperative_smem_bytes`, while non-RLE policy instantiations remove uses of the pending arrays; promoted warnings then fail every MSVC matrix variant. The declarations must be scoped to the compile-time branches that use them or explicitly marked unused in discarded variants. Evidence: Copy this prompt into a coding agentJobs:
3. Cooperative histogram kernel triggers signed-narrowing clang-tidy errors · 1 jobExplanation: The new kernel mixes unsigned CUDA built-ins such as `threadIdx.x` and `blockDim.x` with signed loop variables and passes an unsigned value to `__clz`. Clang-tidy treats each implicit unsigned-to-signed conversion as an error. Evidence: Copy this prompt into a coding agentJobs: 4. CUB histogram stream operators generate duplicate inline declarations · 1 jobExplanation: Documentation generation emits two `inline` tokens for the four new histogram enum stream operators, and warnings are treated as errors. The failing declarations combine `_CCCL_HOST_API` with explicit `inline`; the supplied diff proposes plain `inline` host-only overloads. Evidence: Copy this prompt into a coding agentJobs: 5. Device ArgMinMax selects the wrong first maximum index for abs comparison · 1 jobExplanation: The maximum value assertion passes, but first-maximum mode returns a later index for the `short` input and `abs_less_t` comparator. No reduction implementation is changed by the supplied PR diff, so the evidence cannot establish whether this is a pre-existing deterministic defect or a seed/hardware-specific flake. Evidence: Copy this prompt into a coding agentJobs: |
|
The production-relevant techniques from the final raw autoresearch winner are now ported to this PR: single-probe shared-memory caching, block-private global-memory spill, RLE-compressed misses, cooperative gather, occupancy-preserving dynamic cache sizing, separate local/output counter widths, staged single-channel loading, vectorized multi-channel loading, and the optimized EVEN/RANGE classification paths. The raw benchmark instrumentation and experimental policy scaffolding were intentionally not carried over. I reran the comprehensive B200 sweep against trunk using the raw branch's run and graph scripts. The PR description now contains the exact coverage, all eight aggregate graphs, immutable links to the 128 per-shape graphs and raw JSON, and the final regression assessment. The corrected implementation has strong aggregate gains (1.918x–3.178x geometric mean over the high-bin cells, depending on API and sample type), but it is not regression-free: 108 of 6,720 high-bin cells are below trunk, with 84 more than 5% slower. The material slowdowns cluster in I32 single-channel EVEN/RANGE around 32K–65K bins and I32 multi-channel EVEN around 49K–57K bins. Multi-channel RANGE improves every high-bin cell. |
Why
This PR improves histogram configurations whose block-private counters no longer fit in shared memory. The existing high-bin path gives every block a complete histogram in global memory and gathers every block/bin pair afterward. That is robust, but it performs substantial global-memory traffic even when many updates repeatedly target a small working set of bins.
The new path keeps the same block-private global-memory representation for correctness and bounded contention, but places a shared-memory cache in front of it. A cooperative launch then gathers the private histograms directly into the output in the same kernel. The existing non-cooperative global-memory-privatized sweep remains the fallback when the cooperative path cannot run.
Design
All algorithm and tuning choices live in the existing
HistogramPolicy. There is no second policy hierarchy or separate legacy wrapper. The policy contains both the established histogram-agent settings and the high-bin settings used by dispatch:The production high-bin policy uses the algorithm selected by the final autoresearch run:
Dispatch treats the policy cache size as a floor. It finds the largest power-of-two cache that fits the selected kernel's opt-in dynamic-shared-memory limit without reducing occupancy below the floor configuration. Occupancy and grid sizing are computed from the cooperative kernel that will actually launch.
The input path also includes the production optimizations from the raw research branch:
Dispatch flow
flowchart TD A[Histogram request] --> B{Private counters fit the<br/>low-bin shared-memory path?} B -- Yes --> C[Existing shared-memory-privatized sweep] B -- No --> D[Read the selected HistogramPolicy] D --> E{Host-initialized launch and<br/>cooperative launch supported?} E -- No --> F[Existing block-private global-memory sweep<br/>followed by gather] E -- Yes --> G[Start with the policy cache floor] G --> H[Grow cache by powers of two while it fits<br/>the kernel's dynamic-SMEM limit and preserves occupancy] H --> I{At least one cooperative block<br/>can reside on every SM?} I -- No --> F I -- Yes --> J[Launch one cooperative grid] J --> K[Initialize output and block-private histograms] K --> L[Load and classify samples] L --> M{Single-probe cache hit?} M -- Yes --> N[Update replicated shared-memory counter] M -- No --> O[RLE-compress misses and update<br/>the block-private global histogram] N --> P[Flush cache into the block-private histogram] O --> P P --> Q[Grid-wide barrier] Q --> R[Cooperatively gather private histograms<br/>into the output]Device-initialized/JIT launches and devices that cannot satisfy cooperative-launch or dynamic-shared-memory requirements use the existing fallback. Temporary storage is allocated only for the selected spill mode.
Performance
The final B200 sweep compares PR source
28c00020993d1f0b850fe3f0909cd51211d33895with trunk sourceb7aaea69a2b07e50f09e67f2962da0243e0b7c5d. Both sides use the same benchmark-only input-shape overlay and thehistogram_algo_sweep.pyandhistogram_algo_perf.pyscripts from the raw autoresearch branch. The production selector is unforced.The sweep covers:
The 1G and 2B multi-channel cases use 64-bit offsets on both sides because four interleaved channels exceed a 32-bit row stride. Counters remain 32-bit for those cases because the per-channel count is at most 2B. The resulting dataset has matching PR/trunk coverage: 4,080 cells per single-channel API and 3,060 cells per multi-channel API.
The table reports the geometric mean across the entire sweep, then the geometric mean and tail within the high-bin tier (32,768 bins and above). “Faster cells” counts ratios greater than or equal to 1.0 in that high-bin tier.
These results are not regression-free. Of 6,720 high-bin cells, 108 are slower than trunk and 84 are more than 5% slower. The material regressions are concentrated in I32 single-channel EVEN/RANGE around 32K–65K bins and in I32 multi-channel EVEN around 49K–57K bins. The worst high-bin cells are 0.543x for single-channel EVEN, 0.680x for single-channel RANGE, and 0.764x for multi-channel EVEN. Multi-channel RANGE improves every high-bin cell. F64 is effectively regression-free for the single-channel APIs at a 5% threshold; multi-channel EVEN has one 0.919x cell.
The figures below are geometric means across all 15 shapes and all applicable element counts. The published asset set also contains one figure per input shape.
Raw benchmark results
Validation
Validated on an NVIDIA B200 with CUDA 13.3, GCC 13.3, CMake 4.3.2, C++20, and native
sm_100code generation.Passed on the final PR head:
git diff --check;cub.test.device.histogram.lid_0: 53,595 assertions in 39 test cases;cub.test.device.histogram_env.lid_0: 758 assertions in 40 test cases;cub.test.device.histogram_custom_policy_hub.lid_0: 1 assertion in 1 test case; andcccl.c.parallel.test.histogram: 304 assertions in 11 test cases.The comprehensive benchmark also compiled and exercised C++17 single- and multi-channel EVEN/RANGE binaries with 32-bit and 64-bit offset configurations.