[CP] 1/N: Support MLA Prefill Context Parallel (#23292)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
81cd338fcc
commit
b0ce16d0c5
@@ -508,6 +508,25 @@ class FlashAttentionBackend(AttentionBackend):
|
||||
metadata.cu_seqlens_k = torch.nn.functional.pad(
|
||||
torch.cumsum(seqlens_in_batch, dim=0, dtype=torch.int32), (1, 0)
|
||||
)
|
||||
|
||||
# MLA/MHA CP: prepare_mlp_sync_batch pads extend tokens up to
|
||||
# lcm(attn_tp_size, attn_cp_size), so cache_seqlens_cp can exceed
|
||||
# seq_lens_cpu.max(). Widen page_table by the pad delta to keep
|
||||
# FA3's causal reads in-bounds; widened columns index KV slot 0
|
||||
# (req_to_token is zero-init) and outputs for padding queries are
|
||||
# discarded downstream.
|
||||
if (
|
||||
self.attn_cp_size > 1
|
||||
and forward_batch.global_num_tokens_cpu is not None
|
||||
and forward_batch.extend_num_tokens is not None
|
||||
and forward_batch.extend_seq_lens_cpu is not None
|
||||
):
|
||||
padded_extend = int(forward_batch.extend_num_tokens)
|
||||
real_extend = int(sum(forward_batch.extend_seq_lens_cpu))
|
||||
pad_delta = padded_extend - real_extend
|
||||
if pad_delta > 0:
|
||||
metadata.max_seq_len_k += pad_delta
|
||||
|
||||
metadata.page_table = self.req_to_token_pool.req_to_token[
|
||||
forward_batch.req_pool_indices, : metadata.max_seq_len_k
|
||||
]
|
||||
@@ -627,36 +646,43 @@ class FlashAttentionBackend(AttentionBackend):
|
||||
k_rope: Optional[torch.Tensor] = None,
|
||||
sinks: Optional[torch.Tensor] = None,
|
||||
):
|
||||
is_cp_mode = (
|
||||
forward_batch.forward_mode.is_context_parallel_extend()
|
||||
and forward_batch.attn_cp_metadata is not None
|
||||
and self.attn_cp_size > 1
|
||||
)
|
||||
|
||||
if k is not None:
|
||||
assert v is not None
|
||||
|
||||
is_cp_mode = (
|
||||
forward_batch.forward_mode.is_context_parallel_extend()
|
||||
and forward_batch.attn_cp_metadata is not None
|
||||
and self.attn_cp_size > 1
|
||||
)
|
||||
|
||||
if save_kv_cache and not is_cp_mode and not self.fa_skip_kv_cache:
|
||||
if save_kv_cache and not self.fa_skip_kv_cache:
|
||||
cache_loc = (
|
||||
forward_batch.out_cache_loc
|
||||
if not layer.is_cross_attention
|
||||
else forward_batch.encoder_out_cache_loc
|
||||
)
|
||||
if not self.use_mla:
|
||||
self.token_to_kv_pool.set_kv_buffer(
|
||||
layer, cache_loc, k, v, layer.k_scale, layer.v_scale
|
||||
)
|
||||
else:
|
||||
if self.use_mla:
|
||||
# MLA: under CP, k and k_rope arrive full-sequence
|
||||
# (rebuild_cp_kv_cache ran upstream in
|
||||
# forward_absorb_prepare); rank-local otherwise.
|
||||
# out_cache_loc is never zigzag-split, so the write
|
||||
# lands in the right slots on every rank in either case.
|
||||
self.token_to_kv_pool.set_mla_kv_buffer(
|
||||
layer,
|
||||
cache_loc,
|
||||
k,
|
||||
k_rope,
|
||||
)
|
||||
if is_cp_mode:
|
||||
cp_allgather_and_save_kv_cache(
|
||||
forward_batch, layer, k, v, self.attn_cp_size
|
||||
)
|
||||
elif is_cp_mode:
|
||||
# Dense-MHA CP: k, v are still rank-local; backend
|
||||
# all-gathers and writes to the per-rank pool.
|
||||
cp_allgather_and_save_kv_cache(
|
||||
forward_batch, layer, k, v, self.attn_cp_size
|
||||
)
|
||||
else:
|
||||
self.token_to_kv_pool.set_kv_buffer(
|
||||
layer, cache_loc, k, v, layer.k_scale, layer.v_scale
|
||||
)
|
||||
|
||||
# Use precomputed metadata across all layers
|
||||
metadata = self.forward_metadata
|
||||
@@ -974,57 +1000,103 @@ class FlashAttentionBackend(AttentionBackend):
|
||||
q_nope = q_all[:, :, : layer.v_head_dim]
|
||||
q_rope = q_all[:, :, layer.v_head_dim :]
|
||||
|
||||
result = flash_attn_with_kvcache(
|
||||
q=q_rope,
|
||||
k_cache=k_rope_cache,
|
||||
v_cache=c_kv_cache,
|
||||
qv=q_nope,
|
||||
page_table=page_table,
|
||||
cache_seqlens=cache_seqlens,
|
||||
cu_seqlens_q=cu_seqlens_q,
|
||||
cu_seqlens_k_new=cu_seqlens_k if not use_local_attn else None,
|
||||
max_seqlen_q=max_seqlen_q,
|
||||
softmax_scale=layer.scaling,
|
||||
causal=False if use_cascade_attn else causal,
|
||||
softcap=layer.logit_cap,
|
||||
k_descale=k_descale,
|
||||
v_descale=v_descale,
|
||||
return_softmax_lse=use_cascade_attn,
|
||||
num_splits=self.num_splits,
|
||||
ver=self.fa_impl_ver,
|
||||
)
|
||||
if use_cascade_attn:
|
||||
o, softmax_lse, *rest = result
|
||||
o_expand, softmax_lse_expand, *rest_expand = (
|
||||
flash_attn_with_kvcache(
|
||||
q=q_rope,
|
||||
if is_cp_mode:
|
||||
# MLA CP: q is rank-local zigzag-split; run the
|
||||
# absorbed-MLA kernel twice (prev/next halves) against
|
||||
# the full latent KV pool (which rebuild_cp_kv_cache
|
||||
# populated upstream) via cp_attn_forward_extend.
|
||||
# Concat q_nope + q_rope along dim=-1 so the wrapper's
|
||||
# chunk(2, dim=0) keeps their alignment; split back
|
||||
# inside the closure.
|
||||
assert (
|
||||
not use_cascade_attn
|
||||
), "Cascade attention under MLA CP is not supported in v1."
|
||||
q_fused = torch.cat([q_nope, q_rope], dim=-1)
|
||||
|
||||
def _mla_cp_attn(
|
||||
q_chunk,
|
||||
cu_seqlens_q_cp,
|
||||
cache_seqlens_cp,
|
||||
max_seqlen_q_cp,
|
||||
):
|
||||
q_nope_chunk = q_chunk[..., : layer.v_head_dim]
|
||||
q_rope_chunk = q_chunk[..., layer.v_head_dim :]
|
||||
return flash_attn_with_kvcache(
|
||||
q=q_rope_chunk,
|
||||
qv=q_nope_chunk,
|
||||
k_cache=k_rope_cache,
|
||||
v_cache=c_kv_cache,
|
||||
qv=q_nope,
|
||||
page_table=self.forward_metadata_spec_decode_expand.page_table,
|
||||
cache_seqlens=self.forward_metadata_spec_decode_expand.cache_seqlens_int32,
|
||||
cu_seqlens_q=self.forward_metadata_spec_decode_expand.cu_seqlens_q,
|
||||
cu_seqlens_k_new=self.forward_metadata_spec_decode_expand.cu_seqlens_k,
|
||||
max_seqlen_q=self.forward_metadata_spec_decode_expand.max_seq_len_q,
|
||||
page_table=page_table,
|
||||
cache_seqlens=cache_seqlens_cp,
|
||||
cu_seqlens_q=cu_seqlens_q_cp,
|
||||
cu_seqlens_k_new=(
|
||||
cu_seqlens_k if not use_local_attn else None
|
||||
),
|
||||
max_seqlen_q=max_seqlen_q_cp,
|
||||
softmax_scale=layer.scaling,
|
||||
causal=False,
|
||||
window_size=window_size,
|
||||
causal=causal,
|
||||
softcap=layer.logit_cap,
|
||||
k_descale=k_descale,
|
||||
v_descale=v_descale,
|
||||
return_softmax_lse=True,
|
||||
num_splits=self.num_splits,
|
||||
ver=self.fa_impl_ver,
|
||||
)
|
||||
)
|
||||
o, _ = merge_state_v2_wrapper(
|
||||
o,
|
||||
softmax_lse.T.contiguous(),
|
||||
o_expand,
|
||||
softmax_lse_expand.T.contiguous(),
|
||||
|
||||
o = cp_attn_forward_extend(
|
||||
forward_batch, q_fused, self.device, _mla_cp_attn
|
||||
)
|
||||
else:
|
||||
o = result
|
||||
result = flash_attn_with_kvcache(
|
||||
q=q_rope,
|
||||
k_cache=k_rope_cache,
|
||||
v_cache=c_kv_cache,
|
||||
qv=q_nope,
|
||||
page_table=page_table,
|
||||
cache_seqlens=cache_seqlens,
|
||||
cu_seqlens_q=cu_seqlens_q,
|
||||
cu_seqlens_k_new=cu_seqlens_k if not use_local_attn else None,
|
||||
max_seqlen_q=max_seqlen_q,
|
||||
softmax_scale=layer.scaling,
|
||||
causal=False if use_cascade_attn else causal,
|
||||
softcap=layer.logit_cap,
|
||||
k_descale=k_descale,
|
||||
v_descale=v_descale,
|
||||
return_softmax_lse=use_cascade_attn,
|
||||
num_splits=self.num_splits,
|
||||
ver=self.fa_impl_ver,
|
||||
)
|
||||
if use_cascade_attn:
|
||||
o, softmax_lse, *rest = result
|
||||
o_expand, softmax_lse_expand, *rest_expand = (
|
||||
flash_attn_with_kvcache(
|
||||
q=q_rope,
|
||||
k_cache=k_rope_cache,
|
||||
v_cache=c_kv_cache,
|
||||
qv=q_nope,
|
||||
page_table=self.forward_metadata_spec_decode_expand.page_table,
|
||||
cache_seqlens=self.forward_metadata_spec_decode_expand.cache_seqlens_int32,
|
||||
cu_seqlens_q=self.forward_metadata_spec_decode_expand.cu_seqlens_q,
|
||||
cu_seqlens_k_new=self.forward_metadata_spec_decode_expand.cu_seqlens_k,
|
||||
max_seqlen_q=self.forward_metadata_spec_decode_expand.max_seq_len_q,
|
||||
softmax_scale=layer.scaling,
|
||||
causal=False,
|
||||
window_size=window_size,
|
||||
softcap=layer.logit_cap,
|
||||
k_descale=k_descale,
|
||||
v_descale=v_descale,
|
||||
return_softmax_lse=True,
|
||||
num_splits=self.num_splits,
|
||||
ver=self.fa_impl_ver,
|
||||
)
|
||||
)
|
||||
o, _ = merge_state_v2_wrapper(
|
||||
o,
|
||||
softmax_lse.T.contiguous(),
|
||||
o_expand,
|
||||
softmax_lse_expand.T.contiguous(),
|
||||
)
|
||||
else:
|
||||
o = result
|
||||
|
||||
return o.view(-1, layer.tp_q_head_num * layer.v_head_dim)
|
||||
|
||||
|
||||
@@ -65,6 +65,10 @@ from sglang.srt.layers.moe import (
|
||||
should_use_dp_reduce_scatterv,
|
||||
should_use_flashinfer_cutlass_moe_fp4_allgather,
|
||||
)
|
||||
from sglang.srt.layers.utils.cp_utils import (
|
||||
is_mla_prefill_cp_enabled,
|
||||
mla_use_prefill_cp,
|
||||
)
|
||||
from sglang.srt.model_executor.forward_batch_info import ForwardBatch
|
||||
from sglang.srt.server_args import get_global_server_args
|
||||
from sglang.srt.speculative.spec_info import SpeculativeAlgorithm
|
||||
@@ -202,7 +206,7 @@ class ScatterMode(Enum):
|
||||
@staticmethod
|
||||
def model_input_output():
|
||||
"""The scatter mode for model forward pass input and output data"""
|
||||
if is_dsa_enable_prefill_cp():
|
||||
if is_dsa_enable_prefill_cp() or is_mla_prefill_cp_enabled():
|
||||
return ScatterMode.SCATTERED
|
||||
|
||||
return ScatterMode.TP_ATTN_FULL
|
||||
@@ -379,8 +383,10 @@ class LayerScatterModes:
|
||||
or should_use_flashinfer_cutlass_moe_fp4_allgather()
|
||||
):
|
||||
return ScatterMode.SCATTERED
|
||||
# DSA CP doesn't support MOE_FULL yet; fall back to FULL
|
||||
if is_enable_moe_cp_allgather() and not is_dsa_enable_prefill_cp():
|
||||
# DSA CP and MLA CP both don't support MOE_FULL yet; fall back to FULL.
|
||||
if is_enable_moe_cp_allgather() and not (
|
||||
is_dsa_enable_prefill_cp() or is_mla_prefill_cp_enabled()
|
||||
):
|
||||
return ScatterMode.MOE_FULL
|
||||
return ScatterMode.FULL
|
||||
else:
|
||||
@@ -709,7 +715,7 @@ class LayerCommunicator:
|
||||
return True
|
||||
if forward_batch.dp_padding_mode.is_max_len():
|
||||
return True
|
||||
if dsa_use_prefill_cp(forward_batch):
|
||||
if dsa_use_prefill_cp(forward_batch) or mla_use_prefill_cp(forward_batch):
|
||||
return True
|
||||
if get_attn_tp_context().input_scattered and not self.is_last_layer:
|
||||
return True
|
||||
|
||||
@@ -37,6 +37,7 @@ from sglang.srt.layers.dp_attention import (
|
||||
get_attention_cp_group,
|
||||
get_local_dp_buffer,
|
||||
)
|
||||
from sglang.srt.layers.utils.cp_utils import mla_use_prefill_cp
|
||||
from sglang.srt.model_executor.forward_batch_info import ForwardBatch
|
||||
|
||||
|
||||
@@ -152,7 +153,7 @@ class DSACPCommunicateWithAllReduceAndLayerNormFn(
|
||||
hidden_states, residual = layernorm(hidden_states, residual)
|
||||
# for prefill: attn tp scattered -> full
|
||||
# for decode: attn tp full -> full
|
||||
if dsa_use_prefill_cp(forward_batch):
|
||||
if dsa_use_prefill_cp(forward_batch) or mla_use_prefill_cp(forward_batch):
|
||||
assert context.attn_dp_size == 1
|
||||
hidden_states, local_hidden_states = (
|
||||
get_local_dp_buffer(get_attention_cp_group()),
|
||||
@@ -205,7 +206,7 @@ class DSACPCommunicateSummableTensorPairFn(CommunicateSummableTensorPairFn):
|
||||
):
|
||||
# for prefill: full -> attn tp scattered
|
||||
# for decode: full -> attn tp full
|
||||
if dsa_use_prefill_cp(forward_batch):
|
||||
if dsa_use_prefill_cp(forward_batch) or mla_use_prefill_cp(forward_batch):
|
||||
assert context.attn_dp_size == 1
|
||||
input_hidden_states = hidden_states
|
||||
hidden_states = hidden_states.tensor_split(context.attn_cp_size)[
|
||||
|
||||
@@ -51,19 +51,41 @@ def is_prefill_cp_in_seq_split():
|
||||
)
|
||||
|
||||
|
||||
def is_mla_prefill_cp_enabled() -> bool:
|
||||
sa = get_global_server_args()
|
||||
return sa.enable_prefill_context_parallel and sa.use_mla_backend
|
||||
|
||||
|
||||
def mla_use_prefill_cp(forward_batch, mla_enable_prefill_cp=None):
|
||||
if mla_enable_prefill_cp is None:
|
||||
mla_enable_prefill_cp = is_mla_prefill_cp_enabled()
|
||||
return (
|
||||
forward_batch.attn_cp_metadata is not None
|
||||
and mla_enable_prefill_cp
|
||||
and forward_batch.forward_mode.is_context_parallel_extend()
|
||||
)
|
||||
|
||||
|
||||
def can_cp_split(seq_len: int, cp_size: int, forward_batch):
|
||||
# CP metadata (zigzag split) only supports batch=1 for now.
|
||||
from sglang.srt.model_executor.forward_batch_info import ForwardMode
|
||||
|
||||
# TODO current just support prefill batch=1 and len(input_ids) > self.cp_size * 2
|
||||
# Note: (self.cp_size * 2) To achieve load balancing for seq computation,
|
||||
# the seq data needs to be divided and recombined at twice the size of cp_size.
|
||||
cur_cp_seq_len = seq_len // (cp_size * 2)
|
||||
if (
|
||||
return (
|
||||
cur_cp_seq_len != 0
|
||||
and cp_size > 1
|
||||
# prepare_context_parallel_metadata hard-codes bs_per_cp_group = 1;
|
||||
# guard explicitly to avoid silent mis-partitioning under continuous batching.
|
||||
# TODO: remove this guard once we support multi-batch-cp-split
|
||||
and forward_batch.batch_size == 1
|
||||
and forward_batch.forward_mode.is_context_parallel_extend()
|
||||
# is_context_parallel_extend() returns True for MIXED (prefill+decode
|
||||
# in one step), but the zigzag split only makes sense on pure extend.
|
||||
and forward_batch.forward_mode != ForwardMode.MIXED
|
||||
and is_prefill_context_parallel_enabled()
|
||||
and forward_batch.seq_lens_cpu.shape[0] == 1
|
||||
):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
)
|
||||
|
||||
|
||||
def cp_split_and_rebuild_data(forward_batch, input_: torch.Tensor):
|
||||
@@ -395,6 +417,7 @@ def prepare_context_parallel_metadata(
|
||||
cp_rank,
|
||||
cp_size,
|
||||
seqs_len,
|
||||
extend_lens,
|
||||
):
|
||||
from sglang.srt.layers.attention.dsa.utils import (
|
||||
is_dsa_prefill_cp_round_robin_split,
|
||||
@@ -449,18 +472,12 @@ def prepare_context_parallel_metadata(
|
||||
bs_per_cp_group = 1
|
||||
kv_len_origin = kv_len
|
||||
|
||||
# Derive prefix offset from the full sequence length on CPU.
|
||||
# NOTE: forward_batch.seq_lens_cpu includes cached prefix + extend tokens.
|
||||
# In CP we only split the extend tokens, but cache_seqlens passed to FA must
|
||||
# include the cached prefix.
|
||||
prefix_len = 0
|
||||
try:
|
||||
if seqs_len is not None and len(seqs_len) == 1:
|
||||
prefix_len = int(seqs_len[0]) - int(kv_len_origin.item())
|
||||
if prefix_len < 0:
|
||||
prefix_len = 0
|
||||
except Exception:
|
||||
prefix_len = 0
|
||||
# Derive prefix offset from unpadded CPU tensors. Both `seqs_len` and `extend_lens` are unpadded by the caller
|
||||
# Using the padded `kv_len` here would undercount `prefix_len` by the padding amount and shift the FA causal horizon.
|
||||
assert (
|
||||
len(seqs_len) == 1 and len(extend_lens) == 1
|
||||
), "Prefill Context Parallel only supports batch_size == 1 for now"
|
||||
prefix_len = max(0, int(seqs_len[0]) - int(extend_lens[0]))
|
||||
# get zigzag index
|
||||
cp_segment_num = cp_size * 2
|
||||
seq_per_batch = kv_len // cp_segment_num # seq_len for each batch and segment
|
||||
|
||||
@@ -56,6 +56,7 @@ from sglang.srt.layers.logits_processor import LogitsProcessorOutput
|
||||
from sglang.srt.layers.moe.token_dispatcher.deepep import DeepEPBuffer
|
||||
from sglang.srt.layers.moe.utils import get_deepep_mode, get_moe_a2a_backend
|
||||
from sglang.srt.layers.utils import MultiPlatformOp
|
||||
from sglang.srt.layers.utils.cp_utils import is_mla_prefill_cp_enabled
|
||||
from sglang.srt.model_executor.forward_batch_info import (
|
||||
CaptureHiddenMode,
|
||||
ForwardBatch,
|
||||
@@ -567,7 +568,15 @@ class CudaGraphRunner:
|
||||
|
||||
self.attn_tp_size = get_attention_tp_size()
|
||||
self.attn_tp_rank = get_attention_tp_rank()
|
||||
self.dsa_enable_prefill_cp = is_dsa_enable_prefill_cp()
|
||||
# True if a DSACPLayerCommunicator-style prefill-CP flavor is active
|
||||
# (DSA or MLA). These flavors feed a zigzag-split rank-local layout
|
||||
# into the runner; MHA-arch prefill CP (Qwen3/Qwen2 MoE via PR
|
||||
# #18233) uses the plain LayerCommunicator with an attn_tp-replicated
|
||||
# layout and is intentionally excluded so the attn_tp-local
|
||||
# num_token_non_padded adjustment still runs for it.
|
||||
self.enable_prefill_cp = (
|
||||
is_dsa_enable_prefill_cp() or is_mla_prefill_cp_enabled()
|
||||
)
|
||||
|
||||
self.deepep_adapter = DeepEPCudaGraphRunnerAdapter()
|
||||
|
||||
@@ -928,7 +937,7 @@ class CudaGraphRunner:
|
||||
if (
|
||||
enable_num_token_non_padded()
|
||||
and self.require_gathered_buffer
|
||||
and not self.dsa_enable_prefill_cp
|
||||
and not self.enable_prefill_cp
|
||||
):
|
||||
local = compute_local_num_token_non_padded(
|
||||
global_num_token_non_padded=buffers.num_token_non_padded,
|
||||
@@ -1191,7 +1200,9 @@ class CudaGraphRunner:
|
||||
seq_len_fill_value=self.seq_len_fill_value,
|
||||
require_gathered_buffer=self.require_gathered_buffer,
|
||||
num_tokens_per_bs=self.num_tokens_per_bs,
|
||||
dsa_enable_prefill_cp=self.dsa_enable_prefill_cp,
|
||||
# Parameter name retained for API stability; semantically this is
|
||||
# "any prefill-CP flavor enabled" (DSA CP or MLA CP).
|
||||
dsa_enable_prefill_cp=self.enable_prefill_cp,
|
||||
enable_num_token_non_padded_flag=enable_num_token_non_padded(),
|
||||
pp_proxy_tensors=pp_proxy_tensors,
|
||||
)
|
||||
|
||||
@@ -126,6 +126,7 @@ from sglang.srt.layers.pooler import EmbeddingPoolerOutput
|
||||
from sglang.srt.layers.quantization.fp8_kernel import fp8_dtype
|
||||
from sglang.srt.layers.sampler import create_sampler
|
||||
from sglang.srt.layers.torchao_utils import apply_torchao_config_to_model
|
||||
from sglang.srt.layers.utils.cp_utils import is_mla_prefill_cp_enabled
|
||||
from sglang.srt.lora.lora_manager import LoRAManager
|
||||
from sglang.srt.lora.lora_registry import LoRARef
|
||||
from sglang.srt.managers.schedule_batch import sanity_check_mm_pad_shift_value
|
||||
@@ -3285,11 +3286,17 @@ class ModelRunner(ModelRunnerKVCacheMixin):
|
||||
forward_batch.prepare_attn_tp_scatter_input(self)
|
||||
|
||||
# Normalize num_token_non_padded to be local to this attention TP rank if needed.
|
||||
# The skip is scoped to DSACPLayerCommunicator-style CP (DSA, MLA): those
|
||||
# flavors already feed a zigzag-split rank-local layout whose token count
|
||||
# should not be further divided by attn_tp_size. MHA-arch prefill CP
|
||||
# (Qwen3/Qwen2 MoE) keeps the attn_tp-replicated layout and wants the
|
||||
# adjustment to run — see docs/design/prefill-cp-mla.md §Phase 5.
|
||||
if (
|
||||
forward_batch.num_token_non_padded is not None
|
||||
and forward_batch.global_num_tokens_gpu is not None
|
||||
and require_gathered_buffer(self.server_args)
|
||||
and not is_dsa_enable_prefill_cp()
|
||||
and not is_mla_prefill_cp_enabled()
|
||||
):
|
||||
forward_batch.adjust_num_token_non_padded_for_attn_tp(
|
||||
server_args=self.server_args,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from sglang.srt.compilation.piecewise_context_manager import is_in_piecewise_cuda_graph
|
||||
from sglang.srt.layers.attention.tbo_backend import TboAttnBackend
|
||||
from sglang.srt.layers.utils.cp_utils import mla_use_prefill_cp
|
||||
from sglang.srt.model_executor.forward_context import get_attn_backend
|
||||
from sglang.srt.models.deepseek_common.attention_forward_methods.forward_methods import (
|
||||
AttnForwardMethod,
|
||||
@@ -74,6 +75,12 @@ def _handle_attention_backend(attn, forward_batch, backend_name):
|
||||
if is_in_piecewise_cuda_graph():
|
||||
return AttnForwardMethod.MLA
|
||||
|
||||
# MLA prefill CP forces absorbed MLA regardless of prefix length: the
|
||||
# CP path gathers latent KV via rebuild_cp_kv_cache and feeds the
|
||||
# backend's absorbed-MLA kernel.
|
||||
if mla_use_prefill_cp(forward_batch):
|
||||
return _dispatch_mla_subtype(attn, forward_batch)
|
||||
|
||||
sum_extend_prefix_lens = _get_sum_extend_prefix_lens(forward_batch)
|
||||
disable_ragged = (
|
||||
backend_name in ["flashinfer", "flashmla"]
|
||||
|
||||
@@ -13,6 +13,7 @@ from sglang.srt.layers.quantization.fp8_kernel import (
|
||||
per_tensor_quant_mla_fp8,
|
||||
per_token_group_quant_mla_deep_gemm_masked_fp8,
|
||||
)
|
||||
from sglang.srt.layers.utils.cp_utils import mla_use_prefill_cp
|
||||
from sglang.srt.lora.deepseek_mla_correction import (
|
||||
apply_q_correction as apply_kv_b_lora_q_correction,
|
||||
)
|
||||
@@ -380,7 +381,7 @@ class DeepseekMLAForwardMixin:
|
||||
):
|
||||
q_pe, k_pe = self.rotary_emb(positions, q_pe, k_pe)
|
||||
|
||||
if dsa_use_prefill_cp(forward_batch):
|
||||
if dsa_use_prefill_cp(forward_batch) or mla_use_prefill_cp(forward_batch):
|
||||
# support allgather+rerrange
|
||||
k_nope, k_pe = self.rebuild_cp_kv_cache(
|
||||
latent_cache, forward_batch, k_nope, k_pe
|
||||
|
||||
@@ -43,9 +43,12 @@ from sglang.srt.layers.logits_processor import LogitsProcessor
|
||||
from sglang.srt.layers.quantization import Fp8Config
|
||||
from sglang.srt.layers.quantization.base_config import QuantizationConfig
|
||||
from sglang.srt.layers.utils.cp_utils import (
|
||||
can_cp_split,
|
||||
cp_all_gather_rerange_output,
|
||||
cp_split_and_rebuild_data,
|
||||
cp_split_and_rebuild_position,
|
||||
is_mla_prefill_cp_enabled,
|
||||
mla_use_prefill_cp,
|
||||
prepare_context_parallel_metadata,
|
||||
)
|
||||
from sglang.srt.layers.vocab_parallel_embedding import (
|
||||
@@ -136,6 +139,14 @@ class DeepseekModelNextN(nn.Module):
|
||||
layer_name = "layers." + str(config.num_hidden_layers)
|
||||
|
||||
self.quant_config = quant_config
|
||||
self.dsa_enable_prefill_cp = is_dsa_enable_prefill_cp()
|
||||
self.mla_enable_prefill_cp = (
|
||||
is_mla_prefill_cp_enabled() and not is_deepseek_dsa(config)
|
||||
)
|
||||
if self.dsa_enable_prefill_cp or self.mla_enable_prefill_cp:
|
||||
self.cp_size = get_attention_cp_size()
|
||||
else:
|
||||
self.cp_size = None
|
||||
self.decoder = DeepseekV2DecoderLayer(
|
||||
config,
|
||||
0,
|
||||
@@ -144,15 +155,12 @@ class DeepseekModelNextN(nn.Module):
|
||||
is_nextn=True,
|
||||
prefix=add_prefix(layer_name, prefix),
|
||||
alt_stream=self.alt_stream,
|
||||
dsa_enable_prefill_cp=self.dsa_enable_prefill_cp,
|
||||
mla_enable_prefill_cp=self.mla_enable_prefill_cp,
|
||||
)
|
||||
|
||||
self.shared_head = nn.Module()
|
||||
self.shared_head.norm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
|
||||
self.dsa_enable_prefill_cp = is_dsa_enable_prefill_cp()
|
||||
if self.dsa_enable_prefill_cp:
|
||||
self.cp_size = get_attention_cp_size()
|
||||
else:
|
||||
self.cp_size = None
|
||||
|
||||
def forward(
|
||||
self,
|
||||
@@ -193,7 +201,9 @@ class DeepseekModelNextN(nn.Module):
|
||||
else:
|
||||
hidden_states = self.eh_proj(eh_input)
|
||||
|
||||
if dsa_use_prefill_cp(forward_batch, self.dsa_enable_prefill_cp):
|
||||
if dsa_use_prefill_cp(
|
||||
forward_batch, self.dsa_enable_prefill_cp
|
||||
) or mla_use_prefill_cp(forward_batch, self.mla_enable_prefill_cp):
|
||||
hidden_states = cp_split_and_rebuild_data(forward_batch, hidden_states)
|
||||
positions = cp_split_and_rebuild_position(forward_batch, positions)
|
||||
residual = None
|
||||
@@ -212,7 +222,9 @@ class DeepseekModelNextN(nn.Module):
|
||||
else:
|
||||
hidden_states = self.shared_head.norm(hidden_states)
|
||||
|
||||
if dsa_use_prefill_cp(forward_batch, self.dsa_enable_prefill_cp):
|
||||
if 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
|
||||
hidden_states = cp_all_gather_rerange_output(
|
||||
hidden_states,
|
||||
@@ -250,7 +262,8 @@ class DeepseekV3ForCausalLMNextN(DeepseekV3ForCausalLM):
|
||||
self.determine_num_fused_shared_experts("DeepseekV3ForCausalLMNextN")
|
||||
self.use_dsa = is_deepseek_dsa(config)
|
||||
self.dsa_enable_prefill_cp = is_dsa_enable_prefill_cp()
|
||||
if self.dsa_enable_prefill_cp:
|
||||
self.mla_enable_prefill_cp = is_mla_prefill_cp_enabled() and not self.use_dsa
|
||||
if self.dsa_enable_prefill_cp or self.mla_enable_prefill_cp:
|
||||
self.cp_rank = get_attention_cp_rank()
|
||||
self.cp_size = get_attention_cp_size()
|
||||
else:
|
||||
@@ -298,6 +311,16 @@ class DeepseekV3ForCausalLMNextN(DeepseekV3ForCausalLM):
|
||||
self.cp_rank,
|
||||
self.cp_size,
|
||||
forward_batch.seq_lens_cpu.tolist(),
|
||||
extend_lens=forward_batch.extend_seq_lens_cpu,
|
||||
)
|
||||
elif self.mla_enable_prefill_cp:
|
||||
if can_cp_split(len(input_ids), self.cp_size, forward_batch):
|
||||
forward_batch.attn_cp_metadata = prepare_context_parallel_metadata(
|
||||
len(input_ids),
|
||||
self.cp_rank,
|
||||
self.cp_size,
|
||||
forward_batch.seq_lens_cpu.tolist(),
|
||||
extend_lens=forward_batch.extend_seq_lens_cpu,
|
||||
)
|
||||
hidden_states = self.model(input_ids, positions, forward_batch)
|
||||
return self.logits_processor(
|
||||
|
||||
@@ -123,9 +123,12 @@ from sglang.srt.layers.radix_attention import RadixAttention
|
||||
from sglang.srt.layers.rotary_embedding import get_rope_wrapper
|
||||
from sglang.srt.layers.utils import PPMissingLayer
|
||||
from sglang.srt.layers.utils.cp_utils import (
|
||||
can_cp_split,
|
||||
cp_all_gather_rerange_output,
|
||||
cp_split_and_rebuild_data,
|
||||
cp_split_and_rebuild_position,
|
||||
is_prefill_context_parallel_enabled,
|
||||
mla_use_prefill_cp,
|
||||
prepare_context_parallel_metadata,
|
||||
)
|
||||
from sglang.srt.layers.vocab_parallel_embedding import (
|
||||
@@ -339,6 +342,8 @@ class MoEGate(nn.Module):
|
||||
is_nextn: bool = False,
|
||||
is_hash_moe: bool = False,
|
||||
is_deepseek_v4: bool = False,
|
||||
dsa_enable_prefill_cp: bool = False,
|
||||
mla_enable_prefill_cp: bool = False,
|
||||
):
|
||||
super().__init__()
|
||||
self.is_nextn = is_nextn
|
||||
@@ -368,7 +373,9 @@ class MoEGate(nn.Module):
|
||||
self.e_score_correction_bias = None
|
||||
if _is_cpu and _is_cpu_amx_available:
|
||||
self.quant_method = PackWeightMethod(weight_names=["weight"])
|
||||
self.dsa_enable_prefill_cp = is_dsa_enable_prefill_cp()
|
||||
self.use_dsa = is_deepseek_dsa(config)
|
||||
self.dsa_enable_prefill_cp = dsa_enable_prefill_cp
|
||||
self.mla_enable_prefill_cp = mla_enable_prefill_cp
|
||||
|
||||
def forward(
|
||||
self,
|
||||
@@ -390,7 +397,10 @@ class MoEGate(nn.Module):
|
||||
if (
|
||||
not self.is_deepseek_v4
|
||||
and forward_batch is not None
|
||||
and dsa_use_prefill_cp(forward_batch)
|
||||
and (
|
||||
dsa_use_prefill_cp(forward_batch, self.dsa_enable_prefill_cp)
|
||||
or mla_use_prefill_cp(forward_batch, self.mla_enable_prefill_cp)
|
||||
)
|
||||
):
|
||||
logits = F.linear(hidden_states, self.weight, None)
|
||||
else:
|
||||
@@ -442,6 +452,8 @@ class DeepseekV2MoE(nn.Module):
|
||||
alt_stream: Optional[torch.cuda.Stream] = None,
|
||||
is_nextn: bool = False,
|
||||
is_deepseek_v4: bool = False,
|
||||
dsa_enable_prefill_cp: bool = False,
|
||||
mla_enable_prefill_cp: bool = False,
|
||||
):
|
||||
super().__init__()
|
||||
self.tp_size = get_tensor_model_parallel_world_size()
|
||||
@@ -503,6 +515,8 @@ class DeepseekV2MoE(nn.Module):
|
||||
is_nextn=is_nextn,
|
||||
is_hash_moe=self.is_hash,
|
||||
is_deepseek_v4=is_deepseek_v4,
|
||||
dsa_enable_prefill_cp=dsa_enable_prefill_cp,
|
||||
mla_enable_prefill_cp=mla_enable_prefill_cp,
|
||||
)
|
||||
|
||||
# scaling factor for fused shared experts on AMD-platform.
|
||||
@@ -1340,6 +1354,8 @@ class DeepseekV2AttentionMLA(
|
||||
alt_stream: Optional[torch.cuda.Stream] = None,
|
||||
skip_rope: bool = False,
|
||||
is_nextn: bool = False,
|
||||
dsa_enable_prefill_cp: bool = False,
|
||||
mla_enable_prefill_cp: bool = False,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.layer_id = layer_id
|
||||
@@ -1354,11 +1370,14 @@ class DeepseekV2AttentionMLA(
|
||||
attn_tp_rank = get_attention_tp_rank()
|
||||
attn_tp_size = get_attention_tp_size()
|
||||
self.use_dsa = is_deepseek_dsa(config)
|
||||
self.dsa_enable_prefill_cp = is_dsa_enable_prefill_cp()
|
||||
self.dsa_enable_prefill_cp = dsa_enable_prefill_cp
|
||||
self.mla_enable_prefill_cp = mla_enable_prefill_cp
|
||||
if self.dsa_enable_prefill_cp:
|
||||
assert self.use_dsa, "CP currently only supports deepseek v3.2 model"
|
||||
# cp reuse the attn_tp comm group but need to duplicate the weights
|
||||
if self.dsa_enable_prefill_cp and self.use_dsa:
|
||||
# cp reuses the attn_tp comm group but needs to duplicate the weights;
|
||||
# store cp_size whenever either CP flavor is active so rebuild_cp_kv_cache
|
||||
# and the FA3 MLA wrapper can reach it on the dense MLA path too.
|
||||
if self.dsa_enable_prefill_cp or self.mla_enable_prefill_cp:
|
||||
self.cp_size = get_attention_cp_size()
|
||||
self.num_heads = num_heads
|
||||
assert num_heads % attn_tp_size == 0
|
||||
@@ -1793,6 +1812,8 @@ class DeepseekV2DecoderLayer(nn.Module):
|
||||
is_nextn: bool = False,
|
||||
prefix: str = "",
|
||||
alt_stream: Optional[torch.cuda.Stream] = None,
|
||||
dsa_enable_prefill_cp: bool = False,
|
||||
mla_enable_prefill_cp: bool = False,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.hidden_size = config.hidden_size
|
||||
@@ -1809,7 +1830,8 @@ class DeepseekV2DecoderLayer(nn.Module):
|
||||
self.speculative_algorithm = SpeculativeAlgorithm.from_string(
|
||||
get_global_server_args().speculative_algorithm
|
||||
)
|
||||
self.dsa_enable_prefill_cp = is_dsa_enable_prefill_cp()
|
||||
self.dsa_enable_prefill_cp = dsa_enable_prefill_cp
|
||||
self.mla_enable_prefill_cp = mla_enable_prefill_cp
|
||||
self.layer_id = layer_id
|
||||
self.is_nextn = is_nextn
|
||||
self.self_attn = DeepseekV2AttentionMLA(
|
||||
@@ -1832,6 +1854,8 @@ class DeepseekV2DecoderLayer(nn.Module):
|
||||
prefix=add_prefix("self_attn", prefix),
|
||||
alt_stream=alt_stream,
|
||||
is_nextn=is_nextn,
|
||||
dsa_enable_prefill_cp=dsa_enable_prefill_cp,
|
||||
mla_enable_prefill_cp=mla_enable_prefill_cp,
|
||||
)
|
||||
if not hasattr(config, "q_lora_rank") and envs.SGLANG_USE_AG_AFTER_QLORA.get():
|
||||
raise ValueError(
|
||||
@@ -1858,6 +1882,8 @@ class DeepseekV2DecoderLayer(nn.Module):
|
||||
layer_id=self.layer_id,
|
||||
alt_stream=alt_stream,
|
||||
is_nextn=is_nextn,
|
||||
dsa_enable_prefill_cp=dsa_enable_prefill_cp,
|
||||
mla_enable_prefill_cp=mla_enable_prefill_cp,
|
||||
)
|
||||
else:
|
||||
if enable_moe_dense_fully_dp():
|
||||
@@ -1882,7 +1908,10 @@ class DeepseekV2DecoderLayer(nn.Module):
|
||||
|
||||
self._gfx95_quant_format = self._detect_gfx95_quant_format()
|
||||
|
||||
if self.dsa_enable_prefill_cp:
|
||||
if self.dsa_enable_prefill_cp or self.mla_enable_prefill_cp:
|
||||
# DSACPLayerCommunicator is flavor-agnostic; its internal gates
|
||||
# read both dsa_use_prefill_cp and mla_use_prefill_cp. The rename
|
||||
# to CPLayerCommunicator is deferred to a cleanup PR.
|
||||
self.layer_communicator = DSACPLayerCommunicator(
|
||||
layer_scatter_modes=self.layer_scatter_modes,
|
||||
input_layernorm=self.input_layernorm,
|
||||
@@ -1998,7 +2027,10 @@ class DeepseekV2DecoderLayer(nn.Module):
|
||||
gemm_output_zero_allocator,
|
||||
)
|
||||
|
||||
if not self.dsa_enable_prefill_cp and should_allreduce_fusion:
|
||||
if (
|
||||
not (self.dsa_enable_prefill_cp or self.mla_enable_prefill_cp)
|
||||
and should_allreduce_fusion
|
||||
):
|
||||
hidden_states._sglang_needs_allreduce_fusion = True
|
||||
|
||||
if not should_allreduce_fusion:
|
||||
@@ -2096,7 +2128,10 @@ class DeepseekV2Model(nn.Module):
|
||||
self.first_k_dense_replace = config.first_k_dense_replace
|
||||
self.pp_group = get_pp_group()
|
||||
self.dsa_enable_prefill_cp = is_dsa_enable_prefill_cp()
|
||||
if self.dsa_enable_prefill_cp:
|
||||
self.mla_enable_prefill_cp = (
|
||||
is_prefill_context_parallel_enabled() and not is_deepseek_dsa(config)
|
||||
)
|
||||
if self.dsa_enable_prefill_cp or self.mla_enable_prefill_cp:
|
||||
self.cp_size = get_attention_cp_size()
|
||||
else:
|
||||
self.cp_size = None
|
||||
@@ -2129,6 +2164,8 @@ class DeepseekV2Model(nn.Module):
|
||||
quant_config=quant_config,
|
||||
prefix=prefix,
|
||||
alt_stream=self.alt_stream,
|
||||
dsa_enable_prefill_cp=self.dsa_enable_prefill_cp,
|
||||
mla_enable_prefill_cp=self.mla_enable_prefill_cp,
|
||||
),
|
||||
pp_rank=self.pp_group.rank_in_group,
|
||||
pp_size=self.pp_group.world_size,
|
||||
@@ -2255,7 +2292,9 @@ class DeepseekV2Model(nn.Module):
|
||||
else None
|
||||
)
|
||||
|
||||
if dsa_use_prefill_cp(forward_batch):
|
||||
if dsa_use_prefill_cp(
|
||||
forward_batch, self.dsa_enable_prefill_cp
|
||||
) or mla_use_prefill_cp(forward_batch, self.mla_enable_prefill_cp):
|
||||
if self.pp_group.is_first_rank:
|
||||
hidden_states = cp_split_and_rebuild_data(forward_batch, hidden_states)
|
||||
positions = cp_split_and_rebuild_position(forward_batch, positions)
|
||||
@@ -2340,7 +2379,10 @@ class DeepseekV2Model(nn.Module):
|
||||
else:
|
||||
hidden_states, _ = self.norm(hidden_states, residual)
|
||||
|
||||
if self.pp_group.is_last_rank and dsa_use_prefill_cp(forward_batch):
|
||||
if self.pp_group.is_last_rank and (
|
||||
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
|
||||
hidden_states = cp_all_gather_rerange_output(
|
||||
hidden_states,
|
||||
@@ -2418,7 +2460,10 @@ class DeepseekV2ForCausalLM(nn.Module, DeepseekV2WeightLoaderMixin):
|
||||
self.capture_aux_hidden_states = False
|
||||
|
||||
self.dsa_enable_prefill_cp = is_dsa_enable_prefill_cp()
|
||||
if self.dsa_enable_prefill_cp:
|
||||
self.mla_enable_prefill_cp = (
|
||||
is_prefill_context_parallel_enabled() and not is_deepseek_dsa(config)
|
||||
)
|
||||
if self.dsa_enable_prefill_cp or self.mla_enable_prefill_cp:
|
||||
self.cp_rank = get_attention_cp_rank()
|
||||
self.cp_size = get_attention_cp_size()
|
||||
else:
|
||||
@@ -2510,15 +2555,29 @@ class DeepseekV2ForCausalLM(nn.Module, DeepseekV2WeightLoaderMixin):
|
||||
input_embeds: torch.Tensor = None,
|
||||
pp_proxy_tensors: Optional[PPProxyTensors] = None,
|
||||
) -> torch.Tensor:
|
||||
# Minor fix for multi-modal model: input_ids is None
|
||||
len_input_ids = (
|
||||
input_ids.shape[0] if input_ids is not None else input_embeds.shape[0]
|
||||
)
|
||||
if self.dsa_enable_prefill_cp:
|
||||
if can_dsa_cp_split(
|
||||
len(input_ids), self.cp_size, self.use_dsa, forward_batch
|
||||
len_input_ids, self.cp_size, self.use_dsa, forward_batch
|
||||
):
|
||||
forward_batch.attn_cp_metadata = prepare_context_parallel_metadata(
|
||||
len(input_ids),
|
||||
len_input_ids,
|
||||
self.cp_rank,
|
||||
self.cp_size,
|
||||
forward_batch.seq_lens_cpu.tolist(),
|
||||
extend_lens=forward_batch.extend_seq_lens_cpu,
|
||||
)
|
||||
elif self.mla_enable_prefill_cp:
|
||||
if can_cp_split(len_input_ids, self.cp_size, forward_batch):
|
||||
forward_batch.attn_cp_metadata = prepare_context_parallel_metadata(
|
||||
len_input_ids,
|
||||
self.cp_rank,
|
||||
self.cp_size,
|
||||
forward_batch.seq_lens_cpu.tolist(),
|
||||
extend_lens=forward_batch.extend_seq_lens_cpu,
|
||||
)
|
||||
|
||||
with get_attn_tp_context().maybe_input_scattered(forward_batch):
|
||||
|
||||
@@ -1286,6 +1286,7 @@ class DeepseekV4ForCausalLM(nn.Module):
|
||||
self.cp_rank,
|
||||
self.cp_size,
|
||||
forward_batch.seq_lens_cpu.tolist(),
|
||||
extend_lens=forward_batch.extend_seq_lens_cpu,
|
||||
)
|
||||
if is_dsa_prefill_cp_round_robin_split():
|
||||
attn_backend = get_attn_backend()
|
||||
|
||||
@@ -249,6 +249,7 @@ class DeepseekV4ForCausalLMNextN(DeepseekV4ForCausalLM):
|
||||
self.cp_rank,
|
||||
self.cp_size,
|
||||
forward_batch.seq_lens_cpu.tolist(),
|
||||
extend_lens=forward_batch.extend_seq_lens_cpu,
|
||||
)
|
||||
if is_dsa_prefill_cp_round_robin_split():
|
||||
attn_backend = get_attn_backend()
|
||||
|
||||
@@ -7,11 +7,13 @@ import torch
|
||||
from torch import nn
|
||||
from transformers import PretrainedConfig
|
||||
|
||||
from sglang.srt.configs.model_config import is_deepseek_dsa
|
||||
from sglang.srt.distributed import get_pp_group
|
||||
from sglang.srt.layers.attention.dsa.utils import is_dsa_enable_prefill_cp
|
||||
from sglang.srt.layers.layernorm import RMSNorm
|
||||
from sglang.srt.layers.linear import RowParallelLinear
|
||||
from sglang.srt.layers.quantization.base_config import QuantizationConfig
|
||||
from sglang.srt.layers.utils.cp_utils import is_prefill_context_parallel_enabled
|
||||
from sglang.srt.layers.vocab_parallel_embedding import VocabParallelEmbedding
|
||||
from sglang.srt.model_executor.forward_batch_info import ForwardBatch, PPProxyTensors
|
||||
from sglang.srt.models.deepseek_v2 import DeepseekV2DecoderLayer, DeepseekV2Model
|
||||
@@ -36,6 +38,9 @@ class MistralLarge3EagleModel(DeepseekV2Model):
|
||||
assert get_pp_group().world_size == 1
|
||||
self.pp_group = get_pp_group()
|
||||
self.dsa_enable_prefill_cp = is_dsa_enable_prefill_cp()
|
||||
self.mla_enable_prefill_cp = (
|
||||
is_prefill_context_parallel_enabled() and not is_deepseek_dsa(config)
|
||||
)
|
||||
|
||||
self.embed_tokens = VocabParallelEmbedding(
|
||||
config.vocab_size,
|
||||
@@ -50,6 +55,8 @@ class MistralLarge3EagleModel(DeepseekV2Model):
|
||||
prefix=add_prefix(prefix, f"layers.{i}"),
|
||||
quant_config=quant_config,
|
||||
layer_id=i,
|
||||
dsa_enable_prefill_cp=self.dsa_enable_prefill_cp,
|
||||
mla_enable_prefill_cp=self.mla_enable_prefill_cp,
|
||||
)
|
||||
for i in range(self.config.num_hidden_layers)
|
||||
]
|
||||
|
||||
@@ -1005,6 +1005,7 @@ class Qwen3MoeForCausalLM(nn.Module):
|
||||
self.attn_cp_rank,
|
||||
self.attn_cp_size,
|
||||
forward_batch.seq_lens_cpu.tolist(),
|
||||
extend_lens=forward_batch.extend_seq_lens_cpu,
|
||||
)
|
||||
|
||||
hidden_states = self.model(
|
||||
|
||||
@@ -1825,10 +1825,20 @@ class ServerArgs:
|
||||
assert (
|
||||
self.tp_size <= 8
|
||||
), "Context parallel only supports single machine (tp_size <= 8). Cross-machine CP has precision issues."
|
||||
# Note(kpham-sgl): Keep attn_tp_size == 1 under DSA CP.
|
||||
# DSACPLayerCommunicator does not all-reduce attention-TP
|
||||
# partial o_proj outputs before replicated dense FFNs.
|
||||
self.attn_cp_size = self.tp_size // self.dp_size
|
||||
|
||||
self.disable_piecewise_cuda_graph = True
|
||||
logger.warning(
|
||||
f"Enable Context Parallel opt for deeeseekv3.2-DSA, Setting dp_size == {self.dp_size} and moe_dense_tp_size == {self.moe_dense_tp_size}, ep_size == {self.ep_size}, tp_size == {self.tp_size}, kv_cache_dtype == {self.kv_cache_dtype}, moe_a2a_backend {self.moe_a2a_backend} "
|
||||
f"Enable DSA Context Parallel opt, "
|
||||
f"Setting dp_size == {self.dp_size} and "
|
||||
f"moe_dense_tp_size == {self.moe_dense_tp_size}, "
|
||||
f"ep_size == {self.ep_size}, "
|
||||
f"tp_size == {self.tp_size}, "
|
||||
f"kv_cache_dtype == {self.kv_cache_dtype}, "
|
||||
f"moe_a2a_backend {self.moe_a2a_backend}, "
|
||||
f"disable_piecewise_cuda_graph=True"
|
||||
)
|
||||
else:
|
||||
# Pure TP and partial DP Attention mode is active for DSA, logging a warning
|
||||
@@ -1870,7 +1880,7 @@ class ServerArgs:
|
||||
), "CP is only supported for prefill when PD disaggregation, please remove --enable-dsa-prefill-context-parallel."
|
||||
|
||||
else:
|
||||
# DeepSeek V3/R1/V3.1
|
||||
# DeepSeek V3/R1/V3.1 and Kimi K2.5
|
||||
if not self.disable_piecewise_cuda_graph:
|
||||
logger.info("Piecewise CUDA graph is enabled, use MLA for prefill.")
|
||||
|
||||
@@ -1885,6 +1895,37 @@ class ServerArgs:
|
||||
"Use trtllm_mla as attention backend on sm100 for DeepseekV3ForCausalLM"
|
||||
)
|
||||
|
||||
# MLA prefill CP auto-config. Mirrors the NSA CP block above
|
||||
# (minus the in-seq/round-robin mode split, which MLA CP does not support)
|
||||
if self.enable_prefill_context_parallel and self.use_mla_backend():
|
||||
logger.warning(
|
||||
"MLA prefill context parallel is still experimental. "
|
||||
"Verified on Hopper with the fa3 backend."
|
||||
)
|
||||
self.enable_dp_attention = True
|
||||
# TODO(kpham-sgl) Supports moe_dense_tp_size != 1.
|
||||
self.moe_dense_tp_size = 1
|
||||
self.moe_a2a_backend = "deepep"
|
||||
self.ep_size = self.tp_size
|
||||
logger.warning(
|
||||
"For MLA CP, we have the following restrictions: moe_dense_tp_size == 1, moe_a2a_backend == deepep, ep_size == tp_size, batch_size == 1"
|
||||
)
|
||||
# FIXME(kpham-sgl): Keep attn_tp_size == 1 under MLA CP.
|
||||
# DSACPLayerCommunicator does not all-reduce attention-TP
|
||||
# partial o_proj outputs before replicated dense FFNs.
|
||||
self.attn_cp_size = self.tp_size // self.dp_size
|
||||
self.disable_piecewise_cuda_graph = True
|
||||
logger.warning(
|
||||
f"Enable Context Parallel opt for MLA, "
|
||||
f"Setting dp_size == {self.dp_size} and "
|
||||
f"attn_cp_size == {self.attn_cp_size}, "
|
||||
f"moe_dense_tp_size == {self.moe_dense_tp_size}, "
|
||||
f"ep_size == {self.ep_size}, "
|
||||
f"tp_size == {self.tp_size}, "
|
||||
f"moe_a2a_backend {self.moe_a2a_backend}, "
|
||||
f"disable_piecewise_cuda_graph=True"
|
||||
)
|
||||
|
||||
# Set moe backend for DeepSeek
|
||||
if is_sm100_supported():
|
||||
quant_method = get_quantization_config(hf_config)
|
||||
@@ -3039,6 +3080,19 @@ class ServerArgs:
|
||||
)
|
||||
|
||||
def _handle_context_parallelism(self):
|
||||
if (
|
||||
self.enable_prefill_context_parallel
|
||||
and self.enable_dsa_prefill_context_parallel
|
||||
):
|
||||
raise ValueError(
|
||||
"--enable-prefill-context-parallel and "
|
||||
"--enable-nsa-prefill-context-parallel are mutually "
|
||||
"exclusive. Use --enable-nsa-prefill-context-parallel for "
|
||||
"DeepSeek V3.2 (NSA) models and "
|
||||
"--enable-prefill-context-parallel for MLA-based models "
|
||||
"(DeepSeek V3/R1, Kimi K2.5) or MHA/GQA-based models."
|
||||
)
|
||||
|
||||
if self.attn_cp_size > 1:
|
||||
# The tp_size is the world size, not the real tensor parallel size
|
||||
assert (
|
||||
|
||||
Reference in New Issue
Block a user