Skip to content

[LoRA] MoE-LoRA CUDA-graph fixes under DP attention + RL adapter-reload fixes - #31525

Open
yushengsu-thu wants to merge 2 commits into
mainfrom
yusheng/lora-moe-cg-dp-attention
Open

[LoRA] MoE-LoRA CUDA-graph fixes under DP attention + RL adapter-reload fixes#31525
yushengsu-thu wants to merge 2 commits into
mainfrom
yusheng/lora-moe-cg-dp-attention

Conversation

@yushengsu-thu

@yushengsu-thu yushengsu-thu commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

Motivation

With LoRA enabled under DP attention (#31520), MoE-LoRA still breaks with CUDA graphs: the per-token routing buffers (token_lora_mapping, token_mask, weight_indices_long) are sized for the per-rank batch, but MoE layers under DP attention run on the DP-gathered token count — CUDA-graph capture crashes (assert x.shape[-1] == K in the LoRA shrink kernel on main) or reads out of bounds. Separately, colocated RL flows that unload + reload adapters every step leak memory-pool buffer slots, and DeepSeek V4 is rejected by the DSA-indexer LoRA buffer init.

Built on top of #31520 (its commit is included; only the last commit is new to this PR). Ports sglang-miles #29874 to main.

Modifications

  • Gathered-token buffer sizing: new get_gathered_moe_num_tokens() in lora/backend/base_backend.py (upper bound from global_num_tokens_cpu with attn-tp/context-parallel ceil_align); init_cuda_graph_moe_buffers allocates for max_bs * attention_dp_size.
  • Eager demotion: prepare_lora_batch demotes a graph-eligible batch to the eager prep path when the gathered length exceeds the static buffers (other DP ranks carrying more tokens).
  • Single-adapter tail stamping: idle-rank and active-rank stamps so cross-rank gathered tokens still receive the LoRA delta when exactly one adapter is loaded (max_loras_per_batch = 1, the RL rollout configuration). Pinned by the new unit test test/registered/unit/lora/test_moe_lora_tail_stamp.py (6 cases).
  • RL adapter-reload fix: LoRAMemoryPool.free_lora(uid) releases the buffer slot + bookkeeping + eviction-policy entry on unload_lora_adapter, so per-step unload/load with a fresh uid re-copies fresh weights into the same CUDA-graph-captured buffer.
  • DeepSeek V4: get_dsa_index_n_heads accepts V4 so indexer-target LoRA buffer init works.

Accuracy Tests

On 8x H200 (toy fzyzcjy/Qwen3-30B-A3B-5layer, synthetic adapter, --tp 8 --dp 4 --enable-dp-attention --enable-lora --max-loras-per-batch 1, CUDA graphs on):

  • A/B: identical launch on main (plus [LoRA] Support LoRA under DP attention: idle-forward guards, attn-TP-local slicing, per-rank tensor serialization #31520 alone) dies during CUDA-graph capture with assert x.shape[-1] == K (sgemm_lora_a.py:143); with this PR, capture completes on all ranks and the server serves. Burst of 32 concurrent LoRA requests (gathered length > per-rank): no IMA, no crash.
  • Idle-rank correctness: single-request stream on the dp4 server (3/4 ranks idle → tail-stamp path) produces LoRA output token-exact vs a dp1 --tp 8 reference on the primary rank; base outputs are token-identical across all ranks and both topologies. (Non-primary dp ranks show numeric divergence at later tokens — same behavior as the source branch, documented inherited gap for multi-rank gathered MoE-LoRA.)
  • RL reload loop: 5x /unload_lora_adapter + /load_lora_adapter (fresh name each iteration) + generate: every iteration succeeds, the slot is freed and reused (no "no free slot"), outputs per rank exactly reproducible.
  • test/registered/unit/lora/test_moe_lora_tail_stamp.py: 6/6. test/registered/lora/test_moe_lora_info.py: 3/3 (CUDA). test/registered/lora/test_virtual_experts_kernels.py: 13 passed, 5 skipped.

Checklist


CI States

Latest PR Test (Base): ❌ Run #29554459361
Latest PR Test (Extra): ❌ Run #29554459159

nanjiangwill and others added 2 commits July 16, 2026 18:05
…cal slicing, per-rank tensor serialization

Skip LoRA application on DP-attention idle forwards via a None batch_info
guard in every LoRA-wrapped layer, and clear stale batch info on idle batches.
Slice LoRA A/B weights by the base layer's attn_tp-local rank and size the
memory-pool buffers of attention modules by attn_tp_size.
Relax the dp_size == 1 asserts for dynamic LoRA load/unload when DP attention
is enabled, and ship load_lora_adapter_from_tensors payloads as one serialized
copy per TP rank, mirroring update_weights_from_tensor.
Derive CUDA-graph MoE LoRA buffer dims from the base FusedMoE so all quant
schemes work.

Ported from the sglang-miles branch (original PR #25141).
…ad fixes

Size MoE-LoRA routing buffers for the DP-gathered token count via
get_gathered_moe_num_tokens (cuda-graph buffers of max_bs*attn_dp_size), demote
to eager prep when the gathered length exceeds them, and stamp the gathered
tail with the single loaded adapter (idle-rank / active-rank stamps).
Free the memory-pool slot on unload for colocate-RL per-step adapter reloads,
fix per-expert A/B init under experts_shared_outer_loras, drop the unsafe
virtual-experts routing trim, and allow DeepSeek V4 in get_dsa_index_n_heads.

Ported from the sglang-miles branch (original PR #29874).
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

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.

3 participants