You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The CUDA one-state and two-state blockwise 8-bit optimizer kernels already use grid-stride loops, but the host launch currently creates one 256-thread CTA for every 256 parameters and each kernel sets its loop bound from gridDim.x * 256. As a result, each CTA executes exactly one state block.
Before the loop, every CTA copies one or two fixed 256-entry quantization maps into shared memory, initializes CUB storage, and synchronizes. For large optimizer tensors this setup and CTA scheduling are repeated thousands of times per step. On a 148-SM B300, capping the launch to a measured SM-wave multiple could let each CTA process several independent state blocks while reusing its shared quantization maps.
This is a measurement-gated hypothesis, not an assumed performance win. A persistent grid can reduce load balancing or latency hiding, and the maps may already be cache-resident. No source dispatch change is justified unless direct and end-to-end B300 measurements clear the acceptance gate below.
Compare the current uncapped launch with a small forced matrix of exact-SM103 grid caps using isolated builds in the same B300 allocation.
Keep forced cap choices in benchmark/build scaffolding. Do not expose a public forcing API.
Only if evidence identifies a coherent winner, cap the exact-SM103 launch grid and make the existing grid-stride loops cover the full n; retain the current uncapped launch on every other CUDA architecture and on HIP.
Use an existing cached device-property mechanism or equally narrow per-device cache. Do not add an uncached device query to every optimizer step and do not introduce a broad dispatch framework.
The intended production surface is limited to the one-state and two-state blockwise 8-bit CUDA optimizer launch/loop bounds plus focused validation. No paging, FSDP, optimizer refactor, or state-format change belongs here.
Correctness oracle
For deterministic inputs, compare forced uncapped and capped variants after one and multiple steps.
Cover AdamW8bit/Adam8bit and AdEMAMix8bit on the two-state path, plus Lion8bit, Momentum8bit, RMSprop8bit, and Adagrad8bit on the one-state path where supported.
Cover FP16, BF16, and FP32 parameters/gradients; weight_decay=0 as the primary launch-only comparison; one nonzero-decay control that preserves upstream ordering; and skip_zeros true/false where existing support permits.
Require byte-identical packed state buffers and bitwise-identical absmax arrays. Require bitwise-identical parameter storage between forced variants when the only difference is CTA-to-block assignment; retain existing public-reference tolerances as a secondary oracle.
Include n below one block, at 255/256/257, immediately below/at/above the selected cap * 256, across multiple grid-stride rounds, and with a final partial block. Include realistic parameter counts.
Run focused tests/test_optim.py coverage, relevant public optimizer-step regressions, the CUDA build, and the full pre-commit suite. All CUDA compilation and GPU tests run only through Slurm.
Do not use this work to change existing skip_zeros, FSDP, paging, or weight-decay semantics.
B300 benchmark and acceptance
Use one B300/SM103 allocation and record exact source/build identity, job/node/GPU, driver, CUDA compiler, PyTorch/bitsandbytes versions, commands, environment, and raw logs/results. Baseline and forced variants must use the same compiler/flags and be interleaved in the same allocation.
Compare current uncapped launch with 1x, 3x, 6x, and 12x SM caps; narrow around a winner only if the first matrix supports it.
Measure direct blockwise 8-bit optimizer updates with preallocated state for 4K, 64K, 1M, 16M, and 64M or the largest memory-safe size, plus representative counts such as 4096x4096 and 8192x28672.
Cover FP16/BF16/FP32 and both one-state and two-state kernels.
Measure end-to-end optimizer.step() for AdamW8bit, AdEMAMix8bit, and Lion8bit on representative large tensors. Exclude allocation/state initialization from the timed region.
Use at least 20 warmups where practical and at least 7 interleaved batches per cell. Choose enough CUDA-event iterations to make each batch stable, scaling the count down for very large tensors and end-to-end steps rather than forcing 100 expensive calls everywhere. Report complete/round-level samples, median, p10/p90, elements/s, and an explicitly defined effective-bandwidth estimate; never report best-only results.
Proceed only if one cap produces at least a 5% median gain across at least two large realistic shapes, the gain is larger than interleaved dispersion and remains visible in end-to-end optimizer.step(), correctness is bit-exact between launch variants, and no adjacent large shape regresses by more than 5%. A gain confined to the direct kernel, a cherry-picked size, or small launch-bound tensors is a no-go.
Non-goals
No optimizer math change; no weight-decay-ordering fix; no skip_zeros, paging, FSDP, or state-format change; no 32-bit optimizer tuning; no General8bit/4-bit quantization work; no public API; no other architecture/backend generalization; no dependency on prior fork branches or issues.
Problem
The CUDA one-state and two-state blockwise 8-bit optimizer kernels already use grid-stride loops, but the host launch currently creates one 256-thread CTA for every 256 parameters and each kernel sets its loop bound from
gridDim.x * 256. As a result, each CTA executes exactly one state block.Before the loop, every CTA copies one or two fixed 256-entry quantization maps into shared memory, initializes CUB storage, and synchronizes. For large optimizer tensors this setup and CTA scheduling are repeated thousands of times per step. On a 148-SM B300, capping the launch to a measured SM-wave multiple could let each CTA process several independent state blocks while reusing its shared quantization maps.
This is a measurement-gated hypothesis, not an assumed performance win. A persistent grid can reduce load balancing or latency hiding, and the maps may already be cache-resident. No source dispatch change is justified unless direct and end-to-end B300 measurements clear the acceptance gate below.
Scope
bitsandbytes-foundation/bitsandbytes@95f9af309d4d5793847169c39288dcd3fcbdf564.n; retain the current uncapped launch on every other CUDA architecture and on HIP.The intended production surface is limited to the one-state and two-state blockwise 8-bit CUDA optimizer launch/loop bounds plus focused validation. No paging, FSDP, optimizer refactor, or state-format change belongs here.
Correctness oracle
For deterministic inputs, compare forced uncapped and capped variants after one and multiple steps.
weight_decay=0as the primary launch-only comparison; one nonzero-decay control that preserves upstream ordering; andskip_zerostrue/false where existing support permits.nbelow one block, at 255/256/257, immediately below/at/above the selectedcap * 256, across multiple grid-stride rounds, and with a final partial block. Include realistic parameter counts.tests/test_optim.pycoverage, relevant public optimizer-step regressions, the CUDA build, and the full pre-commit suite. All CUDA compilation and GPU tests run only through Slurm.Do not use this work to change existing
skip_zeros, FSDP, paging, or weight-decay semantics.B300 benchmark and acceptance
Use one B300/SM103 allocation and record exact source/build identity, job/node/GPU, driver, CUDA compiler, PyTorch/bitsandbytes versions, commands, environment, and raw logs/results. Baseline and forced variants must use the same compiler/flags and be interleaved in the same allocation.
optimizer.step()for AdamW8bit, AdEMAMix8bit, and Lion8bit on representative large tensors. Exclude allocation/state initialization from the timed region.Proceed only if one cap produces at least a 5% median gain across at least two large realistic shapes, the gain is larger than interleaved dispersion and remains visible in end-to-end
optimizer.step(), correctness is bit-exact between launch variants, and no adjacent large shape regresses by more than 5%. A gain confined to the direct kernel, a cherry-picked size, or small launch-bound tensors is a no-go.Non-goals
No optimizer math change; no weight-decay-ordering fix; no
skip_zeros, paging, FSDP, or state-format change; no 32-bit optimizer tuning; no General8bit/4-bit quantization work; no public API; no other architecture/backend generalization; no dependency on prior fork branches or issues.