[FEA] Reduce libcudf fatbin size by deduplicating equivalent CUDA kernel instantiations
- Dominant language
- C++
- Stars
- 9.8k
- Forks
- 1.1k
- Avg merge
- 3d 6m
- Merged PRs (30d)
- 278
Description
**Is your feature request related to a problem? Please describe.**
`libcudf.so` is approximately 1.5 GiB in the current cudf-spark-jni package. A recent full Release build containing the supported CUDA architecture matrix measured:
- `libcudf.so`: 1,504,051,352 bytes
- `.nv_fatbin`: 1,061,134,960 bytes (70.55% of the library)
- 518 `fatbinData` blobs, commonly containing eight SASS images plus PTX
This size increases package storage, distribution, and native-library extraction time. It is particularly visible in Spark executor startup, where every executor may need to extract the bundled native library before CUDA initialization (NVIDIA/cudf-spark#15145).
Inspection of the largest fatbin blobs found that separate CUDA translation units often emit equivalent template kernel instantiations. The duplication is then repeated across the supported GPU architectures. Existing size-oriented fatbin compression reduces the byte representation but cannot remove these duplicate kernels.
**Describe the solution you'd like**
Deduplicate the highest-value equivalent kernel instantiations while preserving all supported GPU architectures and production symbols.
Three isolated CUDA object prototypes produced the following results:
| Candidate | Separate/specialized fatbins (`sm_75`) | Prototype fatbin (`sm_75`) | Measured saving | Estimated full-matrix saving |
|---|---:|---:|---:|---:|
| Co-locate stable and unstable segmented sort | 9,627,344 B | 4,815,528 B | 4,811,816 B (49.98%) | 18.32 MiB |
| Co-locate segmented standard deviation and variance | 4,540,088 B | 2,285,232 B | 2,254,856 B (49.67%) | 15.14 MiB |
| Share bounded-open and bounded-closed range-window kernel types | 7,790,032 B | 5,207,784 B | 2,582,248 B (33.15%) | 6.16 MiB |
The combined conservative estimate is 39.6 MiB, or 3.91% of `.nv_fatbin` and 2.76% of the complete library. The full-matrix values are estimates obtained by applying the measured one-architecture reduction ratio to the exact corresponding blobs in the full build; they still need confirmation with an all-architecture build.
All three prototypes compiled successfully with CUDA 12.9.1 and `CMAKE_CUDA_ARCHITECTURES=75`. The strong exported symbol set of each prototype matched the original object, or the union of the original objects for co-location tests.
Suggested implementations:
1. **Segmented sort:** Place the stable and unstable implementations from `src/sort/segmented_sort.cu` and `src/sort/stable_segmented_sort.cu` in one translation unit and remove the second source from the CMake source list. The prototype included both sources only as a measurement shortcut; a production change should move the definitions normally. Across all inspected architectures, 99.8-100% of the stable segmented-sort SASS was already present in the unstable blob. The stable PTX entry points were also a subset of the unstable PTX entry points, except for one additional unstable helper kernel.
2. **Segmented std/variance:** Place the two dispatch entry points currently in `src/reductions/segmented/std.cu` and `src/reductions/segmented/var.cu` in one translation unit. On `sm_75`, 96-98% of their SASS was identical by section name and contents.
3. **Range windows:** Remove `WindowType` from the template identity of `rolling::bounded_distance_functor` and pass open/closed as a uniform runtime flag or enum. In the `sm_75` cubin, the 72 bounded-open and 72 bounded-closed kernels each occupied 5.059 MiB; together they represented 81.3% of the TU's SASS. Sharing the kernel type collapses these to one family.
The first two should not change runtime GPU behavior. The range-window change adds a uniform branch to comparison logic, so it should only proceed with focused rolling benchmarks covering open/closed bounds, grouped/ungrouped inputs, nulls, and the supported data types.
A follow-up candidate is scalar standard deviation/variance. Their `sm_75` cubins have about 5.29 MiB of exactly overlapping SASS, suggesting roughly another 10 MiB at full architecture coverage, but this pair has not yet been compiled as a combined prototype and is not included in the estimate above.
**Describe alternatives you've considered**
- **Remove GPU architectures:** Not acceptable; all supported architectures must remain available.
- **Strip more symbols:** Does not address the dominant fatbin payload and would reduce production stack-trace quality.
- **Increase fatbin/JAR compression:** cuDF already calls `rapids_cuda_enable_fatbin_compression(... TUNE_FOR rapids)`. CUDA 13's size-oriented zstd fatbin compression should improve representation but does not eliminate equivalent instantiations.
- **Split large translation units:** This improves parallel build time but generally redistributes or duplicates device code instead of reducing the final binary. This goal needs to be balanced with the compile-time work in #21973.
- **Move the kernels to RTCX/LTO IR:** The infrastructure from #22496 is complementary and may produce larger long-term savings, but migrating ordinary libcudf kernels introduces runtime-linking and cache considerations. The source-level changes above are smaller AOT optimizations.
**Additional context**
Related work:
- NVIDIA/cudf-spark#15145 tracks executor startup latency, including native-library extraction of the approximately 1.5 GiB library.
- #21973 documents libcudf compile-time optimization and the build-time benefits of splitting translation units. Co-location changes proposed here should therefore measure both final binary size and the parallel build critical path.
- #22496 implemented the LTO IR infrastructure for deferred specialization and binary-size reduction.
- #17399 tracks broader JIT compilation of cuDF kernels.
Suggested validation for each change:
- Full Release build with the complete supported architecture matrix
- Compare `.nv_fatbin`, complete `libcudf.so`, and packaged artifact sizes
- Confirm exported symbols and run the affected unit tests
- Measure the CUDA compilation critical path
- Run focused NVBench coverage for any device-code type erasure, especially range rolling
Contributor guide
Research direction
Start with src/sort/segmented_sort.cu, src/sort/stable_segmented_sort.cu, src/reductions/segmented/std.cu, and src/reductions/segmented/var.cu, then inspect the CMake source lists and the rolling bounded-distance implementation. Run an all-architecture Release build and the affected unit tests; for range windows, add focused NVBench coverage. Done means reduced .nv_fatbin and library sizes with exported symbols, GPU behavior, and the build critical path preserved.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cmake, cpp
- Domain
- build-system, performance
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 45/100