[CP] Migrate MLA prefill CP (DeepSeek V3) to CP-v2 zigzag strategy (#31619)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
639261f7b2
commit
7a896215e7
@@ -995,11 +995,18 @@ class FlashAttentionBackend(AttentionBackend):
|
|||||||
else forward_batch.encoder_out_cache_loc
|
else forward_batch.encoder_out_cache_loc
|
||||||
)
|
)
|
||||||
if self.use_mla:
|
if self.use_mla:
|
||||||
# MLA: under CP, k and k_rope arrive full-sequence
|
if is_cp_v2_active(forward_batch):
|
||||||
# (rebuild_cp_kv_cache ran upstream in
|
# CP-v2: k/k_rope are rank-local; the strategy gathers
|
||||||
# forward_absorb_prepare); rank-local otherwise.
|
# the latent to full sequence and writes it.
|
||||||
# out_cache_loc is never zigzag-split, so the write
|
cp_strategy = get_cp_strategy()
|
||||||
# lands in the right slots on every rank in either case.
|
assert cp_strategy is not None
|
||||||
|
cp_strategy.materialize_full_mla_kv(
|
||||||
|
forward_batch, layer, k, k_rope
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# CP-v1: k/k_rope arrive full-sequence (rebuild_cp_kv_cache
|
||||||
|
# ran upstream); rank-local when CP is off. out_cache_loc is
|
||||||
|
# never zigzag-split, so the write lands in the right slots.
|
||||||
self.token_to_kv_pool.set_mla_kv_buffer(
|
self.token_to_kv_pool.set_mla_kv_buffer(
|
||||||
layer,
|
layer,
|
||||||
cache_loc,
|
cache_loc,
|
||||||
@@ -1438,6 +1445,17 @@ class FlashAttentionBackend(AttentionBackend):
|
|||||||
ver=self.fa_impl_ver,
|
ver=self.fa_impl_ver,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if is_cp_v2_active(forward_batch):
|
||||||
|
cp_strategy = get_cp_strategy()
|
||||||
|
assert cp_strategy is not None
|
||||||
|
o = cp_strategy.run_attention(
|
||||||
|
q_fused,
|
||||||
|
forward_batch,
|
||||||
|
self.device,
|
||||||
|
_mla_cp_attn,
|
||||||
|
attention_backend=CPAttentionBackendKind.FLASH_ATTENTION,
|
||||||
|
)
|
||||||
|
else:
|
||||||
o = cp_attn_forward_extend(
|
o = cp_attn_forward_extend(
|
||||||
forward_batch, q_fused, self.device, _mla_cp_attn
|
forward_batch, q_fused, self.device, _mla_cp_attn
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ if TYPE_CHECKING:
|
|||||||
CP_V2_DEFAULT_MODEL_CLASSES = frozenset(
|
CP_V2_DEFAULT_MODEL_CLASSES = frozenset(
|
||||||
{
|
{
|
||||||
"Qwen3MoeForCausalLM",
|
"Qwen3MoeForCausalLM",
|
||||||
|
"DeepseekV3ForCausalLM",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -354,6 +354,21 @@ class ZigzagCPStrategy(ContextParallelStrategy):
|
|||||||
layer.v_scale,
|
layer.v_scale,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def materialize_full_mla_kv(
|
||||||
|
self, forward_batch, layer: Any, k_nope: Any, k_rope: Any
|
||||||
|
) -> None:
|
||||||
|
kv_lora_rank = k_nope.shape[-1]
|
||||||
|
latent = torch.cat([k_nope, k_rope], dim=-1).contiguous()
|
||||||
|
latent_full = self.gather_kv_cache(
|
||||||
|
latent, forward_batch, torch.cuda.current_stream()
|
||||||
|
)
|
||||||
|
get_token_to_kv_pool().set_mla_kv_buffer(
|
||||||
|
layer,
|
||||||
|
forward_batch.out_cache_loc,
|
||||||
|
latent_full[..., :kv_lora_rank],
|
||||||
|
latent_full[..., kv_lora_rank:],
|
||||||
|
)
|
||||||
|
|
||||||
def _all_gather_reorganized(self, x: torch.Tensor, forward_batch, stream):
|
def _all_gather_reorganized(self, x: torch.Tensor, forward_batch, stream):
|
||||||
meta = forward_batch.attn_cp_metadata
|
meta = forward_batch.attn_cp_metadata
|
||||||
max_len = meta.max_rank_len[0]
|
max_len = meta.max_rank_len[0]
|
||||||
|
|||||||
@@ -285,21 +285,7 @@ class EagerRunner(BaseRunner):
|
|||||||
model_runner.attn_backend.init_forward_metadata(forward_batch)
|
model_runner.attn_backend.init_forward_metadata(forward_batch)
|
||||||
|
|
||||||
cp_v2_active = is_cp_v2_active(forward_batch)
|
cp_v2_active = is_cp_v2_active(forward_batch)
|
||||||
forward_positions = forward_batch.positions
|
if not cp_v2_active:
|
||||||
if cp_v2_active:
|
|
||||||
prepare_cp_forward(forward_batch)
|
|
||||||
complete_hidden_states = kwargs.get("input_embeds")
|
|
||||||
if complete_hidden_states is None:
|
|
||||||
embed_layer = model_runner.model.get_input_embeddings()
|
|
||||||
complete_hidden_states = embed_layer(forward_batch.input_ids)
|
|
||||||
sharded_hidden_states, sharded_positions = cp_split_before_forward(
|
|
||||||
complete_hidden_states,
|
|
||||||
forward_batch.positions,
|
|
||||||
forward_batch,
|
|
||||||
)
|
|
||||||
kwargs["input_embeds"] = sharded_hidden_states
|
|
||||||
forward_positions = sharded_positions
|
|
||||||
else:
|
|
||||||
forward_batch.attn_cp_metadata = None
|
forward_batch.attn_cp_metadata = None
|
||||||
|
|
||||||
category = (
|
category = (
|
||||||
@@ -335,51 +321,67 @@ class EagerRunner(BaseRunner):
|
|||||||
):
|
):
|
||||||
ret = model_runner.model.forward(
|
ret = model_runner.model.forward(
|
||||||
forward_batch.input_ids,
|
forward_batch.input_ids,
|
||||||
forward_positions,
|
forward_batch.positions,
|
||||||
forward_batch,
|
forward_batch,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
)
|
)
|
||||||
elif cp_v2_active:
|
elif cp_v2_active:
|
||||||
# CP-V2: drive .model directly to gather across CP ranks before logits.
|
ret = self._execute_extend_cp_v2(forward_batch, kwargs)
|
||||||
hidden_states = model_runner.model.model(
|
|
||||||
forward_batch.input_ids,
|
|
||||||
forward_positions,
|
|
||||||
forward_batch,
|
|
||||||
input_embeds=kwargs.get("input_embeds"),
|
|
||||||
pp_proxy_tensors=kwargs.get("pp_proxy_tensors"),
|
|
||||||
)
|
|
||||||
aux_hidden_states = None
|
|
||||||
capture_aux_hidden_states = getattr(
|
|
||||||
model_runner.model, "capture_aux_hidden_states", False
|
|
||||||
)
|
|
||||||
if capture_aux_hidden_states:
|
|
||||||
hidden_states, aux_hidden_states = hidden_states
|
|
||||||
if model_runner.model.pp_group.is_last_rank:
|
|
||||||
hidden_states = cp_gather_after_forward(
|
|
||||||
hidden_states,
|
|
||||||
forward_batch,
|
|
||||||
torch.cuda.current_stream(),
|
|
||||||
)
|
|
||||||
ret = model_runner.model.logits_processor(
|
|
||||||
forward_batch.input_ids,
|
|
||||||
hidden_states,
|
|
||||||
model_runner.model.lm_head,
|
|
||||||
forward_batch,
|
|
||||||
aux_hidden_states,
|
|
||||||
)
|
|
||||||
elif capture_aux_hidden_states:
|
|
||||||
ret = hidden_states, aux_hidden_states
|
|
||||||
else:
|
|
||||||
ret = hidden_states
|
|
||||||
else:
|
else:
|
||||||
ret = model_runner.model.forward(
|
ret = model_runner.model.forward(
|
||||||
forward_batch.input_ids,
|
forward_batch.input_ids,
|
||||||
forward_positions,
|
forward_batch.positions,
|
||||||
forward_batch,
|
forward_batch,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
)
|
)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
def _execute_extend_cp_v2(
|
||||||
|
self, forward_batch: ForwardBatch, kwargs: dict
|
||||||
|
) -> Union[LogitsProcessorOutput, PPProxyTensors]:
|
||||||
|
"""CP-v2 extend: shard inputs at the model boundary, run the body on the
|
||||||
|
rank-local slice, then gather hidden states before the logits step.
|
||||||
|
"""
|
||||||
|
model = self.model_runner.model
|
||||||
|
|
||||||
|
prepare_cp_forward(forward_batch)
|
||||||
|
input_embeds = kwargs.get("input_embeds")
|
||||||
|
if input_embeds is None:
|
||||||
|
input_embeds = model.get_input_embeddings()(forward_batch.input_ids)
|
||||||
|
input_embeds, positions = cp_split_before_forward(
|
||||||
|
input_embeds, forward_batch.positions, forward_batch
|
||||||
|
)
|
||||||
|
|
||||||
|
hidden_states = model.model(
|
||||||
|
forward_batch.input_ids,
|
||||||
|
positions,
|
||||||
|
forward_batch,
|
||||||
|
input_embeds=input_embeds,
|
||||||
|
pp_proxy_tensors=kwargs.get("pp_proxy_tensors"),
|
||||||
|
)
|
||||||
|
capture_aux_hidden_states = getattr(model, "capture_aux_hidden_states", False)
|
||||||
|
aux_hidden_states = None
|
||||||
|
if capture_aux_hidden_states:
|
||||||
|
hidden_states, aux_hidden_states = hidden_states
|
||||||
|
|
||||||
|
if not model.pp_group.is_last_rank:
|
||||||
|
return (
|
||||||
|
(hidden_states, aux_hidden_states)
|
||||||
|
if capture_aux_hidden_states
|
||||||
|
else hidden_states
|
||||||
|
)
|
||||||
|
|
||||||
|
hidden_states = cp_gather_after_forward(
|
||||||
|
hidden_states, forward_batch, torch.cuda.current_stream()
|
||||||
|
)
|
||||||
|
return model.logits_processor(
|
||||||
|
forward_batch.input_ids,
|
||||||
|
hidden_states,
|
||||||
|
model.lm_head,
|
||||||
|
forward_batch,
|
||||||
|
aux_hidden_states,
|
||||||
|
)
|
||||||
|
|
||||||
def _execute_idle(
|
def _execute_idle(
|
||||||
self, forward_batch: ForwardBatch, pp_proxy_tensors=None
|
self, forward_batch: ForwardBatch, pp_proxy_tensors=None
|
||||||
) -> Union[LogitsProcessorOutput, PPProxyTensors]:
|
) -> Union[LogitsProcessorOutput, PPProxyTensors]:
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ from sglang.srt.layers.attention.dsa.utils import (
|
|||||||
is_graph_dsa_split_op_surface,
|
is_graph_dsa_split_op_surface,
|
||||||
)
|
)
|
||||||
from sglang.srt.layers.communicator import get_attn_tp_context
|
from sglang.srt.layers.communicator import get_attn_tp_context
|
||||||
|
from sglang.srt.layers.cp.utils import is_cp_v2_active
|
||||||
from sglang.srt.layers.dcp import (
|
from sglang.srt.layers.dcp import (
|
||||||
all_gather_kv_cache_for_mla_extend,
|
all_gather_kv_cache_for_mla_extend,
|
||||||
all_gather_q_for_mla_decode,
|
all_gather_q_for_mla_decode,
|
||||||
@@ -565,8 +566,13 @@ class DeepseekMLAForwardMixin:
|
|||||||
dsa_prefill_cp=dsa_prefill_cp,
|
dsa_prefill_cp=dsa_prefill_cp,
|
||||||
fuse_rope_for_trtllm_mla=fuse_rope_for_trtllm_mla,
|
fuse_rope_for_trtllm_mla=fuse_rope_for_trtllm_mla,
|
||||||
)
|
)
|
||||||
if (dsa_prefill_cp or mla_prefill_cp) and not defer_kv_gather_until_after_rope:
|
if (
|
||||||
# support allgather+rerrange
|
(dsa_prefill_cp or mla_prefill_cp)
|
||||||
|
and not defer_kv_gather_until_after_rope
|
||||||
|
and not is_cp_v2_active(forward_batch)
|
||||||
|
):
|
||||||
|
# CP-v1 gathers the latent here; CP-v2 gathers it in the attention
|
||||||
|
# backend via the strategy (materialize_full_mla_kv).
|
||||||
k_nope, k_pe = self.rebuild_cp_kv_cache(
|
k_nope, k_pe = self.rebuild_cp_kv_cache(
|
||||||
latent_cache, forward_batch, k_nope, k_pe
|
latent_cache, forward_batch, k_nope, k_pe
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ from sglang.srt.layers.attention.dsa.utils import (
|
|||||||
is_dsa_enable_prefill_cp,
|
is_dsa_enable_prefill_cp,
|
||||||
is_dsa_prefill_cp_round_robin_split,
|
is_dsa_prefill_cp_round_robin_split,
|
||||||
)
|
)
|
||||||
|
from sglang.srt.layers.cp.utils import is_cp_v2_active
|
||||||
from sglang.srt.layers.layernorm import RMSNorm
|
from sglang.srt.layers.layernorm import RMSNorm
|
||||||
from sglang.srt.layers.linear import ReplicatedLinear
|
from sglang.srt.layers.linear import ReplicatedLinear
|
||||||
from sglang.srt.layers.logits_processor import LogitsProcessor
|
from sglang.srt.layers.logits_processor import LogitsProcessor
|
||||||
@@ -251,10 +252,12 @@ class DeepseekModelNextN(nn.Module):
|
|||||||
else:
|
else:
|
||||||
hidden_states = self.eh_proj(eh_input)
|
hidden_states = self.eh_proj(eh_input)
|
||||||
|
|
||||||
use_cp = dsa_use_prefill_cp(
|
# CP-v2 shards/gathers at the eager-runner boundary instead.
|
||||||
forward_batch, self.dsa_enable_prefill_cp
|
use_cp_v1 = (
|
||||||
) or mla_use_prefill_cp(forward_batch, self.mla_enable_prefill_cp)
|
dsa_use_prefill_cp(forward_batch, self.dsa_enable_prefill_cp)
|
||||||
if use_cp:
|
or mla_use_prefill_cp(forward_batch, self.mla_enable_prefill_cp)
|
||||||
|
) and not is_cp_v2_active(forward_batch)
|
||||||
|
if use_cp_v1:
|
||||||
hidden_states = cp_split_and_rebuild_data(forward_batch, hidden_states)
|
hidden_states = cp_split_and_rebuild_data(forward_batch, hidden_states)
|
||||||
positions = cp_split_and_rebuild_position(forward_batch, positions)
|
positions = cp_split_and_rebuild_position(forward_batch, positions)
|
||||||
residual = None
|
residual = None
|
||||||
@@ -285,7 +288,7 @@ class DeepseekModelNextN(nn.Module):
|
|||||||
else:
|
else:
|
||||||
hidden_states = self.shared_head.norm(hidden_states)
|
hidden_states = self.shared_head.norm(hidden_states)
|
||||||
|
|
||||||
if use_cp:
|
if use_cp_v1:
|
||||||
local_num_tokens = hidden_states.shape[0]
|
local_num_tokens = hidden_states.shape[0]
|
||||||
hidden_states = cp_all_gather_rerange_output(
|
hidden_states = cp_all_gather_rerange_output(
|
||||||
hidden_states,
|
hidden_states,
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ from sglang.srt.layers.communicator_dsa_cp import (
|
|||||||
DSACPLayerCommunicator,
|
DSACPLayerCommunicator,
|
||||||
maybe_prefetch_next_full_attention_kv,
|
maybe_prefetch_next_full_attention_kv,
|
||||||
)
|
)
|
||||||
|
from sglang.srt.layers.cp.utils import is_cp_v2_active
|
||||||
from sglang.srt.layers.dcp.planner import (
|
from sglang.srt.layers.dcp.planner import (
|
||||||
prepare_decode_context_parallel_metadata,
|
prepare_decode_context_parallel_metadata,
|
||||||
)
|
)
|
||||||
@@ -2554,9 +2555,13 @@ class DeepseekV2Model(nn.Module):
|
|||||||
else None
|
else None
|
||||||
)
|
)
|
||||||
|
|
||||||
if dsa_use_prefill_cp(
|
# CP-v2 shards/gathers at the eager-runner boundary instead.
|
||||||
forward_batch, self.dsa_enable_prefill_cp
|
use_cp_v1 = (
|
||||||
) or mla_use_prefill_cp(forward_batch, self.mla_enable_prefill_cp):
|
dsa_use_prefill_cp(forward_batch, self.dsa_enable_prefill_cp)
|
||||||
|
or mla_use_prefill_cp(forward_batch, self.mla_enable_prefill_cp)
|
||||||
|
) and not is_cp_v2_active(forward_batch)
|
||||||
|
|
||||||
|
if use_cp_v1:
|
||||||
if self.pp_group.is_first_rank:
|
if self.pp_group.is_first_rank:
|
||||||
hidden_states = cp_split_and_rebuild_data(forward_batch, hidden_states)
|
hidden_states = cp_split_and_rebuild_data(forward_batch, hidden_states)
|
||||||
positions = cp_split_and_rebuild_position(forward_batch, positions)
|
positions = cp_split_and_rebuild_position(forward_batch, positions)
|
||||||
@@ -2659,10 +2664,7 @@ class DeepseekV2Model(nn.Module):
|
|||||||
else:
|
else:
|
||||||
hidden_states, _ = self.norm(hidden_states, residual)
|
hidden_states, _ = self.norm(hidden_states, residual)
|
||||||
|
|
||||||
if self.pp_group.is_last_rank and (
|
if self.pp_group.is_last_rank and use_cp_v1:
|
||||||
dsa_use_prefill_cp(forward_batch, self.dsa_enable_prefill_cp)
|
|
||||||
or mla_use_prefill_cp(forward_batch, self.mla_enable_prefill_cp)
|
|
||||||
):
|
|
||||||
# allgather + rerrange
|
# allgather + rerrange
|
||||||
hidden_states = cp_all_gather_rerange_output(
|
hidden_states = cp_all_gather_rerange_output(
|
||||||
hidden_states,
|
hidden_states,
|
||||||
|
|||||||
Reference in New Issue
Block a user