[Refactor] Deduplicate NSA utils.py into cp_utils.py for context parallel (#22914)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
eb76aaba88
commit
c304d0d64d
@@ -807,7 +807,7 @@ class AscendAttnBackend(AttentionBackend):
|
|||||||
if (
|
if (
|
||||||
is_prefill
|
is_prefill
|
||||||
and is_nsa_enable_prefill_cp()
|
and is_nsa_enable_prefill_cp()
|
||||||
and forward_batch.nsa_cp_metadata is not None
|
and forward_batch.attn_cp_metadata is not None
|
||||||
):
|
):
|
||||||
attn_out = self.do_cp_balance_attn(
|
attn_out = self.do_cp_balance_attn(
|
||||||
q_nope,
|
q_nope,
|
||||||
|
|||||||
@@ -53,7 +53,6 @@ from sglang.srt.distributed import (
|
|||||||
from sglang.srt.distributed.parallel_state import get_pp_group
|
from sglang.srt.distributed.parallel_state import get_pp_group
|
||||||
from sglang.srt.layers import deep_gemm_wrapper
|
from sglang.srt.layers import deep_gemm_wrapper
|
||||||
from sglang.srt.layers.attention.nsa.utils import (
|
from sglang.srt.layers.attention.nsa.utils import (
|
||||||
cp_all_gather_rerange_output,
|
|
||||||
is_nsa_enable_prefill_cp,
|
is_nsa_enable_prefill_cp,
|
||||||
is_nsa_prefill_cp_in_seq_split,
|
is_nsa_prefill_cp_in_seq_split,
|
||||||
)
|
)
|
||||||
@@ -61,6 +60,7 @@ from sglang.srt.layers.communicator import ScatterMode
|
|||||||
from sglang.srt.layers.linear import ReplicatedLinear
|
from sglang.srt.layers.linear import ReplicatedLinear
|
||||||
from sglang.srt.layers.quantization.base_config import QuantizationConfig
|
from sglang.srt.layers.quantization.base_config import QuantizationConfig
|
||||||
from sglang.srt.layers.rotary_embedding import get_rope_wrapper
|
from sglang.srt.layers.rotary_embedding import get_rope_wrapper
|
||||||
|
from sglang.srt.layers.utils.cp_utils import cp_all_gather_rerange_output
|
||||||
from sglang.srt.model_executor.cuda_graph_runner import get_is_capture_mode
|
from sglang.srt.model_executor.cuda_graph_runner import get_is_capture_mode
|
||||||
from sglang.srt.model_executor.forward_batch_info import ForwardBatch
|
from sglang.srt.model_executor.forward_batch_info import ForwardBatch
|
||||||
from sglang.srt.server_args import get_global_server_args
|
from sglang.srt.server_args import get_global_server_args
|
||||||
@@ -358,7 +358,7 @@ class Indexer(MultiPlatformOp):
|
|||||||
current_stream.wait_stream(self.alt_stream)
|
current_stream.wait_stream(self.alt_stream)
|
||||||
elif (
|
elif (
|
||||||
self.alt_stream is not None
|
self.alt_stream is not None
|
||||||
and forward_batch.nsa_cp_metadata is not None
|
and forward_batch.attn_cp_metadata is not None
|
||||||
and self.nsa_enable_prefill_cp
|
and self.nsa_enable_prefill_cp
|
||||||
):
|
):
|
||||||
key = rotate_activation(key)
|
key = rotate_activation(key)
|
||||||
@@ -380,7 +380,7 @@ class Indexer(MultiPlatformOp):
|
|||||||
key = rotate_activation(key)
|
key = rotate_activation(key)
|
||||||
|
|
||||||
# allgather+rerrange
|
# allgather+rerrange
|
||||||
if forward_batch.nsa_cp_metadata is not None and self.nsa_enable_prefill_cp:
|
if forward_batch.attn_cp_metadata is not None and self.nsa_enable_prefill_cp:
|
||||||
key = cp_all_gather_rerange_output(
|
key = cp_all_gather_rerange_output(
|
||||||
key.contiguous(),
|
key.contiguous(),
|
||||||
self.cp_size,
|
self.cp_size,
|
||||||
@@ -1231,17 +1231,17 @@ class Indexer(MultiPlatformOp):
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
if (
|
if (
|
||||||
forward_batch.nsa_cp_metadata is not None
|
forward_batch.attn_cp_metadata is not None
|
||||||
and is_nsa_prefill_cp_in_seq_split()
|
and is_nsa_prefill_cp_in_seq_split()
|
||||||
):
|
):
|
||||||
kv_len_prev = forward_batch.nsa_cp_metadata.kv_len_prev
|
kv_len_prev = forward_batch.attn_cp_metadata.kv_len_prev
|
||||||
kv_len_next = forward_batch.nsa_cp_metadata.kv_len_next
|
kv_len_next = forward_batch.attn_cp_metadata.kv_len_next
|
||||||
actual_seq_q_prev = forward_batch.nsa_cp_metadata.actual_seq_q_prev
|
actual_seq_q_prev = forward_batch.attn_cp_metadata.actual_seq_q_prev
|
||||||
actual_seq_q_next = forward_batch.nsa_cp_metadata.actual_seq_q_next
|
actual_seq_q_next = forward_batch.attn_cp_metadata.actual_seq_q_next
|
||||||
|
|
||||||
# TODO support mutil-batch
|
# TODO support mutil-batch
|
||||||
# cp_batch_seq_index_prev = forward_batch.nsa_cp_metadata["cp_batch_seq_index_prev"]
|
# cp_batch_seq_index_prev = forward_batch.attn_cp_metadata["cp_batch_seq_index_prev"]
|
||||||
# cp_batch_seq_index_next = forward_batch.nsa_cp_metadata["cp_batch_seq_index_next"]
|
# cp_batch_seq_index_next = forward_batch.attn_cp_metadata["cp_batch_seq_index_next"]
|
||||||
# TODO prev, next, combined into a single call
|
# TODO prev, next, combined into a single call
|
||||||
q_fp8_prev, q_fp8_next = torch.split(
|
q_fp8_prev, q_fp8_next = torch.split(
|
||||||
q_fp8, (q_fp8.shape[0] + 1) // 2, dim=0
|
q_fp8, (q_fp8.shape[0] + 1) // 2, dim=0
|
||||||
@@ -1442,7 +1442,7 @@ class Indexer(MultiPlatformOp):
|
|||||||
if (
|
if (
|
||||||
is_prefill
|
is_prefill
|
||||||
and self.nsa_enable_prefill_cp
|
and self.nsa_enable_prefill_cp
|
||||||
and forward_batch.nsa_cp_metadata is not None
|
and forward_batch.attn_cp_metadata is not None
|
||||||
):
|
):
|
||||||
k = cp_all_gather_rerange_output(
|
k = cp_all_gather_rerange_output(
|
||||||
k.contiguous().view(-1, self.head_dim),
|
k.contiguous().view(-1, self.head_dim),
|
||||||
@@ -1455,14 +1455,17 @@ class Indexer(MultiPlatformOp):
|
|||||||
layer_id, forward_batch.out_cache_loc, k
|
layer_id, forward_batch.out_cache_loc, k
|
||||||
)
|
)
|
||||||
if is_prefill:
|
if is_prefill:
|
||||||
if self.nsa_enable_prefill_cp and forward_batch.nsa_cp_metadata is not None:
|
if (
|
||||||
|
self.nsa_enable_prefill_cp
|
||||||
|
and forward_batch.attn_cp_metadata is not None
|
||||||
|
):
|
||||||
forward_batch.attn_backend.forward_metadata.actual_seq_lengths_q = (
|
forward_batch.attn_backend.forward_metadata.actual_seq_lengths_q = (
|
||||||
forward_batch.nsa_cp_metadata.actual_seq_q_prev_tensor,
|
forward_batch.attn_cp_metadata.actual_seq_q_prev_tensor,
|
||||||
forward_batch.nsa_cp_metadata.actual_seq_q_next_tensor,
|
forward_batch.attn_cp_metadata.actual_seq_q_next_tensor,
|
||||||
)
|
)
|
||||||
forward_batch.attn_backend.forward_metadata.actual_seq_lengths_kv = (
|
forward_batch.attn_backend.forward_metadata.actual_seq_lengths_kv = (
|
||||||
forward_batch.nsa_cp_metadata.kv_len_prev_tensor,
|
forward_batch.attn_cp_metadata.kv_len_prev_tensor,
|
||||||
forward_batch.nsa_cp_metadata.kv_len_next_tensor,
|
forward_batch.attn_cp_metadata.kv_len_next_tensor,
|
||||||
)
|
)
|
||||||
actual_seq_lengths_q = (
|
actual_seq_lengths_q = (
|
||||||
forward_batch.attn_backend.forward_metadata.actual_seq_lengths_q
|
forward_batch.attn_backend.forward_metadata.actual_seq_lengths_q
|
||||||
@@ -1517,7 +1520,7 @@ class Indexer(MultiPlatformOp):
|
|||||||
if (
|
if (
|
||||||
is_prefill
|
is_prefill
|
||||||
and self.nsa_enable_prefill_cp
|
and self.nsa_enable_prefill_cp
|
||||||
and forward_batch.nsa_cp_metadata is not None
|
and forward_batch.attn_cp_metadata is not None
|
||||||
):
|
):
|
||||||
block_table = block_table[: actual_seq_lengths_q[0].numel()]
|
block_table = block_table[: actual_seq_lengths_q[0].numel()]
|
||||||
topk_indices = self.do_npu_cp_balance_indexer(
|
topk_indices = self.do_npu_cp_balance_indexer(
|
||||||
|
|||||||
@@ -1,24 +1,14 @@
|
|||||||
# temp NSA debugging environ
|
|
||||||
from dataclasses import dataclass
|
|
||||||
from itertools import accumulate
|
|
||||||
from typing import TYPE_CHECKING, List, Tuple, Union
|
from typing import TYPE_CHECKING, List, Tuple, Union
|
||||||
|
|
||||||
import torch
|
import torch
|
||||||
import torch.nn.functional as F
|
|
||||||
import triton
|
import triton
|
||||||
import triton.language as tl
|
import triton.language as tl
|
||||||
|
|
||||||
from sglang.srt.distributed.device_communicators.pynccl_allocator import (
|
|
||||||
use_symmetric_memory,
|
|
||||||
)
|
|
||||||
from sglang.srt.layers.dp_attention import (
|
from sglang.srt.layers.dp_attention import (
|
||||||
DpPaddingMode,
|
DpPaddingMode,
|
||||||
attn_cp_all_gather_into_tensor,
|
|
||||||
get_attention_cp_group,
|
|
||||||
get_attention_cp_rank,
|
get_attention_cp_rank,
|
||||||
get_attention_cp_size,
|
get_attention_cp_size,
|
||||||
get_attention_dp_rank,
|
get_attention_dp_rank,
|
||||||
is_allocation_symmetric,
|
|
||||||
)
|
)
|
||||||
from sglang.srt.server_args import get_global_server_args
|
from sglang.srt.server_args import get_global_server_args
|
||||||
from sglang.srt.utils.common import ceil_align, ceil_div
|
from sglang.srt.utils.common import ceil_align, ceil_div
|
||||||
@@ -135,27 +125,7 @@ def pad_nsa_cache_seqlens(forward_batch: "ForwardBatch", nsa_cache_seqlens):
|
|||||||
return nsa_cache_seqlens
|
return nsa_cache_seqlens
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
def can_nsa_cp_split(seq_len: int, cp_size: int, use_nsa: bool, forward_batch):
|
||||||
class NSAContextParallelMetadata:
|
|
||||||
|
|
||||||
split_list: List[int] = None
|
|
||||||
max_rank_len: List[int] = None
|
|
||||||
zigzag_index: List[int] = None
|
|
||||||
per_rank_actual_token: List[int] = None
|
|
||||||
reverse_split_len: List[int] = None
|
|
||||||
cp_reverse_index: List[int] = None
|
|
||||||
kv_len_prev: int = -1
|
|
||||||
kv_len_next: int = -1
|
|
||||||
actual_seq_q_prev: int = -1
|
|
||||||
actual_seq_q_next: int = -1
|
|
||||||
kv_len_prev_tensor: torch.Tensor = None
|
|
||||||
kv_len_next_tensor: torch.Tensor = None
|
|
||||||
actual_seq_q_prev_tensor: torch.Tensor = None
|
|
||||||
actual_seq_q_next_tensor: torch.Tensor = None
|
|
||||||
total_seq_lens: torch.Tensor = None
|
|
||||||
|
|
||||||
|
|
||||||
def can_cp_split(seq_len: int, cp_size: int, use_nsa: bool, forward_batch):
|
|
||||||
if is_nsa_prefill_cp_round_robin_split():
|
if is_nsa_prefill_cp_round_robin_split():
|
||||||
cur_cp_seq_len = seq_len // cp_size
|
cur_cp_seq_len = seq_len // cp_size
|
||||||
assert (
|
assert (
|
||||||
@@ -179,42 +149,6 @@ def can_cp_split(seq_len: int, cp_size: int, use_nsa: bool, forward_batch):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def cp_split_and_rebuild_data(forward_batch, input_: torch.Tensor):
|
|
||||||
if is_nsa_prefill_cp_round_robin_split():
|
|
||||||
cp_size = get_attention_cp_size()
|
|
||||||
assert (
|
|
||||||
input_.shape[0] % cp_size == 0
|
|
||||||
), f"Expect input shape 0 can divided by cp size, but got input shape {input_.shape}, cp size {cp_size}"
|
|
||||||
return nsa_cp_round_robin_split_data(input_)
|
|
||||||
|
|
||||||
input_list = list(
|
|
||||||
torch.split(input_, forward_batch.nsa_cp_metadata.split_list, dim=0)
|
|
||||||
)
|
|
||||||
result = torch.cat(
|
|
||||||
[input_list[i] for i in forward_batch.nsa_cp_metadata.zigzag_index], dim=0
|
|
||||||
).view(-1, input_.shape[-1])
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def cp_split_and_rebuild_position(forward_batch, positions: torch.Tensor):
|
|
||||||
if is_nsa_prefill_cp_round_robin_split():
|
|
||||||
cp_size = get_attention_cp_size()
|
|
||||||
assert positions.shape[0] % cp_size == 0, (
|
|
||||||
f"Expect positions shape 0 can divided by cp size, but got positions shape {positions.shape}, "
|
|
||||||
f"cp size {cp_size}"
|
|
||||||
)
|
|
||||||
return nsa_cp_round_robin_split_data(positions)
|
|
||||||
|
|
||||||
position_id_list = list(
|
|
||||||
torch.split(positions, forward_batch.nsa_cp_metadata.split_list, dim=-1)
|
|
||||||
)
|
|
||||||
positions = torch.cat(
|
|
||||||
[position_id_list[i] for i in forward_batch.nsa_cp_metadata.zigzag_index],
|
|
||||||
dim=-1,
|
|
||||||
)
|
|
||||||
return positions
|
|
||||||
|
|
||||||
|
|
||||||
@triton.jit
|
@triton.jit
|
||||||
def nsa_cp_round_robin_split_q_seqs_kernel(
|
def nsa_cp_round_robin_split_q_seqs_kernel(
|
||||||
in_seqs_ptr,
|
in_seqs_ptr,
|
||||||
@@ -285,295 +219,10 @@ def nsa_use_prefill_cp(forward_batch, nsa_enable_prefill_cp=None):
|
|||||||
if nsa_enable_prefill_cp is None:
|
if nsa_enable_prefill_cp is None:
|
||||||
nsa_enable_prefill_cp = is_nsa_enable_prefill_cp()
|
nsa_enable_prefill_cp = is_nsa_enable_prefill_cp()
|
||||||
if (
|
if (
|
||||||
forward_batch.nsa_cp_metadata is not None
|
forward_batch.attn_cp_metadata is not None
|
||||||
and nsa_enable_prefill_cp
|
and nsa_enable_prefill_cp
|
||||||
and forward_batch.forward_mode.is_context_parallel_extend()
|
and forward_batch.forward_mode.is_context_parallel_extend()
|
||||||
):
|
):
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def cp_attn_tp_all_gather_reorganazied_into_tensor(
|
|
||||||
input_: torch.Tensor, total_len, attn_tp_size, forward_batch, stream_op
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Allgather communication for context_parallel(kv_cache, index_k, hidden_states).
|
|
||||||
This implementation mainly consists of three parts:
|
|
||||||
Step 1, padding the input shape to unify the shape for allgather communication (the shape must be the same).
|
|
||||||
Step 2, allgather communication(async).
|
|
||||||
Step 3, removing the padding and reassembling the data according to the actual tokens.
|
|
||||||
"""
|
|
||||||
# step1
|
|
||||||
max_len = (total_len + attn_tp_size - 1) // attn_tp_size
|
|
||||||
pad_size = max_len - input_.shape[0]
|
|
||||||
if pad_size > 0:
|
|
||||||
input_ = F.pad(input_, (0, 0, 0, pad_size), mode="constant", value=0)
|
|
||||||
with use_symmetric_memory(
|
|
||||||
get_attention_cp_group(), disabled=not is_allocation_symmetric()
|
|
||||||
):
|
|
||||||
input_tensor_all = torch.empty(
|
|
||||||
max_len * attn_tp_size,
|
|
||||||
input_.shape[1],
|
|
||||||
device=input_.device,
|
|
||||||
dtype=input_.dtype,
|
|
||||||
)
|
|
||||||
# step2
|
|
||||||
get_attention_cp_group().cp_all_gather_into_tensor_async(
|
|
||||||
input_tensor_all, input_, stream_op
|
|
||||||
)
|
|
||||||
# step3
|
|
||||||
outputs_list_max = list(
|
|
||||||
torch.split(input_tensor_all, forward_batch.nsa_cp_metadata.max_rank_len, dim=0)
|
|
||||||
)
|
|
||||||
outputs = torch.cat(
|
|
||||||
[
|
|
||||||
outputs_list_max[index][:per_rank_len]
|
|
||||||
for index, per_rank_len in enumerate(
|
|
||||||
forward_batch.nsa_cp_metadata.per_rank_actual_token
|
|
||||||
)
|
|
||||||
],
|
|
||||||
dim=0,
|
|
||||||
)
|
|
||||||
return outputs
|
|
||||||
|
|
||||||
|
|
||||||
def cp_all_gather_rerange_output(input_tensor, cp_size, forward_batch, stream):
|
|
||||||
"""
|
|
||||||
# for in-seq-split
|
|
||||||
| +-----------before allgather------------+|
|
|
||||||
| | dp_atten_tp0: block0, block7 |
|
|
||||||
| | dp_atten_tp1: block1, block6 |
|
|
||||||
| | dp_atten_tp2: block2, block5 |
|
|
||||||
| | dp_atten_tp3: block3, block4 |
|
|
||||||
|
|
|
||||||
| +----------before rerange---------------+|
|
|
||||||
| block0 | block7 | block1 | block6 | block2 | block5 | block3 | block4 |
|
|
||||||
|
|
|
||||||
| +--------------result-------------------+
|
|
||||||
| block0 | block1 | block2 | block3 | block4 | block5 | block6 | block7 |
|
|
||||||
| +-------------------------+
|
|
||||||
|
|
||||||
# for round-robin-split
|
|
||||||
| +-----------before allgather------------+|
|
|
||||||
| dp_atten_tp0: token0, token4, token8, token12, token16, ... |
|
|
||||||
| dp_atten_tp1: token1, token5, token9, token13, token17, ... |
|
|
||||||
| dp_atten_tp2: token2, token6, token10, token14, token18, ... |
|
|
||||||
| dp_atten_tp3: token3, token7, token11, token15, token19, ... |
|
|
||||||
|
|
|
||||||
| +--------------result-------------------+
|
|
||||||
| token0, token1, token2, token3, token4, token5, token6, token7, ...
|
|
||||||
| +-------------------------+
|
|
||||||
"""
|
|
||||||
if is_nsa_prefill_cp_round_robin_split():
|
|
||||||
with use_symmetric_memory(
|
|
||||||
get_attention_cp_group(), disabled=not is_allocation_symmetric()
|
|
||||||
):
|
|
||||||
output_tensor = input_tensor.new_empty(
|
|
||||||
(input_tensor.shape[0] * cp_size, *input_tensor.shape[1:]),
|
|
||||||
)
|
|
||||||
attn_cp_all_gather_into_tensor(
|
|
||||||
output_tensor,
|
|
||||||
input_tensor,
|
|
||||||
)
|
|
||||||
out_shape = output_tensor.shape
|
|
||||||
output_tensor = (
|
|
||||||
output_tensor.view(cp_size, -1, *out_shape[1:])
|
|
||||||
.transpose(0, 1)
|
|
||||||
.reshape(out_shape)
|
|
||||||
)
|
|
||||||
return output_tensor
|
|
||||||
|
|
||||||
bs_seq_len, hidden_size = input_tensor.shape
|
|
||||||
output_tensor = cp_attn_tp_all_gather_reorganazied_into_tensor(
|
|
||||||
input_tensor,
|
|
||||||
forward_batch.nsa_cp_metadata.total_seq_lens,
|
|
||||||
cp_size,
|
|
||||||
forward_batch,
|
|
||||||
stream,
|
|
||||||
)
|
|
||||||
outputs_list = list(
|
|
||||||
torch.split(
|
|
||||||
output_tensor, forward_batch.nsa_cp_metadata.reverse_split_len, dim=0
|
|
||||||
)
|
|
||||||
)
|
|
||||||
output_tensor = torch.cat(
|
|
||||||
[outputs_list[i] for i in forward_batch.nsa_cp_metadata.cp_reverse_index], dim=0
|
|
||||||
)
|
|
||||||
output_tensor = output_tensor.view(-1, hidden_size)
|
|
||||||
return output_tensor
|
|
||||||
|
|
||||||
|
|
||||||
def calculate_cp_seq_idx(cp_chunks_len, seqs_len):
|
|
||||||
"""Used to obtain the index of the seq corresponding
|
|
||||||
to each cp block in the forwardbatch, and the starting
|
|
||||||
and ending positions of the corresponding seq in the cp block"""
|
|
||||||
j = 0
|
|
||||||
tuple_len = [] # Only keep this result list
|
|
||||||
cumulative = {} # Used to track cumulative values for each index
|
|
||||||
|
|
||||||
for i in range(len(cp_chunks_len)):
|
|
||||||
current_dict = {}
|
|
||||||
current_tuples = []
|
|
||||||
c_val = cp_chunks_len[i]
|
|
||||||
|
|
||||||
while j < len(seqs_len):
|
|
||||||
s_val = seqs_len[j]
|
|
||||||
if s_val == c_val:
|
|
||||||
idx = j
|
|
||||||
current_dict[idx] = s_val
|
|
||||||
# Update cumulative value for this index
|
|
||||||
cumulative[idx] = cumulative.get(idx, 0) + s_val
|
|
||||||
j += 1
|
|
||||||
break
|
|
||||||
elif s_val > c_val:
|
|
||||||
idx = j
|
|
||||||
current_dict[idx] = c_val
|
|
||||||
# Update cumulative value for this index
|
|
||||||
cumulative[idx] = cumulative.get(idx, 0) + c_val
|
|
||||||
seqs_len[j] = s_val - c_val
|
|
||||||
break
|
|
||||||
else: # s_val < c_val
|
|
||||||
idx = j
|
|
||||||
current_dict[idx] = s_val
|
|
||||||
# Update cumulative value for this index
|
|
||||||
cumulative[idx] = cumulative.get(idx, 0) + s_val
|
|
||||||
c_val -= s_val
|
|
||||||
j += 1
|
|
||||||
|
|
||||||
# Build tuple: (index, historical cumulative, historical+current)
|
|
||||||
for idx, val in current_dict.items():
|
|
||||||
# Subtract current value to get historical cumulative
|
|
||||||
prev_cum = cumulative.get(idx, 0) - val
|
|
||||||
current_cum = prev_cum + val
|
|
||||||
current_tuples.append((idx, prev_cum, current_cum))
|
|
||||||
|
|
||||||
tuple_len.append(current_tuples)
|
|
||||||
return tuple_len
|
|
||||||
|
|
||||||
|
|
||||||
def prepare_input_dp_with_cp_dsa(
|
|
||||||
kv_len,
|
|
||||||
cp_rank,
|
|
||||||
cp_size,
|
|
||||||
seqs_len,
|
|
||||||
):
|
|
||||||
if is_nsa_prefill_cp_round_robin_split():
|
|
||||||
return True
|
|
||||||
"""prepare_input_dp_with_cp_dsa-zigzag index
|
|
||||||
Example (DP_ATTENT_TP == CP_SIZE == 4):
|
|
||||||
Description:
|
|
||||||
1. Start with a full-length request.
|
|
||||||
2. Split the request into multiple blocks (block0 to block7).
|
|
||||||
3. Rearrange these blocks to balance computational
|
|
||||||
load across different DP ranks.
|
|
||||||
4. Assign the rearranged blocks to different DP attention
|
|
||||||
time points (dp_atten_tp0 to dp_atten_tp3).
|
|
||||||
+---------------------------------+
|
|
||||||
| cp_split_tokens |
|
|
||||||
+---------------------------------+
|
|
||||||
| |
|
|
||||||
| request_with_full_length |
|
|
||||||
| | split (cp_size * 2) |
|
|
||||||
| +-------------------------+ |
|
|
||||||
| | block0 | block1 | block2 | block3 | block4 | block5 | block6 | block7 |
|
|
||||||
| +-------------------------+ |
|
|
||||||
| | rerange |
|
|
||||||
| +---------------------------------+
|
|
||||||
| | block0 | block7 | block1 | block6 | block2 | block5 | block3 | block4 |
|
|
||||||
| +---------------------------------+
|
|
||||||
| |
|
|
||||||
| +-------------------------+
|
|
||||||
| | dp_atten_tp0: block0, block7 |
|
|
||||||
| | dp_atten_tp1: block1, block6 |
|
|
||||||
| | dp_atten_tp2: block2, block5 |
|
|
||||||
| | dp_atten_tp3: block3, block4 |
|
|
||||||
| +-------------------------+
|
|
||||||
|
|
||||||
Why zigzag rearrange?
|
|
||||||
- Attention calculations must follow causal attention principles.
|
|
||||||
- Simply slicing by rank order can lead to computational load imbalance:
|
|
||||||
* First rank may focus on fewer historical key-value tokens (less computation)
|
|
||||||
* Last rank may focus on more tokens (more computation)
|
|
||||||
- To mitigate uneven load, the input hissenstate needs to be sliced by cp_size*2 and rearranged.
|
|
||||||
"""
|
|
||||||
# just support batch = 1
|
|
||||||
kv_len = torch.tensor(kv_len)
|
|
||||||
bs_per_cp_group = 1
|
|
||||||
kv_len_origin = kv_len
|
|
||||||
# get zigzag index
|
|
||||||
cp_segment_num = cp_size * 2
|
|
||||||
seq_per_batch = kv_len // cp_segment_num # seq_len for each batch and segment
|
|
||||||
split_list = seq_per_batch.repeat_interleave(cp_segment_num).int().tolist()
|
|
||||||
remainder = kv_len % (cp_segment_num)
|
|
||||||
if remainder > 0:
|
|
||||||
split_list[:remainder] = [x + 1 for x in split_list[:remainder]]
|
|
||||||
|
|
||||||
seq_max_rank_len = (kv_len + cp_size - 1) // cp_size
|
|
||||||
max_rank_len = seq_max_rank_len.repeat_interleave(cp_size).int().tolist()
|
|
||||||
zigzag_index = list(
|
|
||||||
range(cp_rank, cp_rank + bs_per_cp_group * cp_segment_num, cp_segment_num)
|
|
||||||
) + list(
|
|
||||||
range(
|
|
||||||
cp_segment_num - cp_rank - 1,
|
|
||||||
bs_per_cp_group * cp_segment_num,
|
|
||||||
cp_segment_num,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
per_rank_actual_token = list(
|
|
||||||
split_list[i] + split_list[cp_size * 2 - i - 1] for i in range(cp_size)
|
|
||||||
)
|
|
||||||
reverse_split_len = [
|
|
||||||
element
|
|
||||||
for i in range(cp_size)
|
|
||||||
for element in (split_list[i], split_list[cp_size * 2 - i - 1])
|
|
||||||
]
|
|
||||||
# get zigzag reverse index
|
|
||||||
cp_reverse_index = []
|
|
||||||
for batch_id in range(bs_per_cp_group):
|
|
||||||
cp_reverse_index.extend(
|
|
||||||
list(range(batch_id, cp_segment_num * bs_per_cp_group, 2 * bs_per_cp_group))
|
|
||||||
+ list(
|
|
||||||
range(
|
|
||||||
(cp_segment_num - 1) * bs_per_cp_group + batch_id,
|
|
||||||
0,
|
|
||||||
-2 * bs_per_cp_group,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
prefix_sum_list = list(accumulate(split_list))
|
|
||||||
|
|
||||||
# TODO Support multi-batch-cp-split, multi-batch-cp support has accuracy issues
|
|
||||||
# cp_seq_index = calculate_cp_seq_idx(split_list[:], seqs_len[:])
|
|
||||||
kv_len_prev = prefix_sum_list[cp_rank]
|
|
||||||
kv_len_next = prefix_sum_list[cp_size * 2 - cp_rank - 1]
|
|
||||||
actual_seq_q_prev = split_list[cp_rank]
|
|
||||||
actual_seq_q_next = split_list[cp_size * 2 - cp_rank - 1]
|
|
||||||
kv_len_prev_tensor = torch.tensor(kv_len_prev).to(device="cuda", dtype=torch.int32)
|
|
||||||
kv_len_next_tensor = torch.tensor(kv_len_next).to(device="cuda", dtype=torch.int32)
|
|
||||||
actual_seq_q_prev_tensor = torch.tensor(actual_seq_q_prev).to(
|
|
||||||
device="cuda", dtype=torch.int32
|
|
||||||
)
|
|
||||||
actual_seq_q_next_tensor = torch.tensor(actual_seq_q_next).to(
|
|
||||||
device="cuda", dtype=torch.int32
|
|
||||||
)
|
|
||||||
|
|
||||||
nsa_cp_metadata = NSAContextParallelMetadata(
|
|
||||||
split_list=split_list,
|
|
||||||
max_rank_len=max_rank_len,
|
|
||||||
zigzag_index=zigzag_index,
|
|
||||||
per_rank_actual_token=per_rank_actual_token,
|
|
||||||
reverse_split_len=reverse_split_len,
|
|
||||||
cp_reverse_index=cp_reverse_index,
|
|
||||||
kv_len_prev=kv_len_prev,
|
|
||||||
kv_len_next=kv_len_next,
|
|
||||||
actual_seq_q_prev=actual_seq_q_prev,
|
|
||||||
actual_seq_q_next=actual_seq_q_next,
|
|
||||||
kv_len_prev_tensor=kv_len_prev_tensor,
|
|
||||||
kv_len_next_tensor=kv_len_next_tensor,
|
|
||||||
actual_seq_q_prev_tensor=actual_seq_q_prev_tensor,
|
|
||||||
actual_seq_q_next_tensor=actual_seq_q_next_tensor,
|
|
||||||
total_seq_lens=kv_len_origin,
|
|
||||||
)
|
|
||||||
return nsa_cp_metadata
|
|
||||||
|
|||||||
@@ -5,7 +5,15 @@ from typing import Callable, List
|
|||||||
import torch
|
import torch
|
||||||
import torch.nn.functional as F
|
import torch.nn.functional as F
|
||||||
|
|
||||||
from sglang.srt.layers.dp_attention import get_attention_cp_group
|
from sglang.srt.distributed.device_communicators.pynccl_allocator import (
|
||||||
|
use_symmetric_memory,
|
||||||
|
)
|
||||||
|
from sglang.srt.layers.dp_attention import (
|
||||||
|
attn_cp_all_gather_into_tensor,
|
||||||
|
get_attention_cp_group,
|
||||||
|
get_attention_cp_size,
|
||||||
|
is_allocation_symmetric,
|
||||||
|
)
|
||||||
from sglang.srt.server_args import get_global_server_args
|
from sglang.srt.server_args import get_global_server_args
|
||||||
|
|
||||||
|
|
||||||
@@ -60,6 +68,18 @@ def can_cp_split(seq_len: int, cp_size: int, forward_batch):
|
|||||||
|
|
||||||
|
|
||||||
def cp_split_and_rebuild_data(forward_batch, input_: torch.Tensor):
|
def cp_split_and_rebuild_data(forward_batch, input_: torch.Tensor):
|
||||||
|
from sglang.srt.layers.attention.nsa.utils import (
|
||||||
|
is_nsa_prefill_cp_round_robin_split,
|
||||||
|
nsa_cp_round_robin_split_data,
|
||||||
|
)
|
||||||
|
|
||||||
|
if is_nsa_prefill_cp_round_robin_split():
|
||||||
|
cp_size = get_attention_cp_size()
|
||||||
|
assert (
|
||||||
|
input_.shape[0] % cp_size == 0
|
||||||
|
), f"Expect input shape 0 can divided by cp size, but got input shape {input_.shape}, cp size {cp_size}"
|
||||||
|
return nsa_cp_round_robin_split_data(input_)
|
||||||
|
|
||||||
input_list = list(
|
input_list = list(
|
||||||
torch.split(input_, forward_batch.attn_cp_metadata.split_list, dim=0)
|
torch.split(input_, forward_batch.attn_cp_metadata.split_list, dim=0)
|
||||||
)
|
)
|
||||||
@@ -70,6 +90,19 @@ def cp_split_and_rebuild_data(forward_batch, input_: torch.Tensor):
|
|||||||
|
|
||||||
|
|
||||||
def cp_split_and_rebuild_position(forward_batch, positions: torch.Tensor):
|
def cp_split_and_rebuild_position(forward_batch, positions: torch.Tensor):
|
||||||
|
from sglang.srt.layers.attention.nsa.utils import (
|
||||||
|
is_nsa_prefill_cp_round_robin_split,
|
||||||
|
nsa_cp_round_robin_split_data,
|
||||||
|
)
|
||||||
|
|
||||||
|
if is_nsa_prefill_cp_round_robin_split():
|
||||||
|
cp_size = get_attention_cp_size()
|
||||||
|
assert positions.shape[0] % cp_size == 0, (
|
||||||
|
f"Expect positions shape 0 can divided by cp size, but got positions shape {positions.shape}, "
|
||||||
|
f"cp size {cp_size}"
|
||||||
|
)
|
||||||
|
return nsa_cp_round_robin_split_data(positions)
|
||||||
|
|
||||||
position_id_list = list(
|
position_id_list = list(
|
||||||
torch.split(positions, forward_batch.attn_cp_metadata.split_list, dim=-1)
|
torch.split(positions, forward_batch.attn_cp_metadata.split_list, dim=-1)
|
||||||
)
|
)
|
||||||
@@ -84,7 +117,11 @@ def cp_all_gather_reorganized_into_tensor(
|
|||||||
input_tensor, total_len, cp_size, forward_batch, stream
|
input_tensor, total_len, cp_size, forward_batch, stream
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Allgather communication for context_parallel hidden_states.
|
Allgather communication for context_parallel(kv_cache, index_k, hidden_states).
|
||||||
|
This implementation mainly consists of three parts:
|
||||||
|
Step 1, padding the input shape to unify the shape for allgather communication (the shape must be the same).
|
||||||
|
Step 2, allgather communication(async).
|
||||||
|
Step 3, removing the padding and reassembling the data according to the actual tokens.
|
||||||
"""
|
"""
|
||||||
# The input tensor should already be padded to the same length for allgather communication.
|
# The input tensor should already be padded to the same length for allgather communication.
|
||||||
# No need to pad again.
|
# No need to pad again.
|
||||||
@@ -95,6 +132,9 @@ def cp_all_gather_reorganized_into_tensor(
|
|||||||
input_tensor = F.pad(
|
input_tensor = F.pad(
|
||||||
input_tensor, (0, 0, 0, pad_size), mode="constant", value=0
|
input_tensor, (0, 0, 0, pad_size), mode="constant", value=0
|
||||||
)
|
)
|
||||||
|
with use_symmetric_memory(
|
||||||
|
get_attention_cp_group(), disabled=not is_allocation_symmetric()
|
||||||
|
):
|
||||||
input_tensor_full = torch.empty(
|
input_tensor_full = torch.empty(
|
||||||
max_len * cp_size,
|
max_len * cp_size,
|
||||||
input_tensor.shape[1],
|
input_tensor.shape[1],
|
||||||
@@ -185,7 +225,40 @@ def cp_all_gather_rerange_output(input_tensor, cp_size, forward_batch, stream):
|
|||||||
| +--------------result-------------------+
|
| +--------------result-------------------+
|
||||||
| block0 | block1 | block2 | block3 | block4 | block5 | block6 | block7 |
|
| block0 | block1 | block2 | block3 | block4 | block5 | block6 | block7 |
|
||||||
| +-------------------------+
|
| +-------------------------+
|
||||||
|
|
||||||
|
# for round-robin-split
|
||||||
|
| +-----------before allgather------------+|
|
||||||
|
| dp_atten_tp0: token0, token4, token8, token12, token16, ... |
|
||||||
|
| dp_atten_tp1: token1, token5, token9, token13, token17, ... |
|
||||||
|
| dp_atten_tp2: token2, token6, token10, token14, token18, ... |
|
||||||
|
| dp_atten_tp3: token3, token7, token11, token15, token19, ... |
|
||||||
|
|
|
||||||
|
| +--------------result-------------------+
|
||||||
|
| token0, token1, token2, token3, token4, token5, token6, token7, ...
|
||||||
|
| +-------------------------+
|
||||||
"""
|
"""
|
||||||
|
from sglang.srt.layers.attention.nsa.utils import (
|
||||||
|
is_nsa_prefill_cp_round_robin_split,
|
||||||
|
)
|
||||||
|
|
||||||
|
if is_nsa_prefill_cp_round_robin_split():
|
||||||
|
with use_symmetric_memory(
|
||||||
|
get_attention_cp_group(), disabled=not is_allocation_symmetric()
|
||||||
|
):
|
||||||
|
output_tensor = input_tensor.new_empty(
|
||||||
|
(input_tensor.shape[0] * cp_size, *input_tensor.shape[1:]),
|
||||||
|
)
|
||||||
|
attn_cp_all_gather_into_tensor(
|
||||||
|
output_tensor,
|
||||||
|
input_tensor,
|
||||||
|
)
|
||||||
|
out_shape = output_tensor.shape
|
||||||
|
output_tensor = (
|
||||||
|
output_tensor.view(cp_size, -1, *out_shape[1:])
|
||||||
|
.transpose(0, 1)
|
||||||
|
.reshape(out_shape)
|
||||||
|
)
|
||||||
|
return output_tensor
|
||||||
|
|
||||||
# TODO: Do we need to remove the padding here?
|
# TODO: Do we need to remove the padding here?
|
||||||
bs_seq_len, hidden_size = input_tensor.shape
|
bs_seq_len, hidden_size = input_tensor.shape
|
||||||
@@ -321,6 +394,13 @@ def prepare_context_parallel_metadata(
|
|||||||
cp_size,
|
cp_size,
|
||||||
seqs_len,
|
seqs_len,
|
||||||
):
|
):
|
||||||
|
from sglang.srt.layers.attention.nsa.utils import (
|
||||||
|
is_nsa_prefill_cp_round_robin_split,
|
||||||
|
)
|
||||||
|
|
||||||
|
if is_nsa_prefill_cp_round_robin_split():
|
||||||
|
return ContextParallelMetadata()
|
||||||
|
|
||||||
"""prepare_input_dp_with_cp_dsa-zigzag index
|
"""prepare_input_dp_with_cp_dsa-zigzag index
|
||||||
Example (DP_ATTENT_TP == CP_SIZE == 4):
|
Example (DP_ATTENT_TP == CP_SIZE == 4):
|
||||||
Description:
|
Description:
|
||||||
@@ -424,8 +504,19 @@ def prepare_context_parallel_metadata(
|
|||||||
|
|
||||||
# TODO Support multi-batch-cp-split, multi-batch-cp support has accuracy issues
|
# TODO Support multi-batch-cp-split, multi-batch-cp support has accuracy issues
|
||||||
# Prefix offset is critical when radix cache hits (prefix_len > 0).
|
# Prefix offset is critical when radix cache hits (prefix_len > 0).
|
||||||
# These "cache_seqlens" values represent how many KV tokens are visible to
|
# For non-NSA CP (e.g. qwen3-moe), consumers use these values directly as
|
||||||
# each query segment during CP attention.
|
# FlashAttention cache_seqlens, so the prefix must be baked in here.
|
||||||
|
# For NSA CP, `_get_topk_ragged_with_cp` re-adds the cached-prefix offset
|
||||||
|
# from (seq_lens_cpu - extend_seq_lens_cpu); baking prefix_len in here
|
||||||
|
# would silently drop it whenever the scheduler packs multiple requests
|
||||||
|
# into a single CP extend (len(seqs_len) != 1 -> prefix_len falls back
|
||||||
|
# to 0), corrupting the indexer's ke_offset on prefix-cache hits.
|
||||||
|
from sglang.srt.layers.attention.nsa.utils import is_nsa_enable_prefill_cp
|
||||||
|
|
||||||
|
if is_nsa_enable_prefill_cp():
|
||||||
|
kv_len_prev = prefix_sum_list[cp_rank]
|
||||||
|
kv_len_next = prefix_sum_list[cp_size * 2 - cp_rank - 1]
|
||||||
|
else:
|
||||||
kv_len_prev = prefix_len + prefix_sum_list[cp_rank]
|
kv_len_prev = prefix_len + prefix_sum_list[cp_rank]
|
||||||
kv_len_next = prefix_len + prefix_sum_list[cp_size * 2 - cp_rank - 1]
|
kv_len_next = prefix_len + prefix_sum_list[cp_size * 2 - cp_rank - 1]
|
||||||
actual_seq_q_prev = split_list[cp_rank]
|
actual_seq_q_prev = split_list[cp_rank]
|
||||||
|
|||||||
@@ -2151,8 +2151,6 @@ class ScheduleBatch(ScheduleBatchDisaggregationDecodeMixin):
|
|||||||
# Clear context parallel metadata - CP is only for prefill, not decode
|
# Clear context parallel metadata - CP is only for prefill, not decode
|
||||||
if hasattr(self, "attn_cp_metadata") and self.attn_cp_metadata is not None:
|
if hasattr(self, "attn_cp_metadata") and self.attn_cp_metadata is not None:
|
||||||
self.attn_cp_metadata = None
|
self.attn_cp_metadata = None
|
||||||
if hasattr(self, "nsa_cp_metadata") and self.nsa_cp_metadata is not None:
|
|
||||||
self.nsa_cp_metadata = None
|
|
||||||
|
|
||||||
if self.is_spec_v2:
|
if self.is_spec_v2:
|
||||||
# TODO(spec-v2): all spec v2 should go through this path
|
# TODO(spec-v2): all spec v2 should go through this path
|
||||||
|
|||||||
@@ -42,7 +42,6 @@ from sglang.srt.distributed.parallel_state import (
|
|||||||
get_moe_expert_parallel_world_size,
|
get_moe_expert_parallel_world_size,
|
||||||
get_tensor_model_parallel_world_size,
|
get_tensor_model_parallel_world_size,
|
||||||
)
|
)
|
||||||
from sglang.srt.layers.attention.nsa.utils import NSAContextParallelMetadata
|
|
||||||
from sglang.srt.layers.dp_attention import (
|
from sglang.srt.layers.dp_attention import (
|
||||||
DpPaddingMode,
|
DpPaddingMode,
|
||||||
get_attention_cp_size,
|
get_attention_cp_size,
|
||||||
@@ -421,8 +420,6 @@ class ForwardBatch(ForwardBatchDeepSeekMHAMixin):
|
|||||||
dimensions: Optional[list[int]] = None
|
dimensions: Optional[list[int]] = None
|
||||||
|
|
||||||
attn_cp_metadata: Optional[ContextParallelMetadata] = None
|
attn_cp_metadata: Optional[ContextParallelMetadata] = None
|
||||||
# Record the split metadata of the sequence number of NSA context parallels.
|
|
||||||
nsa_cp_metadata: Optional[NSAContextParallelMetadata] = None
|
|
||||||
|
|
||||||
# For hidden states before normal
|
# For hidden states before normal
|
||||||
return_hidden_states_before_norm: bool = False
|
return_hidden_states_before_norm: bool = False
|
||||||
|
|||||||
@@ -28,13 +28,9 @@ from sglang.srt.distributed import get_pp_group, get_tensor_model_parallel_world
|
|||||||
from sglang.srt.environ import envs
|
from sglang.srt.environ import envs
|
||||||
from sglang.srt.eplb.expert_distribution import get_global_expert_distribution_recorder
|
from sglang.srt.eplb.expert_distribution import get_global_expert_distribution_recorder
|
||||||
from sglang.srt.layers.attention.nsa.utils import (
|
from sglang.srt.layers.attention.nsa.utils import (
|
||||||
can_cp_split,
|
can_nsa_cp_split,
|
||||||
cp_all_gather_rerange_output,
|
|
||||||
cp_split_and_rebuild_data,
|
|
||||||
cp_split_and_rebuild_position,
|
|
||||||
is_nsa_enable_prefill_cp,
|
is_nsa_enable_prefill_cp,
|
||||||
nsa_use_prefill_cp,
|
nsa_use_prefill_cp,
|
||||||
prepare_input_dp_with_cp_dsa,
|
|
||||||
)
|
)
|
||||||
from sglang.srt.layers.dp_attention import (
|
from sglang.srt.layers.dp_attention import (
|
||||||
get_attention_cp_rank,
|
get_attention_cp_rank,
|
||||||
@@ -45,6 +41,12 @@ from sglang.srt.layers.layernorm import RMSNorm
|
|||||||
from sglang.srt.layers.logits_processor import LogitsProcessor
|
from sglang.srt.layers.logits_processor import LogitsProcessor
|
||||||
from sglang.srt.layers.quantization import Fp8Config
|
from sglang.srt.layers.quantization import Fp8Config
|
||||||
from sglang.srt.layers.quantization.base_config import QuantizationConfig
|
from sglang.srt.layers.quantization.base_config import QuantizationConfig
|
||||||
|
from sglang.srt.layers.utils.cp_utils import (
|
||||||
|
cp_all_gather_rerange_output,
|
||||||
|
cp_split_and_rebuild_data,
|
||||||
|
cp_split_and_rebuild_position,
|
||||||
|
prepare_context_parallel_metadata,
|
||||||
|
)
|
||||||
from sglang.srt.layers.vocab_parallel_embedding import (
|
from sglang.srt.layers.vocab_parallel_embedding import (
|
||||||
ParallelLMHead,
|
ParallelLMHead,
|
||||||
VocabParallelEmbedding,
|
VocabParallelEmbedding,
|
||||||
@@ -268,8 +270,10 @@ class DeepseekV3ForCausalLMNextN(DeepseekV3ForCausalLM):
|
|||||||
) -> torch.Tensor:
|
) -> torch.Tensor:
|
||||||
# TODO current just support prefill batch=1 and len(input_ids) > self.cp_size * 2
|
# TODO current just support prefill batch=1 and len(input_ids) > self.cp_size * 2
|
||||||
if self.nsa_enable_prefill_cp:
|
if self.nsa_enable_prefill_cp:
|
||||||
if can_cp_split(len(input_ids), self.cp_size, self.use_nsa, forward_batch):
|
if can_nsa_cp_split(
|
||||||
forward_batch.nsa_cp_metadata = prepare_input_dp_with_cp_dsa(
|
len(input_ids), self.cp_size, self.use_nsa, forward_batch
|
||||||
|
):
|
||||||
|
forward_batch.attn_cp_metadata = prepare_context_parallel_metadata(
|
||||||
len(input_ids),
|
len(input_ids),
|
||||||
self.cp_rank,
|
self.cp_rank,
|
||||||
self.cp_size,
|
self.cp_size,
|
||||||
|
|||||||
@@ -55,13 +55,9 @@ from sglang.srt.layers.activation import SiluAndMul
|
|||||||
from sglang.srt.layers.amx_utils import PackWeightMethod
|
from sglang.srt.layers.amx_utils import PackWeightMethod
|
||||||
from sglang.srt.layers.attention.nsa.nsa_indexer import Indexer
|
from sglang.srt.layers.attention.nsa.nsa_indexer import Indexer
|
||||||
from sglang.srt.layers.attention.nsa.utils import (
|
from sglang.srt.layers.attention.nsa.utils import (
|
||||||
can_cp_split,
|
can_nsa_cp_split,
|
||||||
cp_all_gather_rerange_output,
|
|
||||||
cp_split_and_rebuild_data,
|
|
||||||
cp_split_and_rebuild_position,
|
|
||||||
is_nsa_enable_prefill_cp,
|
is_nsa_enable_prefill_cp,
|
||||||
nsa_use_prefill_cp,
|
nsa_use_prefill_cp,
|
||||||
prepare_input_dp_with_cp_dsa,
|
|
||||||
)
|
)
|
||||||
from sglang.srt.layers.communicator import (
|
from sglang.srt.layers.communicator import (
|
||||||
LayerCommunicator,
|
LayerCommunicator,
|
||||||
@@ -112,6 +108,12 @@ from sglang.srt.layers.quantization.fp8 import Fp8Config
|
|||||||
from sglang.srt.layers.radix_attention import RadixAttention
|
from sglang.srt.layers.radix_attention import RadixAttention
|
||||||
from sglang.srt.layers.rotary_embedding import get_rope_wrapper
|
from sglang.srt.layers.rotary_embedding import get_rope_wrapper
|
||||||
from sglang.srt.layers.utils import PPMissingLayer
|
from sglang.srt.layers.utils import PPMissingLayer
|
||||||
|
from sglang.srt.layers.utils.cp_utils import (
|
||||||
|
cp_all_gather_rerange_output,
|
||||||
|
cp_split_and_rebuild_data,
|
||||||
|
cp_split_and_rebuild_position,
|
||||||
|
prepare_context_parallel_metadata,
|
||||||
|
)
|
||||||
from sglang.srt.layers.vocab_parallel_embedding import (
|
from sglang.srt.layers.vocab_parallel_embedding import (
|
||||||
ParallelLMHead,
|
ParallelLMHead,
|
||||||
VocabParallelEmbedding,
|
VocabParallelEmbedding,
|
||||||
@@ -2288,8 +2290,10 @@ class DeepseekV2ForCausalLM(nn.Module, DeepseekV2WeightLoaderMixin):
|
|||||||
pp_proxy_tensors: Optional[PPProxyTensors] = None,
|
pp_proxy_tensors: Optional[PPProxyTensors] = None,
|
||||||
) -> torch.Tensor:
|
) -> torch.Tensor:
|
||||||
if self.nsa_enable_prefill_cp:
|
if self.nsa_enable_prefill_cp:
|
||||||
if can_cp_split(len(input_ids), self.cp_size, self.use_nsa, forward_batch):
|
if can_nsa_cp_split(
|
||||||
forward_batch.nsa_cp_metadata = prepare_input_dp_with_cp_dsa(
|
len(input_ids), self.cp_size, self.use_nsa, forward_batch
|
||||||
|
):
|
||||||
|
forward_batch.attn_cp_metadata = prepare_context_parallel_metadata(
|
||||||
len(input_ids),
|
len(input_ids),
|
||||||
self.cp_rank,
|
self.cp_rank,
|
||||||
self.cp_size,
|
self.cp_size,
|
||||||
|
|||||||
Reference in New Issue
Block a user