fix(cuda): chunk 4-bit quant/dequant launches above INT32_MAX elements (#1785) - #2071
Open
Anai-Guo wants to merge 1 commit into
Open
fix(cuda): chunk 4-bit quant/dequant launches above INT32_MAX elements (#1785)#2071Anai-Guo wants to merge 1 commit into
Anai-Guo wants to merge 1 commit into
Conversation
bitsandbytes-foundation#1785) The CUDA blockwise 4-bit kernels take the element count as a 32-bit int, so a single launch cannot describe a tensor with more than INT32_MAX elements: the count wraps negative, the derived grid dimension goes negative and the launch fails with cudaErrorInvalidValue ("invalid configuration argument" at ops.cu). Issue the work as several blocksize-aligned launches instead. Chunks start on a block boundary, so per-block absmax entries and the two-elements-per-byte 4-bit packing stay aligned; tensors within the limit still take a single launch with byte-identical arguments. Signed-off-by: Tai An <antai12232931@outlook.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The CUDA blockwise 4-bit kernels take the element count as a 32-bit
int(
ops.cu:void quantizeBlockwise(..., const int n)), and the Python side passes itthrough a
ct.c_int32argtype. A tensor withnumel() >= 2**31therefore wraps thecount negative,
num_blocks = n / blocksizegoes negative, and the launch fails:This is the 4-bit half of #1785 ("Support quantizing tensors when
numel() > INT_MAX",listed there under high priority), and was reported again in #2067 for
google/gemma-4-E4B-it's embedding under axolotl's NF4 round-trip simulation. It alsohits fused-MoE expert weights held as one parameter, e.g.
[128, 4096, 4096] == 2**31.Reproduced on an RTX 4090 (bitsandbytes 0.49.2, torch 2.5.1+cu121), one process per size:
n2**31 - 655362**31invalid configuration argument2**31 + 65536invalid configuration argumentChange
quantize_4bit/_dequantize_4bit_implin the CUDA backend now issue the work asseveral blocksize-aligned launches instead of one. Chunks start on a block boundary,
so per-block
absmaxentries and the two-elements-per-byte 4-bit packing stay aligned;each chunk's element count fits in
int32, so in-kernel indexing stays in range too.Tensors within the limit yield a single
(0, n)chunk with byte-identical kernelarguments, i.e. the previous behaviour is untouched on the common path. No C ABI change,
so existing
ctypescallers are unaffected.Scoped to the 4-bit ops (the high-priority pair in #1785). 8-bit blockwise quantization,
4-bit GEMV and the LLM.int8() ops still have the same int32 limit; kernel-level int64
indexing remains the longer-term fix.
Prior art: #2049 by @alex-ht took the same chunking approach at the
functional.pylevel; it was closed by its author. This one sits in the CUDA backend, where the int32
ABI actually is, so non-CUDA backends are left alone.
Verification (RTX 4090, 24 GB)
1. The reported failure is fixed, and the data is correct — not just "it doesn't crash".
Quantize + dequantize a real
2**31-element fp16 tensor (nf4, blocksize 64) and compareagainst the input. The chunk seam falls at element 2147483584:
The error is flat across the seam and matches NF4 quality on
N(0,1)— the second chunkis real data, not garbage written at a wrong offset.
2. Bit-exact against the single-launch path. With the per-launch limit lowered so the
chunked path runs on small tensors,
q,absmaxand the dequantized output are alltorch.equalto the single-launch results across 3 dtypes x {fp4, nf4} x 8 blocksizes(48 cases), with a deliberately unaligned limit and a partial trailing block.
3. The new test is not vacuous. It runs 8 launches per case, and mutating the packed
offset (
_off // 2->_off // 4) fails all 48 cases.4. Lint.
ruff==0.14.3(the pinned.pre-commit-config.yamlrev)checkandformat --checkclean on both files.🤖 Generated with Claude Code