Skip to content

Benchmark stream-ordered lookahead prefetch for CUDA paged optimizers on B300 #23

Description

@heiheiha798

Summary

This issue tracks a measurement-gated B300 experiment for CUDA paged optimizers. The current optimizer loop executes each active parameter as prefetch_state -> update_step -> device synchronize, while the native cudaMemPrefetchAsync wrapper always uses stream 0. That serializes managed-state migration and optimizer compute even though paged state is independent across parameters.

The hypothesis is that a one-parameter, event-ordered lookahead pipeline can overlap migration of parameter i + 1 with the unchanged optimizer kernel for parameter i, reducing steady-state paged-optimizer step time on B300 without changing optimizer math or the completed-step contract.

This is a hypothesis, not a measured performance claim.

Current behavior

  • Optimizer8bit.step() calls prefetch_state(p), update_step(...), and sync_gpu(p) for every active parameter.
  • functional.prefetch_tensor() exposes no stream argument.
  • cprefetch() invokes cudaMemPrefetchAsync(..., stream=0) for both pre-CUDA-13 and CUDA-13 runtime signatures.
  • Optimizer kernels run on PyTorch's current CUDA stream.

As a result, the next parameter's managed state cannot be deliberately prefetched on an independent stream while the current parameter update is running.

Proposed bounded experiment

  1. Keep the existing cprefetch symbol and behavior intact. Add an internal stream-aware CUDA prefetch entry point that accepts an explicit raw stream and handles both CUDA <13 and CUDA >=13 signatures.
  2. For a single-device CUDA paged optimizer, lazily own one prefetch stream per device. Enqueue state1 and, when present, state2 for the active parameter, then record one event after both requests.
  3. Prime the first active paged parameter. Before each update, make the user current stream wait for that parameter's prefetch event. Launch the unchanged optimizer update, then enqueue the next active paged parameter's state on the prefetch stream so migration can overlap current compute.
  4. Preserve the final synchronization before step() returns. Preserve the existing sequential path for non-paged optimizers, non-paged state tensors, unsupported concurrent-managed-access devices/backends, one-active-parameter cases, capture-unsafe execution, and multi-device parameter groups not covered by the experiment.
  5. Keep lookahead depth exactly one. Any minimum-size or capability guard must be selected from complete B300 evidence rather than assumed.

The implementation must not add multi-tensor optimizer kernels, change optimizer arithmetic or state formats, add eviction policy, alter checkpoint/FSDP behavior, or broaden into non-paged synchronization cleanup.

Expected scope

  • bitsandbytes/functional.py: internal stream-aware paged prefetch wrapper while retaining the existing wrapper.
  • bitsandbytes/optim/optimizer.py: paged-only one-item lookahead and per-device stream/event ownership.
  • csrc/pythonInterface.cpp: additive raw-stream prefetch wrapper with CUDA-version handling and unchanged legacy ABI.
  • Focused paged optimizer tests and a reproducible benchmark/profile harness.

No optimizer CUDA kernel or public constructor/API change is expected.

Correctness oracle

  • Compare upstream and candidate from identical seeded parameters and gradient sequences for paged Adam/AdamW, Lion, and AdEMAMix, covering 8-bit and 32-bit state where supported, one-state/two-state/double-buffer layouts, FP16/BF16/FP32 parameters, and 5-20 steps. Require bitwise equality of parameters and all state tensors.
  • Cover heterogeneous parameter lists, the 100k-element paging boundary, absent/reappearing gradients, one active parameter, and mixed paged/non-paged state.
  • Exercise a non-default current stream with gradients produced immediately before step(). Verify event ordering and immediate visibility after step() returns.
  • Verify that the event follows all state prefetch requests and that the update stream waits before dereferencing managed state.
  • Preserve the existing path for non-paged optimizers, unsupported concurrent-managed-access devices/backends, capture-unsafe execution, and unsupported multi-device groups.
  • Run focused paged optimizer tests, relevant functional tests, available CUDA 12/13 builds, compute-sanitizer checks where applicable, and full pre-commit. All CUDA/GPU work runs through Slurm.

B300 benchmark and profiler gate

Use one B300 allocation with isolated upstream/candidate builds in the same software environment. Exclude allocation and first-step initialization, warm up at least 10 steady-state steps, and time at least 50 steps with CUDA events plus host-wall confirmation. Report median and p10/p90.

Primary cases:

  • PagedAdamW8bit and PagedAdamW32bit; PagedLion8bit as a one-state control.
  • 8-64 heterogeneous parameters with per-state working sets around 2, 8, 32, 64, and 256 MiB.
  • Fully resident managed state as a regression control.
  • Reproducibly host-resident state established before timing by CPU page touch.
  • An oversubscribed working set only when runtime HBM and host-memory limits permit it.
  • A Transformers-style heterogeneous parameter distribution as the public integration control.

Nsight Systems must show prefetch of state i + 1 overlapping optimizer kernel i, correct event waits, and removal of intermediate device-wide barriers only in the selected paged path. Record managed-memory HtoD bytes, effective migration bandwidth, overlap fraction, page faults, and peak HBM.

Proceed only if:

  • all correctness and ordering checks pass;
  • host-resident PagedAdamW 8/32-bit cells show at least 15% geomean steady-state step improvement;
  • the public heterogeneous workload retains a repeatable gain;
  • no primary paging cell regresses by more than 5%;
  • resident-path regression is at most 3%; and
  • peak memory and completed-step semantics are unchanged.

If gains come only from removing synchronization without demonstrated migration/compute overlap, only from tiny tensors, or disappear in the public-style workload, record a no-go rather than expand scope.

Risks and non-goals

  • Migration may contend with optimizer kernels for HBM or interconnect bandwidth.
  • One-step lookahead can evict useful pages under severe oversubscription.
  • Streams/events must be per device and long-lived; incorrect device ownership is a correctness bug.
  • CUDA graph capture and non-CUDA backends may require retaining the existing path.
  • This does not change non-paged synchronization, optimizer kernels/math, state formats, FSDP/checkpoint behavior, multi-device scheduling, or UVM eviction policy.

No open upstream issue or PR was found that adds a raw-stream cudaMemPrefetchAsync wrapper or an event-ordered lookahead scheduler for the existing CUDA paged optimizers.

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