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
Upstream issue #1633 reports a deterministic illegal CUDA memory access on the first AdamW8bit.step() under PyTorch FSDP2. The issue is open, labeled Contributions Welcome, FSDP, and Optimizers, and currently has no linked branch or pull request.
The current optimizer runtime assumes every parameter, gradient, and state object passed to the native kernels is a plain tensor. Under FSDP2, the optimizer key is a DTensor: state allocation through zeros_like(p) preserves the wrapper, while update_step() ultimately passes wrapper-backed pointers and a local gradient element count into the existing CUDA optimizer kernel. The detailed upstream diagnosis shows that both plain local state allocation and passing the parameter's local tensor to the kernel are required; either change alone still fails.
This is a public distributed-training correctness gap, not a kernel-tuning hypothesis. The goal is to reuse the existing optimizer kernels safely on each FSDP2 local shard while preserving the global DTensor parameter as the optimizer key.
Bounded Scope
Investigate a Python-only runtime fix in bitsandbytes/optim/optimizer.py plus focused distributed tests:
detect an FSDP2 DTensor through public, lazily imported PyTorch APIs;
keep the global parameter object as the param-group and state-dict key;
keep optimizer-mode and min_8bit_size selection based on the global parameter size;
use to_local() to obtain the contiguous, nonempty local parameter and gradient views at the native-kernel boundary;
allocate state1/state2 and 256-element absmax arrays as plain tensors sized for that local shard; and
leave the existing CUDA kernels, arithmetic, state formats, and plain-tensor path unchanged.
The supported claim is deliberately narrow: ordinary contiguous FSDP2 Shard(0) parameters on the non-paged Optimizer1State and Optimizer2State runtime paths with max_unorm == 0, validated through AdamW8bit and Lion8bit, including their small-parameter FP32-state fallback.
Explicit Non-Goals
paged/UVM optimizers;
LAMB/LARS or any max_unorm > 0 path requiring distributed-global norm semantics;
scheduled AdEMAMix, which owns separate state/update overrides;
arbitrary hand-created DTensor placements, noncontiguous or empty local shards;
FSDP2 distributed checkpoint/save-load support beyond preserving existing behavior;
Unsupported DTensor combinations must not be passed as raw wrapper pointers to native kernels. If encountered during implementation, fail clearly rather than silently copy/update detached storage or expand this issue.
Validate patched AdamW8bit and Lion8bit at world sizes 1 and 2; world size 4 is useful when available but not required for the minimum patch.
Use fixed deterministic gradients and compare every local shard update to an explicit plain-local-tensor bitsandbytes reference from an identical snapshot. Require exact parameter, state, and absmax equality where the shard/block partition is identical.
Cover FP16, BF16, and FP32 parameter/gradient storage; global sizes below and above min_8bit_size; aligned shards; local tails around 255/256/257 and 4095/4096/4097; and multiple steps.
Assert that optimizer state remains keyed by the global parameter, while native state tensors are plain tensors with local numel, device, dtype, and ceil(local_numel / 256) absmax sizing.
Run plain-tensor optimizer regression and state-dict round-trip coverage, the focused optimizer suite, full pre-commit, and a small compute-sanitizer check. Distributed FSDP2 checkpoint support is reported separately and is not implied by a passing runtime step.
B300 Validation and System Evidence
Use one multi-B300 Slurm allocation with recorded node/GPU/driver/CUDA/PyTorch/bitsandbytes identities and exact commands. Do not run GPU work on the login node.
Run a synthetic fully trainable model and a small public FSDP2 training reproduction with AdamW8bit; add Lion8bit as the one-state coverage path.
Record per-rank optimizer-state bytes, peak allocated/reserved memory, median/p10/p90 step latency, and finite training loss over at least 100 timed steps after warmup.
Compare AdamW8bit state storage with torch.optim.AdamW under the same FSDP2 topology. The expected two-moment storage is approximately 2.03125 bytes per parameter for uint8 states plus FP32 block scales versus 8 bytes per parameter for two FP32 moments; measure rather than infer the actual per-rank and peak-memory effect.
Trace one step to confirm that the existing optimizer kernels receive local n, and that the adapter adds no host-device copies, implicit all-gather, or extra per-parameter collective.
Confirm that the unchanged plain-tensor bitsandbytes path has no material latency regression.
This is a correctness and distributed-memory-support contribution. Do not claim a B300 speedup unless the measurements independently justify one.
Acceptance
the exact-baseline first-step failure is reproduced;
patched world-size-2 FSDP2 steps complete without invalid access;
local parameter/state/absmax results satisfy the exact reference oracle across the aligned and tail matrix;
AdamW8bit and Lion8bit cover both the 8-bit and small-parameter FP32-state paths;
state is genuinely local and the measured Adam moment-state reduction is consistent with the expected approximately 74.6% reduction;
the trace shows no adapter-induced all-gather, transfer, or collective; and
plain-tensor tests and latency remain healthy.
If public to_local() views do not alias the updatable shard safely, if exact local parity fails, or if correct support requires paged/global-norm/checkpoint redesign, record a bounded no-go rather than expanding the patch.
Problem
Upstream issue #1633 reports a deterministic illegal CUDA memory access on the first
AdamW8bit.step()under PyTorch FSDP2. The issue is open, labeledContributions Welcome,FSDP, andOptimizers, and currently has no linked branch or pull request.The current optimizer runtime assumes every parameter, gradient, and state object passed to the native kernels is a plain tensor. Under FSDP2, the optimizer key is a
DTensor: state allocation throughzeros_like(p)preserves the wrapper, whileupdate_step()ultimately passes wrapper-backed pointers and a local gradient element count into the existing CUDA optimizer kernel. The detailed upstream diagnosis shows that both plain local state allocation and passing the parameter's local tensor to the kernel are required; either change alone still fails.This is a public distributed-training correctness gap, not a kernel-tuning hypothesis. The goal is to reuse the existing optimizer kernels safely on each FSDP2 local shard while preserving the global
DTensorparameter as the optimizer key.Bounded Scope
Investigate a Python-only runtime fix in
bitsandbytes/optim/optimizer.pyplus focused distributed tests:DTensorthrough public, lazily imported PyTorch APIs;min_8bit_sizeselection based on the global parameter size;to_local()to obtain the contiguous, nonempty local parameter and gradient views at the native-kernel boundary;state1/state2and 256-elementabsmaxarrays as plain tensors sized for that local shard; andThe supported claim is deliberately narrow: ordinary contiguous FSDP2
Shard(0)parameters on the non-pagedOptimizer1StateandOptimizer2Stateruntime paths withmax_unorm == 0, validated throughAdamW8bitandLion8bit, including their small-parameter FP32-state fallback.Explicit Non-Goals
max_unorm > 0path requiring distributed-global norm semantics;AdEMAMix, which owns separate state/update overrides;Linear4bit, or the separate issue FSDP2 fully_shard breaks Linear4bit forward via NaN canonicalization of packed-NF4 bf16 storage bitsandbytes-foundation/bitsandbytes#1945.Unsupported DTensor combinations must not be passed as raw wrapper pointers to native kernels. If encountered during implementation, fail clearly rather than silently copy/update detached storage or expand this issue.
Correctness Plan
torchrunon at least two B300 GPUs andCUDA_LAUNCH_BLOCKING=1; the first optimizer step must reach the documented native illegal access.AdamW8bitandLion8bitat world sizes 1 and 2; world size 4 is useful when available but not required for the minimum patch.min_8bit_size; aligned shards; local tails around 255/256/257 and 4095/4096/4097; and multiple steps.ceil(local_numel / 256)absmax sizing.B300 Validation and System Evidence
Use one multi-B300 Slurm allocation with recorded node/GPU/driver/CUDA/PyTorch/bitsandbytes identities and exact commands. Do not run GPU work on the login node.
AdamW8bit; addLion8bitas the one-state coverage path.torch.optim.AdamWunder the same FSDP2 topology. The expected two-moment storage is approximately 2.03125 bytes per parameter for uint8 states plus FP32 block scales versus 8 bytes per parameter for two FP32 moments; measure rather than infer the actual per-rank and peak-memory effect.n, and that the adapter adds no host-device copies, implicit all-gather, or extra per-parameter collective.This is a correctness and distributed-memory-support contribution. Do not claim a B300 speedup unless the measurements independently justify one.
Acceptance
If public
to_local()views do not alias the updatable shard safely, if exact local parity fails, or if correct support requires paged/global-norm/checkpoint redesign, record a bounded no-go rather than expanding the patch.Baseline:
95f9af309d4d5793847169c39288dcd3fcbdf564.