Skip to content

[DeepSeek-V4] Offload fp8 wo_a dequant to GPU to speed up weight loading - #2

Open
AMD-yanfeiwang wants to merge 2 commits into
mainfrom
fix/deepseek-v4-wo-a-dequant-gpu
Open

[DeepSeek-V4] Offload fp8 wo_a dequant to GPU to speed up weight loading#2
AMD-yanfeiwang wants to merge 2 commits into
mainfrom
fix/deepseek-v4-wo-a-dequant-gpu

Conversation

@AMD-yanfeiwang

@AMD-yanfeiwang AMD-yanfeiwang commented Jun 19, 2026

Copy link
Copy Markdown
Owner

Summary

When SGLANG_OPT_FP8_WO_A_GEMM is disabled, the fp8 wo_a weights of DeepSeek-V4 are dequantized to bf16 at load time in _dequant_fp8 (python/sglang/srt/models/deepseek_v4.py). Because the checkpoint tensors are loaded on the host, this dequant ran on the CPU, making weight loading CPU-bound and very slow for large models.

Observed on an 8x MI355 node serving DeepSeek-V4-Pro (tp=8): after the shards finish loading, startup stalls for several minutes on Execute dequant fp8 wo_a with each TP scheduler pinned at ~30% CPU and the GPUs sitting at 0% utilization.

Fix

  • Move the elementwise fp8 -> bf16 multiply to the current CUDA device when one is available, then move the result back to the input device.
  • Downstream weight-loading behavior is unchanged (the returned tensor keeps the same device/dtype contract as before, just bf16).
  • Uses torch.cuda.current_device() so it stays correct under tensor parallelism, where each rank is bound to its own GPU (avoids piling every rank onto cuda:0).
  • Falls back to the original CPU path when CUDA is unavailable.

The dequant is a trivially parallel elementwise operation, so offloading it to the GPU turns a multi-minute, single-threaded CPU step into a sub-second one.

Test plan

  • Launch DeepSeek-V4 with SGLANG_OPT_FP8_WO_A_GEMM=false and confirm weight loading no longer stalls on Execute dequant fp8 wo_a.
  • Confirm server reaches healthy state and produces correct outputs (parity with the previous CPU dequant path).
  • Sanity check SGLANG_OPT_FP8_WO_A_GEMM=true path is unaffected.

CI States

Latest PR Test (Base): ❌ Run #27998902302
Latest PR Test (Extra): ❌ Run #27998902194

When SGLANG_OPT_FP8_WO_A_GEMM is disabled, the fp8 `wo_a` weights are
dequantized to bf16 at load time via `_dequant_fp8`. This ran on the host
because the checkpoint tensors are loaded on CPU, making weight loading
CPU-bound and very slow for large models like DeepSeek-V4 (the dequant is a
single-threaded elementwise multiply over large tensors while the GPUs sit
idle).

Move the elementwise multiply to the current CUDA device when available and
move the result back to the input device, so downstream weight-loading
behavior is unchanged while the heavy math is GPU-accelerated. Using
`torch.cuda.current_device()` keeps this correct under tensor parallelism
where each rank binds to its own device.
…/...)

Address review: the previous condition gated on torch.cuda.is_available(), so
only CUDA/HIP benefited and NPU (and XPU/HPU) fell back to the slow CPU dequant.

Offload any host (CPU) tensor to the current accelerator via the device-agnostic
get_device() helper (already used across the repo, and consistent with the
torch.get_device_module() usage in this file), then move the result back to the
input device. This covers CUDA/HIP/NPU/XPU/... and resolves to the current rank's
device under TP; on a CPU-only host get_device() returns "cpu" so the move is a
no-op.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant