Skip to content

Investigate a persistent SM103 grid for blockwise 8-bit optimizers #6

Description

@heiheiha798

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

  • Baseline: bitsandbytes-foundation/bitsandbytes@95f9af309d4d5793847169c39288dcd3fcbdf564.
  • 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.
  • Preserve all optimizer arithmetic and current public/Python behavior. Open upstream PR Fix decoupled weight decay ordering in the CUDA Adam/AdEMAMix kernels bitsandbytes-foundation/bitsandbytes#2040 changes Adam/AdEMAMix weight-decay ordering in the same kernel file; this issue must preserve whichever ordering is present after any required rebase and must not absorb that correctness fix.

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions