Opt tp: tp attn support tp reduce scattered input (#10568)
This commit is contained in:
@@ -394,6 +394,7 @@ Please consult the documentation below and [server_args.py](https://github.com/s
|
|||||||
| `--enable-return-hidden-states` | Enable returning hidden states with responses. | `False` | bool flag (set to enable) |
|
| `--enable-return-hidden-states` | Enable returning hidden states with responses. | `False` | bool flag (set to enable) |
|
||||||
| `--scheduler-recv-interval` | The interval to poll requests in scheduler. Can be set to >1 to reduce the overhead of this. | `1` | Type: int |
|
| `--scheduler-recv-interval` | The interval to poll requests in scheduler. Can be set to >1 to reduce the overhead of this. | `1` | Type: int |
|
||||||
| `--numa-node` | Sets the numa node for the subprocesses. i-th element corresponds to i-th subprocess. | `None` | List[int] |
|
| `--numa-node` | Sets the numa node for the subprocesses. i-th element corresponds to i-th subprocess. | `None` | List[int] |
|
||||||
|
| `--enable-attn-tp-input-scattered` | Allow input of attention to be scattered when only using tensor parallelism, to reduce the computational load of operations such as qkv latent. | `False` | bool flag (set to enable) |
|
||||||
|
|
||||||
## Debug tensor dumps
|
## Debug tensor dumps
|
||||||
| Argument | Description | Defaults | Options |
|
| Argument | Description | Defaults | Options |
|
||||||
|
|||||||
@@ -11,15 +11,17 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
|
import logging
|
||||||
|
from contextlib import contextmanager
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from enum import Enum, auto
|
from enum import Enum, auto
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from typing import Dict, List, Optional
|
from typing import Callable, Dict, List, Optional, Tuple
|
||||||
|
|
||||||
import torch
|
import torch
|
||||||
|
|
||||||
from sglang.srt.distributed import (
|
from sglang.srt.distributed import (
|
||||||
|
get_tensor_model_parallel_rank,
|
||||||
get_tensor_model_parallel_world_size,
|
get_tensor_model_parallel_world_size,
|
||||||
get_tp_group,
|
get_tp_group,
|
||||||
tensor_model_parallel_all_reduce,
|
tensor_model_parallel_all_reduce,
|
||||||
@@ -59,9 +61,10 @@ from sglang.srt.utils import (
|
|||||||
prepare_weight_cache,
|
prepare_weight_cache,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
_is_cuda = is_cuda()
|
||||||
_is_flashinfer_available = is_flashinfer_available()
|
_is_flashinfer_available = is_flashinfer_available()
|
||||||
_is_sm90_supported = is_cuda() and is_sm90_supported()
|
_is_sm90_supported = _is_cuda and is_sm90_supported()
|
||||||
_is_sm100_supported = is_cuda() and is_sm100_supported()
|
_is_sm100_supported = _is_cuda and is_sm100_supported()
|
||||||
_use_aiter = get_bool_env_var("SGLANG_USE_AITER") and is_hip()
|
_use_aiter = get_bool_env_var("SGLANG_USE_AITER") and is_hip()
|
||||||
_is_gfx95_supported = is_gfx95_supported()
|
_is_gfx95_supported = is_gfx95_supported()
|
||||||
|
|
||||||
@@ -92,6 +95,119 @@ class ScatterMode(Enum):
|
|||||||
return ScatterMode.TP_ATTN_FULL
|
return ScatterMode.TP_ATTN_FULL
|
||||||
|
|
||||||
|
|
||||||
|
class AttentionInputs:
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
hidden_states: torch.Tensor,
|
||||||
|
forward_batch: ForwardBatch,
|
||||||
|
qkv_latent_func: Callable,
|
||||||
|
):
|
||||||
|
self.hidden_states_local = hidden_states
|
||||||
|
self.forward_batch = forward_batch
|
||||||
|
self.qkv_latent_func = qkv_latent_func
|
||||||
|
self.hidden_states_ = None
|
||||||
|
self.qkv_latent_ = None
|
||||||
|
|
||||||
|
def tp_all_gather_hidden_states(self, hidden_states, forward_batch):
|
||||||
|
total_tokens = forward_batch.input_ids.shape[0]
|
||||||
|
output = hidden_states.new_empty((total_tokens, hidden_states.shape[-1]))
|
||||||
|
get_tp_group().all_gather_into_tensor(output, hidden_states)
|
||||||
|
return output
|
||||||
|
|
||||||
|
def fetch_qkv_latent(self):
|
||||||
|
if self.qkv_latent_ is not None:
|
||||||
|
return self.qkv_latent_
|
||||||
|
assert self.qkv_latent_func is not None
|
||||||
|
self.qkv_latent_ = self.qkv_latent_func(
|
||||||
|
self.hidden_states_local, self.forward_batch
|
||||||
|
)
|
||||||
|
if get_attn_tp_context().input_scattered:
|
||||||
|
self.qkv_latent_ = self.tp_all_gather_hidden_states(
|
||||||
|
self.qkv_latent_, self.forward_batch
|
||||||
|
)
|
||||||
|
return self.qkv_latent_
|
||||||
|
|
||||||
|
def fetch_hidden_states(self):
|
||||||
|
if self.hidden_states_ is not None:
|
||||||
|
return self.hidden_states_
|
||||||
|
self.hidden_states_ = self.hidden_states_local
|
||||||
|
if get_attn_tp_context().input_scattered:
|
||||||
|
self.hidden_states_ = self.tp_all_gather_hidden_states(
|
||||||
|
self.hidden_states_, self.forward_batch
|
||||||
|
)
|
||||||
|
return self.hidden_states_
|
||||||
|
|
||||||
|
|
||||||
|
class AttnTpContext:
|
||||||
|
def __init__(self):
|
||||||
|
self.allow_input_scattered = False
|
||||||
|
self.input_scattered_ = False
|
||||||
|
self.attn_inputs_: Optional[AttentionInputs] = None
|
||||||
|
|
||||||
|
def init_context(self, q_lora_rank, is_nsa):
|
||||||
|
self.allow_input_scattered = (
|
||||||
|
get_global_server_args().enable_attn_tp_input_scattered
|
||||||
|
and _is_cuda
|
||||||
|
and q_lora_rank is not None
|
||||||
|
and not is_nsa
|
||||||
|
and get_tensor_model_parallel_world_size() > 1
|
||||||
|
and not is_dp_attention_enabled()
|
||||||
|
and get_moe_a2a_backend().is_none()
|
||||||
|
and not enable_moe_dense_fully_dp()
|
||||||
|
and not get_global_server_args().enable_piecewise_cuda_graph
|
||||||
|
and get_global_server_args().speculative_algorithm != "EAGLE3"
|
||||||
|
)
|
||||||
|
if get_global_server_args().enable_attn_tp_input_scattered:
|
||||||
|
if not self.allow_input_scattered:
|
||||||
|
logging.info(
|
||||||
|
"attn_tp_input_scattered is not enabled while other conditions are not met"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
logging.info("attn_tp_input_scattered is enabled")
|
||||||
|
|
||||||
|
def use_input_scattered(self, forward_batch: ForwardBatch):
|
||||||
|
return (
|
||||||
|
self.allow_input_scattered
|
||||||
|
and forward_batch.forward_mode.is_extend()
|
||||||
|
and not forward_batch.forward_mode.is_target_verify()
|
||||||
|
and not forward_batch.forward_mode.is_draft_extend()
|
||||||
|
and forward_batch.input_ids is not None
|
||||||
|
and not forward_batch.can_run_tbo
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def input_scattered(self):
|
||||||
|
return self.input_scattered_
|
||||||
|
|
||||||
|
def set_attn_inputs(self, attn_inputs: AttentionInputs):
|
||||||
|
self.attn_inputs_ = attn_inputs
|
||||||
|
|
||||||
|
def fetch_qkv_latent(self):
|
||||||
|
assert self.attn_inputs_ is not None
|
||||||
|
return self.attn_inputs_.fetch_qkv_latent()
|
||||||
|
|
||||||
|
def fetch_hidden_states(self):
|
||||||
|
assert self.attn_inputs_ is not None
|
||||||
|
return self.attn_inputs_.fetch_hidden_states()
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def maybe_input_scattered(self, forward_batch: ForwardBatch):
|
||||||
|
flag = self.use_input_scattered(forward_batch)
|
||||||
|
old_flag = self.input_scattered
|
||||||
|
self.input_scattered_ = flag
|
||||||
|
yield
|
||||||
|
self.input_scattered_ = old_flag
|
||||||
|
self.attn_inputs_ = None
|
||||||
|
|
||||||
|
|
||||||
|
ATTN_TP_CONTEXT = AttnTpContext()
|
||||||
|
|
||||||
|
|
||||||
|
def get_attn_tp_context():
|
||||||
|
return ATTN_TP_CONTEXT
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class _LayerModeComputationContext:
|
class _LayerModeComputationContext:
|
||||||
num_layers: int
|
num_layers: int
|
||||||
@@ -188,12 +304,14 @@ class LayerCommunicator:
|
|||||||
# Reduce scatter requires skipping all-reduce in model code after MoE/MLP, so only enable for models which have that implemented. Remove flag once done for all models that use LayerCommunicator.
|
# Reduce scatter requires skipping all-reduce in model code after MoE/MLP, so only enable for models which have that implemented. Remove flag once done for all models that use LayerCommunicator.
|
||||||
allow_reduce_scatter: bool = False,
|
allow_reduce_scatter: bool = False,
|
||||||
is_last_layer: bool = False,
|
is_last_layer: bool = False,
|
||||||
|
qkv_latent_func: Optional[Callable] = None,
|
||||||
):
|
):
|
||||||
self.layer_scatter_modes = layer_scatter_modes
|
self.layer_scatter_modes = layer_scatter_modes
|
||||||
self.input_layernorm = input_layernorm
|
self.input_layernorm = input_layernorm
|
||||||
self.post_attention_layernorm = post_attention_layernorm
|
self.post_attention_layernorm = post_attention_layernorm
|
||||||
self.allow_reduce_scatter = allow_reduce_scatter
|
self.allow_reduce_scatter = allow_reduce_scatter
|
||||||
self.is_last_layer = is_last_layer
|
self.is_last_layer = is_last_layer
|
||||||
|
self.qkv_latent_func = qkv_latent_func
|
||||||
|
|
||||||
self._context = CommunicateContext.init_new()
|
self._context = CommunicateContext.init_new()
|
||||||
self._communicate_simple_fn = CommunicateSimpleFn.get_fn(
|
self._communicate_simple_fn = CommunicateSimpleFn.get_fn(
|
||||||
@@ -252,6 +370,11 @@ class LayerCommunicator:
|
|||||||
forward_batch: ForwardBatch,
|
forward_batch: ForwardBatch,
|
||||||
quant_format: str = "",
|
quant_format: str = "",
|
||||||
):
|
):
|
||||||
|
if get_attn_tp_context().input_scattered:
|
||||||
|
hidden_states, residual = self._tp_reduce_scatter(
|
||||||
|
hidden_states,
|
||||||
|
residual,
|
||||||
|
)
|
||||||
if hidden_states.shape[0] == 0:
|
if hidden_states.shape[0] == 0:
|
||||||
residual = hidden_states
|
residual = hidden_states
|
||||||
else:
|
else:
|
||||||
@@ -335,9 +458,32 @@ class LayerCommunicator:
|
|||||||
forward_batch=forward_batch,
|
forward_batch=forward_batch,
|
||||||
context=self._context,
|
context=self._context,
|
||||||
)
|
)
|
||||||
|
if self.qkv_latent_func is not None:
|
||||||
|
attn_inputs = AttentionInputs(
|
||||||
|
hidden_states, forward_batch, self.qkv_latent_func
|
||||||
|
)
|
||||||
|
get_attn_tp_context().set_attn_inputs(attn_inputs)
|
||||||
return hidden_states, residual
|
return hidden_states, residual
|
||||||
|
|
||||||
|
def _tp_reduce_scatter(
|
||||||
|
self,
|
||||||
|
hidden_states: torch.Tensor,
|
||||||
|
residual: torch.Tensor,
|
||||||
|
) -> Tuple[torch.Tensor, torch.Tensor]:
|
||||||
|
if hidden_states.shape[0] == 0:
|
||||||
|
return hidden_states, hidden_states
|
||||||
|
assert (
|
||||||
|
hidden_states.shape[0] % self._context.tp_size == 0
|
||||||
|
), f"Expected total tokens {hidden_states.shape[0]} % tp_size {self._context.tp_size} to be 0"
|
||||||
|
local_tokens = hidden_states.shape[0] // self._context.tp_size
|
||||||
|
output = hidden_states.new_empty(local_tokens, *hidden_states.shape[1:])
|
||||||
|
get_tp_group().reduce_scatter_tensor(output, hidden_states)
|
||||||
|
if residual is not None:
|
||||||
|
residual = residual.tensor_split(self._context.tp_size)[
|
||||||
|
self._context.tp_rank
|
||||||
|
]
|
||||||
|
return output, residual
|
||||||
|
|
||||||
def prepare_mlp(
|
def prepare_mlp(
|
||||||
self,
|
self,
|
||||||
hidden_states: torch.Tensor,
|
hidden_states: torch.Tensor,
|
||||||
@@ -371,12 +517,17 @@ class LayerCommunicator:
|
|||||||
)
|
)
|
||||||
|
|
||||||
def should_use_reduce_scatter(self, forward_batch: ForwardBatch):
|
def should_use_reduce_scatter(self, forward_batch: ForwardBatch):
|
||||||
return (
|
if not self.allow_reduce_scatter:
|
||||||
self.allow_reduce_scatter
|
return False
|
||||||
and self._communicate_summable_tensor_pair_fn
|
if (
|
||||||
|
self._communicate_summable_tensor_pair_fn
|
||||||
is CommunicateSummableTensorPairFn._scatter_hidden_states
|
is CommunicateSummableTensorPairFn._scatter_hidden_states
|
||||||
and forward_batch.dp_padding_mode.is_max_len()
|
and forward_batch.dp_padding_mode.is_max_len()
|
||||||
)
|
):
|
||||||
|
return True
|
||||||
|
if get_attn_tp_context().input_scattered and not self.is_last_layer:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def should_fuse_mlp_allreduce_with_next_layer(
|
def should_fuse_mlp_allreduce_with_next_layer(
|
||||||
self, forward_batch: ForwardBatch
|
self, forward_batch: ForwardBatch
|
||||||
@@ -388,6 +539,9 @@ class LayerCommunicator:
|
|||||||
):
|
):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
if get_attn_tp_context().input_scattered:
|
||||||
|
return False
|
||||||
|
|
||||||
batch_size = (
|
batch_size = (
|
||||||
forward_batch.input_ids.shape[0]
|
forward_batch.input_ids.shape[0]
|
||||||
if hasattr(forward_batch, "input_ids")
|
if hasattr(forward_batch, "input_ids")
|
||||||
@@ -422,6 +576,7 @@ class CommunicateContext:
|
|||||||
attn_dp_size: int
|
attn_dp_size: int
|
||||||
tp_size: int
|
tp_size: int
|
||||||
cache = None
|
cache = None
|
||||||
|
tp_rank: int
|
||||||
|
|
||||||
def is_same_group_size(self, a: ScatterMode, b: ScatterMode):
|
def is_same_group_size(self, a: ScatterMode, b: ScatterMode):
|
||||||
return self.process_group_sizes[a] == self.process_group_sizes[b]
|
return self.process_group_sizes[a] == self.process_group_sizes[b]
|
||||||
@@ -432,6 +587,7 @@ class CommunicateContext:
|
|||||||
attn_tp_size = get_attention_tp_size()
|
attn_tp_size = get_attention_tp_size()
|
||||||
attn_dp_size = get_attention_dp_size()
|
attn_dp_size = get_attention_dp_size()
|
||||||
tp_size = get_tensor_model_parallel_world_size()
|
tp_size = get_tensor_model_parallel_world_size()
|
||||||
|
tp_rank = get_tensor_model_parallel_rank()
|
||||||
process_group_sizes = {
|
process_group_sizes = {
|
||||||
ScatterMode.SCATTERED: 1,
|
ScatterMode.SCATTERED: 1,
|
||||||
ScatterMode.TP_ATTN_FULL: attn_tp_size,
|
ScatterMode.TP_ATTN_FULL: attn_tp_size,
|
||||||
@@ -444,6 +600,7 @@ class CommunicateContext:
|
|||||||
attn_tp_size=attn_tp_size,
|
attn_tp_size=attn_tp_size,
|
||||||
attn_dp_size=attn_dp_size,
|
attn_dp_size=attn_dp_size,
|
||||||
tp_size=tp_size,
|
tp_size=tp_size,
|
||||||
|
tp_rank=tp_rank,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -566,6 +723,14 @@ class CommunicateWithAllReduceAndLayerNormFn:
|
|||||||
*,
|
*,
|
||||||
residual_input_mode,
|
residual_input_mode,
|
||||||
):
|
):
|
||||||
|
if get_attn_tp_context().input_scattered:
|
||||||
|
return CommunicateWithAllReduceAndLayerNormFn._tp_all_reduce_with_scattered_residual(
|
||||||
|
hidden_states,
|
||||||
|
residual,
|
||||||
|
layernorm,
|
||||||
|
context,
|
||||||
|
)
|
||||||
|
|
||||||
if residual_input_mode == ScatterMode.SCATTERED and context.attn_tp_size > 1:
|
if residual_input_mode == ScatterMode.SCATTERED and context.attn_tp_size > 1:
|
||||||
residual, local_residual = (
|
residual, local_residual = (
|
||||||
get_local_dp_buffer(),
|
get_local_dp_buffer(),
|
||||||
@@ -637,6 +802,22 @@ class CommunicateWithAllReduceAndLayerNormFn:
|
|||||||
hidden_states, residual = layernorm(hidden_states, residual)
|
hidden_states, residual = layernorm(hidden_states, residual)
|
||||||
return hidden_states, residual
|
return hidden_states, residual
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _tp_all_reduce_with_scattered_residual(
|
||||||
|
hidden_states: torch.Tensor,
|
||||||
|
residual: torch.Tensor,
|
||||||
|
layernorm: torch.nn.Module,
|
||||||
|
context: CommunicateContext,
|
||||||
|
):
|
||||||
|
if hidden_states.shape[0] == 0:
|
||||||
|
return hidden_states, hidden_states
|
||||||
|
|
||||||
|
scattered_states = hidden_states.tensor_split(context.tp_size)[context.tp_rank]
|
||||||
|
scattered_states += residual
|
||||||
|
residual = tensor_model_parallel_all_reduce(hidden_states)
|
||||||
|
hidden_states = layernorm(residual)
|
||||||
|
return hidden_states, residual
|
||||||
|
|
||||||
|
|
||||||
class CommunicateSummableTensorPairFn:
|
class CommunicateSummableTensorPairFn:
|
||||||
"""It is allowed to make (hidden_states, residual) := (hidden_states + residual, None) if needed."""
|
"""It is allowed to make (hidden_states, residual) := (hidden_states + residual, None) if needed."""
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ from sglang.srt.distributed.device_communicators.pynccl_allocator import (
|
|||||||
use_symmetric_memory,
|
use_symmetric_memory,
|
||||||
)
|
)
|
||||||
from sglang.srt.layers.amx_utils import PackWeightMethod
|
from sglang.srt.layers.amx_utils import PackWeightMethod
|
||||||
|
from sglang.srt.layers.communicator import get_attn_tp_context
|
||||||
from sglang.srt.layers.dp_attention import get_attention_tp_rank, get_attention_tp_size
|
from sglang.srt.layers.dp_attention import get_attention_tp_rank, get_attention_tp_size
|
||||||
from sglang.srt.layers.parameter import BasevLLMParameter
|
from sglang.srt.layers.parameter import BasevLLMParameter
|
||||||
from sglang.srt.layers.quantization.base_config import (
|
from sglang.srt.layers.quantization.base_config import (
|
||||||
@@ -478,11 +479,10 @@ class VocabParallelEmbedding(torch.nn.Module):
|
|||||||
# Mask the output embedding.
|
# Mask the output embedding.
|
||||||
if self.tp_size > 1:
|
if self.tp_size > 1:
|
||||||
output_parallel.masked_fill_(input_mask.unsqueeze(-1), 0)
|
output_parallel.masked_fill_(input_mask.unsqueeze(-1), 0)
|
||||||
|
if not get_attn_tp_context().input_scattered:
|
||||||
# Reduce across all the model parallel GPUs.
|
# Reduce across all the model parallel GPUs.
|
||||||
output = tensor_model_parallel_all_reduce(output_parallel)
|
output_parallel = tensor_model_parallel_all_reduce(output_parallel)
|
||||||
else:
|
return output_parallel
|
||||||
output = output_parallel
|
|
||||||
return output
|
|
||||||
|
|
||||||
def extra_repr(self) -> str:
|
def extra_repr(self) -> str:
|
||||||
s = f"num_embeddings={self.num_embeddings_per_partition}"
|
s = f"num_embeddings={self.num_embeddings_per_partition}"
|
||||||
|
|||||||
@@ -38,7 +38,10 @@ import torch
|
|||||||
import triton
|
import triton
|
||||||
import triton.language as tl
|
import triton.language as tl
|
||||||
|
|
||||||
from sglang.srt.distributed.parallel_state import get_moe_expert_parallel_world_size
|
from sglang.srt.distributed.parallel_state import (
|
||||||
|
get_moe_expert_parallel_world_size,
|
||||||
|
get_tensor_model_parallel_world_size,
|
||||||
|
)
|
||||||
from sglang.srt.layers.attention.utils import create_flashinfer_kv_indices_triton
|
from sglang.srt.layers.attention.utils import create_flashinfer_kv_indices_triton
|
||||||
from sglang.srt.layers.dp_attention import (
|
from sglang.srt.layers.dp_attention import (
|
||||||
DpPaddingMode,
|
DpPaddingMode,
|
||||||
@@ -766,6 +769,13 @@ class ForwardBatch:
|
|||||||
else:
|
else:
|
||||||
bs = self.batch_size = num_tokens
|
bs = self.batch_size = num_tokens
|
||||||
|
|
||||||
|
# padding
|
||||||
|
self._pad_inputs_to_size(model_runner, num_tokens, bs)
|
||||||
|
self.global_num_tokens_cpu = global_num_tokens
|
||||||
|
global_num_tokens_pinned = torch.tensor(global_num_tokens, pin_memory=True)
|
||||||
|
self.global_num_tokens_gpu.copy_(global_num_tokens_pinned, non_blocking=True)
|
||||||
|
|
||||||
|
def _pad_inputs_to_size(self, model_runner: ModelRunner, num_tokens, bs):
|
||||||
# padding
|
# padding
|
||||||
self.input_ids = self._pad_tensor_to_size(self.input_ids, num_tokens)
|
self.input_ids = self._pad_tensor_to_size(self.input_ids, num_tokens)
|
||||||
self.req_pool_indices = self._pad_tensor_to_size(self.req_pool_indices, bs)
|
self.req_pool_indices = self._pad_tensor_to_size(self.req_pool_indices, bs)
|
||||||
@@ -788,9 +798,6 @@ class ForwardBatch:
|
|||||||
if self.encoder_lens is not None:
|
if self.encoder_lens is not None:
|
||||||
self.encoder_lens = self._pad_tensor_to_size(self.encoder_lens, bs)
|
self.encoder_lens = self._pad_tensor_to_size(self.encoder_lens, bs)
|
||||||
self.positions = self._pad_tensor_to_size(self.positions, num_tokens)
|
self.positions = self._pad_tensor_to_size(self.positions, num_tokens)
|
||||||
self.global_num_tokens_cpu = global_num_tokens
|
|
||||||
global_num_tokens_pinned = torch.tensor(global_num_tokens, pin_memory=True)
|
|
||||||
self.global_num_tokens_gpu.copy_(global_num_tokens_pinned, non_blocking=True)
|
|
||||||
|
|
||||||
if self.mrope_positions is not None:
|
if self.mrope_positions is not None:
|
||||||
self.mrope_positions = self._pad_tensor_to_size(self.mrope_positions, bs)
|
self.mrope_positions = self._pad_tensor_to_size(self.mrope_positions, bs)
|
||||||
@@ -818,6 +825,19 @@ class ForwardBatch:
|
|||||||
spec_info.hidden_states, num_tokens
|
spec_info.hidden_states, num_tokens
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def prepare_attn_tp_scatter_input(self, model_runner: ModelRunner):
|
||||||
|
from sglang.srt.layers.communicator import get_attn_tp_context
|
||||||
|
|
||||||
|
attn_tp_context = get_attn_tp_context()
|
||||||
|
input_scattered = attn_tp_context.use_input_scattered(self)
|
||||||
|
if not input_scattered:
|
||||||
|
return
|
||||||
|
assert self.forward_mode.is_extend()
|
||||||
|
tokens = self.input_ids.shape[0]
|
||||||
|
rank_size = get_tensor_model_parallel_world_size()
|
||||||
|
tokens_padded = (tokens + rank_size - 1) // rank_size * rank_size
|
||||||
|
self._pad_inputs_to_size(model_runner, tokens_padded, self.batch_size)
|
||||||
|
|
||||||
def post_forward_mlp_sync_batch(self, logits_output: LogitsProcessorOutput):
|
def post_forward_mlp_sync_batch(self, logits_output: LogitsProcessorOutput):
|
||||||
|
|
||||||
self.forward_mode = getattr(self, "_original_forward_mode", self.forward_mode)
|
self.forward_mode = getattr(self, "_original_forward_mode", self.forward_mode)
|
||||||
|
|||||||
@@ -2221,6 +2221,8 @@ class ModelRunner:
|
|||||||
# For MLP sync
|
# For MLP sync
|
||||||
if forward_batch.global_num_tokens_cpu is not None:
|
if forward_batch.global_num_tokens_cpu is not None:
|
||||||
forward_batch.prepare_mlp_sync_batch(self)
|
forward_batch.prepare_mlp_sync_batch(self)
|
||||||
|
else:
|
||||||
|
forward_batch.prepare_attn_tp_scatter_input(self)
|
||||||
|
|
||||||
if forward_batch.forward_mode.is_decode():
|
if forward_batch.forward_mode.is_decode():
|
||||||
ret = self.forward_decode(
|
ret = self.forward_decode(
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ from sglang.srt.layers.communicator import (
|
|||||||
LayerCommunicator,
|
LayerCommunicator,
|
||||||
LayerScatterModes,
|
LayerScatterModes,
|
||||||
enable_moe_dense_fully_dp,
|
enable_moe_dense_fully_dp,
|
||||||
|
get_attn_tp_context,
|
||||||
)
|
)
|
||||||
from sglang.srt.layers.dp_attention import (
|
from sglang.srt.layers.dp_attention import (
|
||||||
get_attention_tp_rank,
|
get_attention_tp_rank,
|
||||||
@@ -1413,13 +1414,19 @@ class DeepseekV2AttentionMLA(nn.Module):
|
|||||||
|
|
||||||
# when hidden_states is a tuple of tensors, the tuple will include quantized weight and scale tensor
|
# when hidden_states is a tuple of tensors, the tuple will include quantized weight and scale tensor
|
||||||
if isinstance(hidden_states, tuple):
|
if isinstance(hidden_states, tuple):
|
||||||
if hidden_states[0].shape[0] == 0:
|
if (
|
||||||
|
not get_attn_tp_context().input_scattered
|
||||||
|
and hidden_states[0].shape[0] == 0
|
||||||
|
):
|
||||||
assert (
|
assert (
|
||||||
not self.o_proj.reduce_results
|
not self.o_proj.reduce_results
|
||||||
), "short-circuiting allreduce will lead to hangs"
|
), "short-circuiting allreduce will lead to hangs"
|
||||||
return hidden_states[0]
|
return hidden_states[0]
|
||||||
else:
|
else:
|
||||||
if hidden_states.shape[0] == 0:
|
if (
|
||||||
|
not get_attn_tp_context().input_scattered
|
||||||
|
and hidden_states.shape[0] == 0
|
||||||
|
):
|
||||||
assert (
|
assert (
|
||||||
not self.o_proj.reduce_results
|
not self.o_proj.reduce_results
|
||||||
), "short-circuiting allreduce will lead to hangs"
|
), "short-circuiting allreduce will lead to hangs"
|
||||||
@@ -1502,6 +1509,23 @@ class DeepseekV2AttentionMLA(nn.Module):
|
|||||||
else:
|
else:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def prepare_qkv_latent(
|
||||||
|
self, hidden_states: torch.Tensor, forward_batch: ForwardBatch
|
||||||
|
):
|
||||||
|
assert self.q_lora_rank is not None
|
||||||
|
if (
|
||||||
|
(not isinstance(hidden_states, tuple))
|
||||||
|
and hidden_states.shape[0] >= 1
|
||||||
|
and hidden_states.shape[0] <= 16
|
||||||
|
and self.use_min_latency_fused_a_gemm
|
||||||
|
):
|
||||||
|
qkv_latent = dsv3_fused_a_gemm(
|
||||||
|
hidden_states, self.fused_qkv_a_proj_with_mqa.weight.T
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
qkv_latent = self.fused_qkv_a_proj_with_mqa(hidden_states)[0]
|
||||||
|
return qkv_latent
|
||||||
|
|
||||||
def forward_normal_prepare(
|
def forward_normal_prepare(
|
||||||
self,
|
self,
|
||||||
positions: torch.Tensor,
|
positions: torch.Tensor,
|
||||||
@@ -1510,8 +1534,13 @@ class DeepseekV2AttentionMLA(nn.Module):
|
|||||||
zero_allocator: BumpAllocator,
|
zero_allocator: BumpAllocator,
|
||||||
):
|
):
|
||||||
if self.q_lora_rank is not None:
|
if self.q_lora_rank is not None:
|
||||||
q, latent_cache = self.fused_qkv_a_proj_with_mqa(hidden_states)[0].split(
|
q, latent_cache = (
|
||||||
[self.q_lora_rank, self.kv_lora_rank + self.qk_rope_head_dim], dim=-1
|
get_attn_tp_context()
|
||||||
|
.fetch_qkv_latent()
|
||||||
|
.split(
|
||||||
|
[self.q_lora_rank, self.kv_lora_rank + self.qk_rope_head_dim],
|
||||||
|
dim=-1,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
# NSA Indexer: cache quantized keys, auto-skip topk for sequences <= nsa_index_topk
|
# NSA Indexer: cache quantized keys, auto-skip topk for sequences <= nsa_index_topk
|
||||||
@@ -1634,18 +1663,13 @@ class DeepseekV2AttentionMLA(nn.Module):
|
|||||||
|
|
||||||
q_lora = None
|
q_lora = None
|
||||||
if self.q_lora_rank is not None:
|
if self.q_lora_rank is not None:
|
||||||
if (
|
q, latent_cache = (
|
||||||
(not isinstance(hidden_states, tuple))
|
get_attn_tp_context()
|
||||||
and hidden_states.shape[0] <= 16
|
.fetch_qkv_latent()
|
||||||
and self.use_min_latency_fused_a_gemm
|
.split(
|
||||||
):
|
[self.q_lora_rank, self.kv_lora_rank + self.qk_rope_head_dim],
|
||||||
fused_qkv_a_proj_out = dsv3_fused_a_gemm(
|
dim=-1,
|
||||||
hidden_states, self.fused_qkv_a_proj_with_mqa.weight.T
|
|
||||||
)
|
)
|
||||||
else:
|
|
||||||
fused_qkv_a_proj_out = self.fused_qkv_a_proj_with_mqa(hidden_states)[0]
|
|
||||||
q, latent_cache = fused_qkv_a_proj_out.split(
|
|
||||||
[self.q_lora_rank, self.kv_lora_rank + self.qk_rope_head_dim], dim=-1
|
|
||||||
)
|
)
|
||||||
k_nope = latent_cache[..., : self.kv_lora_rank]
|
k_nope = latent_cache[..., : self.kv_lora_rank]
|
||||||
|
|
||||||
@@ -2742,6 +2766,7 @@ class DeepseekV2DecoderLayer(nn.Module):
|
|||||||
is_last_layer=(
|
is_last_layer=(
|
||||||
is_nextn or (self.layer_id == self.config.num_hidden_layers - 1)
|
is_nextn or (self.layer_id == self.config.num_hidden_layers - 1)
|
||||||
),
|
),
|
||||||
|
qkv_latent_func=self.self_attn.prepare_qkv_latent,
|
||||||
)
|
)
|
||||||
|
|
||||||
def _is_layer_sparse(self, layer_id: int, is_nextn: bool) -> bool:
|
def _is_layer_sparse(self, layer_id: int, is_nextn: bool) -> bool:
|
||||||
@@ -3163,6 +3188,9 @@ class DeepseekV2ForCausalLM(nn.Module):
|
|||||||
)
|
)
|
||||||
self.capture_aux_hidden_states = False
|
self.capture_aux_hidden_states = False
|
||||||
|
|
||||||
|
q_lora_rank = config.q_lora_rank if hasattr(config, "q_lora_rank") else None
|
||||||
|
get_attn_tp_context().init_context(q_lora_rank, is_deepseek_nsa(config))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def routed_experts_weights_of_layer(self):
|
def routed_experts_weights_of_layer(self):
|
||||||
return self._routed_experts_weights_of_layer.value
|
return self._routed_experts_weights_of_layer.value
|
||||||
@@ -3221,6 +3249,7 @@ class DeepseekV2ForCausalLM(nn.Module):
|
|||||||
input_embeds: torch.Tensor = None,
|
input_embeds: torch.Tensor = None,
|
||||||
pp_proxy_tensors: Optional[PPProxyTensors] = None,
|
pp_proxy_tensors: Optional[PPProxyTensors] = None,
|
||||||
) -> torch.Tensor:
|
) -> torch.Tensor:
|
||||||
|
with get_attn_tp_context().maybe_input_scattered(forward_batch):
|
||||||
hidden_states = self.model(
|
hidden_states = self.model(
|
||||||
input_ids, positions, forward_batch, input_embeds, pp_proxy_tensors
|
input_ids, positions, forward_batch, input_embeds, pp_proxy_tensors
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -524,6 +524,7 @@ class ServerArgs:
|
|||||||
numa_node: Optional[List[int]] = None
|
numa_node: Optional[List[int]] = None
|
||||||
enable_deterministic_inference: bool = False
|
enable_deterministic_inference: bool = False
|
||||||
rl_on_policy_target: Optional[str] = None
|
rl_on_policy_target: Optional[str] = None
|
||||||
|
enable_attn_tp_input_scattered: bool = False
|
||||||
|
|
||||||
# Dynamic batch tokenizer
|
# Dynamic batch tokenizer
|
||||||
enable_dynamic_batch_tokenizer: bool = False
|
enable_dynamic_batch_tokenizer: bool = False
|
||||||
@@ -3481,6 +3482,11 @@ class ServerArgs:
|
|||||||
choices=RL_ON_POLICY_TARGET_CHOICES,
|
choices=RL_ON_POLICY_TARGET_CHOICES,
|
||||||
help="The training system that SGLang needs to match for true on-policy.",
|
help="The training system that SGLang needs to match for true on-policy.",
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--enable-attn-tp-input-scattered",
|
||||||
|
action="store_true",
|
||||||
|
help="Allow input of attention to be scattered when only using tensor parallelism, to reduce the computational load of operations such as qkv latent.",
|
||||||
|
)
|
||||||
|
|
||||||
# Dynamic batch tokenizer
|
# Dynamic batch tokenizer
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
|
|||||||
Reference in New Issue
Block a user