Build the GGML IQ packing kernels as a single CUDA extension - #2462
Conversation
IQ1_S and IQ2_XS were loaded as two separate torch extensions. The split that separates `modelopt_cuda_ext`, `_fp8`, and `_mx` exists because those need different CUDA version gates and incompatible build flags; the IQ formats need neither. They share common.cuh, the same `>=11.8` gate, and the same `-O3` flags, so splitting them compiled the shared header twice and grew the loader, `__getattr__`, and `precompile()` once per format. Bind both packers from a single `ggml.cpp` as `iq1_s_pack` and `iq2_xs_pack`, and replace `get_cuda_ext_iq1_s`/`get_cuda_ext_iq2_xs` with `get_cuda_ext_ggml`. Each format keeps its kernels in its own translation unit, so adding a format is a new `.cu` plus one `module.def`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Chenjie Luo <chenjiel@nvidia.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Enterprise Run ID: 📒 Files selected for processing (4)
💤 Files with no reviewable changes (1)
Included review availability: Your plan provides up to 12 included reviews per hour; 11 remain after this review. 📝 WalkthroughWalkthroughThe change consolidates IQ1_S and IQ2_XS packing in one GGML CUDA extension. It moves IQ1_S binding logic into the shared C++ module, renames the IQ2_XS binding, and updates GPU tests to use the shared loader. ChangesGGML IQ packer consolidation
Priority: ⬇️ Low Estimated code review effort: 3 (Moderate) | ~25 minutes Change: Refactor Sequence Diagram(s)sequenceDiagram
participant Test
participant GGMLLoader
participant GGMLExtension
participant CUDAPacker
Test->>GGMLLoader: request get_cuda_ext_ggml()
GGMLLoader->>GGMLExtension: build or return cached extension
Test->>GGMLExtension: call iq1_s_pack or iq2_xs_pack
GGMLExtension->>CUDAPacker: execute CUDA packer
CUDAPacker-->>Test: return packed tensor
Merge Risk: ⚪ Minimal · up to The consolidated extension retains device validation and has no remaining merge-blocking issue. 🚥 Pre-merge checks | ✅ 5 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (5 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Comment |
meenchen
left a comment
There was a problem hiding this comment.
Bot review (claude-opus-5) — DM the bot to share feedback.
LGTM — merging the two IQ extensions into modelopt_cuda_ext_ggml is a clean, mechanical consolidation; the wrappers, validation, docstrings and retry semantics carry over unchanged, and the GPU suite is reparametrized onto the merged module with coverage intact.
Verified: iq1_s.cu/iq2_xs.cu keep all internals in anonymous namespaces and only export the distinct *_pack_cuda entry points, and common.cuh is header-only (inline/templates), so the three-TU build has no ODR or duplicate-symbol risk. package-data globs pick up the renamed ggml.cpp automatically.
Needs action:
- Sign off as a human on the test edit:
test_cuda_ext_iq1_s/test_cuda_ext_iq2_xswere collapsed into onetest_cuda_ext_ggmlandfmt.get_extensionreplaced byfmt.packer, which the single-extension merge justifies — but a deleted test needs owner confirmation. - Update the stacked PRs (#2446/#2447/#2449) to call
get_cuda_ext_ggml().iq1_s_pack(...)/.iq2_xs_pack(...)before they land, as the PR body notes.
No action needed:
- Optional:
_IQ_EXTENSIONSintests/gpu/_extensions/test_torch_extensions.pynow holds formats, not extensions; renaming it would match the new_IqFormatdocstring.
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #2462 +/- ##
==========================================
+ Coverage 71.48% 78.96% +7.48%
==========================================
Files 590 590
Lines 64766 64779 +13
==========================================
+ Hits 46299 51154 +4855
+ Misses 18467 13625 -4842
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
#2462 merged the two IQ packing extensions into one. get_cuda_ext_iq1_s and get_cuda_ext_iq2_xs are gone, replaced by get_cuda_ext_ggml, and the packer each exposed as `pack` is now `iq1_s_pack` / `iq2_xs_pack` on the shared module. Update both codecs and their CUDA tests accordingly. The monkeypatched fallback tests are unaffected in substance: each codec still imports the getter into its own module namespace, so patching it out isolates to one format. Verified on an RTX PRO 6000 Blackwell: 36 unit tests and 33 GPU tests, covering both codec parity suites and the extension-boundary suite. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Chenjie Luo <chenjiel@nvidia.com>
What does this PR do?
Type of change: Code refactoring
#2448added the GGML IQ packing kernels as two torch extensions,modelopt_cuda_ext_iq1_sandmodelopt_cuda_ext_iq2_xs. This merges them into one,modelopt_cuda_ext_ggml.The existing per-extension split in
extensions.pyexists for reasons that don't applyto the IQ formats:
get_cuda_extgates on CUDA>=11while_fp8/_mxgate on>=11.8, and_mxneeds--use_fast_math, which must not reach the basetensor_quantkernels.
get_cuda_ext_iq1_sandget_cuda_ext_iq2_xsdiffered in none of that —same
>=11.8gate, same-O3flags, samecommon.cuh— so the split only compiled theshared header twice, ran nvcc twice, and grew the loader,
__getattr__, andprecompile()once per format. With IQ2_XXS / IQ3_S / IQ4_NL plausibly following, thatscales badly.
Changes:
ggml/ggml.cppholds both host-side validation wrappers and the singlePYBIND11_MODULE, bindingiq1_s_packandiq2_xs_pack(previously each moduleexported a bare
pack). Deletesggml/iq1_s.cppandggml/iq2_xs.cpp; thevalidation logic and docstrings carry over unchanged.
get_cuda_ext_iq1_s+get_cuda_ext_iq2_xs→get_cuda_ext_ggml, which buildsggml.cpp,iq1_s.cu, andiq2_xs.cutogether. The retry-on-raise_if_failedsemantics of the old getters are preserved.
.cuplus onemodule.def— no new extension, loader, orprecompile()line.No caller outside
extensions.pyand its tests referenced the old getters onmain, sonothing else changes. Note for the follow-up PRs in the
#2448series(
#2446/#2447/#2449): the codec layer should callget_cuda_ext_ggml().iq1_s_pack(...)/.iq2_xs_pack(...)instead ofget_cuda_ext_iq1_s().pack(...)/get_cuda_ext_iq2_xs().pack(...).Usage
Testing
Ran on a single H200 NVL (TRT-LLM
1.3.0rc27.dev202609170000container), building themerged extension from scratch:
pytest tests/gpu/_extensions/test_torch_extensions.py— 24 passed (6:44). Thisis the full existing IQ suite (zero-block layout, encode, dtype rejection,
row-straddling rejection, invalid/negative-zero scales, byte-exact dtype equivalence,
and the brute-force optimality round-trip) reparametrized onto the merged module,
plus the untouched
modelopt_cuda_ext/_fp8/_mxload tests.precompile()loads all four extensions and that the merged module exportsexactly
iq1_s_packandiq2_xs_packwith the expected arities..soto confirmno duplicate-symbol collisions between the two
.cutranslation units.pre-commit run --files ...passes on all changed files (ruff, mypy, clang-format,bandit, license headers).
Before your PR is "Ready for review"
#2448(merged today, unreleased) and have no callers outside this file's own tests.
CONTRIBUTING.md: N/A — no new code or dependencies; the moved wrappers keep their original attribution.Additional Information
Follow-up to #2448. Merge before the remaining PRs in that series (#2446, #2447, #2449)
land, so the codec layer is written against
get_cuda_ext_ggmland no rename is neededafterwards.
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Changes
packtoiq2_xs_pack.