[Feature] Enable return routed experts (#12162)

Co-authored-by: yizhang2077 <1109276519@qq.com>
Co-authored-by: Liangsheng Yin <lsyincs@gmail.com>
This commit is contained in:
Junrong Lin
2025-12-21 15:16:43 +08:00
committed by GitHub
co-authored by yizhang2077 Liangsheng Yin
parent 8fe3e37468
commit bed301a5ac
27 changed files with 646 additions and 10 deletions
+6
View File
@@ -565,6 +565,7 @@ async def async_request_sglang_generate(
"stream": not args.disable_stream,
"lora_path": request_func_input.lora_name,
"return_logprob": args.return_logprob,
"return_routed_experts": args.return_routed_experts,
"logprob_start_len": -1,
**request_func_input.extra_request_body,
}
@@ -2809,6 +2810,11 @@ if __name__ == "__main__":
action="store_true",
help="Return logprob.",
)
parser.add_argument(
"--return-routed-experts",
action="store_true",
help="Return routed experts.",
)
parser.add_argument("--seed", type=int, default=1, help="The random seed.")
parser.add_argument(
"--disable-ignore-eos",
+2
View File
@@ -286,6 +286,7 @@ class Engine(EngineBase):
lora_path: Optional[List[Optional[str]]] = None,
custom_logit_processor: Optional[Union[List[str], str]] = None,
return_hidden_states: bool = False,
return_routed_experts: bool = False,
stream: bool = False,
bootstrap_host: Optional[Union[List[str], str]] = None,
bootstrap_port: Optional[Union[List[int], int]] = None,
@@ -321,6 +322,7 @@ class Engine(EngineBase):
lora_path=lora_path,
custom_logit_processor=custom_logit_processor,
return_hidden_states=return_hidden_states,
return_routed_experts=return_routed_experts,
stream=stream,
bootstrap_host=bootstrap_host,
bootstrap_port=bootstrap_port,
@@ -0,0 +1,289 @@
import logging
from abc import ABC
from typing import Optional
import numpy as np
import pybase64
import torch
from sglang.srt.configs.model_config import ModelConfig
from sglang.srt.layers.dp_attention import (
get_attention_dp_rank,
get_dp_local_info,
is_dp_attention_enabled,
)
from sglang.srt.mem_cache.memory_pool import ReqToTokenPool
from sglang.srt.model_executor.forward_batch_info import ForwardBatch
from sglang.srt.server_args import get_global_server_args
logger = logging.getLogger(__name__)
_GB = 1024 * 1024 * 1024
_MB = 1024 * 1024
def get_tensor_size_bytes(t: torch.Tensor):
return np.prod(t.shape) * t.dtype.itemsize
class _RoutedExpertsDeviceCache:
def __init__(
self,
max_running_requests: int,
num_hidden_layers: int,
num_experts_per_tok: int,
num_fused_shared_experts: int,
device: str,
) -> None:
self.buffer = torch.zeros(
(
max(
get_global_server_args().chunked_prefill_size
* get_global_server_args().dp_size,
max_running_requests,
),
num_hidden_layers,
num_experts_per_tok + num_fused_shared_experts,
),
dtype=torch.int32,
device=device,
)
self._finalize_allocation_log()
def get_buffer_size_bytes(self):
assert hasattr(self, "buffer")
return get_tensor_size_bytes(self.buffer)
def capture_fwd_routed_experts(self, layer_id: int, topk_ids: torch.Tensor):
assert layer_id is not None, "capturing routing experts but get layer_id None"
batch, _ = topk_ids.shape
self.buffer[:batch, layer_id, :] = topk_ids
def _finalize_allocation_log(self):
"""Common logging and memory usage computation for captured experts buffers."""
buffer_size_MB = self.get_buffer_size_bytes() / _MB
logger.info(
f"Routing experts device buffer allocated. #shape: {tuple(self.buffer.shape)}, size: {buffer_size_MB:.2f} MB"
)
class _RoutedExpertsHostCache:
def __init__(
self,
num_tokens: int,
num_hidden_layers: int,
num_experts_per_tok: int,
) -> None:
self.num_tokens = num_tokens
self.buffer = torch.zeros(
(
num_tokens,
num_hidden_layers,
num_experts_per_tok,
),
dtype=torch.int32,
device="cpu",
pin_memory=True,
)
self._finalize_allocation_log()
def get_buffer_size_bytes(self):
assert hasattr(self, "buffer")
return get_tensor_size_bytes(self.buffer)
def set_experts_buffer(self, layer_id: int, loc: torch.Tensor, top_k: torch.Tensor):
self.buffer[layer_id, loc, :] = top_k.to(device="cpu", non_blocking=True)
def _finalize_allocation_log(self):
"""Common logging and memory usage computation for captured experts buffers."""
buffer_size_GB = self.get_buffer_size_bytes() / _GB
logger.info(
f"Routing experts host buffer allocated. #tokens: {self.num_tokens}, size: {buffer_size_GB:.2f} GB"
)
class RoutedExpertsCapturer(ABC):
@staticmethod
def create(
enable: bool,
model_config: ModelConfig,
num_fused_shared_experts: int,
num_tokens: int,
max_running_requests: int,
device: str,
):
if enable:
return _RoutedExpertsCapturerReal(
model_config,
num_tokens=num_tokens,
max_running_requests=max_running_requests,
num_fused_shared_experts=num_fused_shared_experts,
device=device,
)
else:
return _RoutedExpertsCapturerNoop()
def _sync_fwd_experts_buffer_DtoH(
self,
forward_batch: ForwardBatch,
can_run_graph: bool,
cuda_graph_batch: int,
):
raise NotImplementedError
def capture(self, layer_id: int, topk_ids: torch.Tensor):
raise NotImplementedError
def get_routed_experts(
self,
req_pool_idx: int,
seqlen: int,
req_to_token_pool: ReqToTokenPool,
):
raise NotImplementedError
def on_forward_end(self, forward_batch, can_run_graph, cuda_graph_batch):
raise NotImplementedError
def get_host_cache(self):
raise NotImplementedError
def get_device_cache(self):
raise NotImplementedError
class _RoutedExpertsCapturerReal(RoutedExpertsCapturer):
"""Capturer for routed experts with host buffer"""
def __init__(
self,
model_config: ModelConfig,
num_tokens: int,
max_running_requests: int,
num_fused_shared_experts: int,
device: str,
):
self.num_fused_shared_experts = num_fused_shared_experts
self.num_hidden_layers = model_config.hf_text_config.num_hidden_layers
self.num_experts_per_tok = model_config.hf_text_config.num_experts_per_tok
self.host_cache = _RoutedExpertsHostCache(
num_tokens=num_tokens,
num_hidden_layers=self.num_hidden_layers,
num_experts_per_tok=self.num_experts_per_tok,
)
self.device_cache = _RoutedExpertsDeviceCache(
max_running_requests=max_running_requests,
num_hidden_layers=self.num_hidden_layers,
num_experts_per_tok=self.num_experts_per_tok,
num_fused_shared_experts=self.num_fused_shared_experts,
device=device,
)
def _sync_fwd_experts_buffer_DtoH(
self,
forward_batch: ForwardBatch,
can_run_graph: bool,
cuda_graph_batch: int,
):
if is_dp_attention_enabled():
local_start_pos, local_num_tokens = get_dp_local_info(forward_batch)
# handle with cuda graph padding
if can_run_graph:
local_start_pos = get_attention_dp_rank() * cuda_graph_batch
local_end_pos = local_start_pos + local_num_tokens
else:
local_end_pos = local_start_pos + local_num_tokens
else:
local_start_pos = 0
local_end_pos = forward_batch.out_cache_loc.shape[0]
# FIXME: sync explicitly here, overlap scheduler breaks here.
out_cache_loc_cpu = forward_batch.out_cache_loc.cpu()
self.host_cache.buffer[out_cache_loc_cpu] = self.device_cache.buffer[
local_start_pos:local_end_pos, :, : self.num_experts_per_tok
].cpu()
def capture(self, layer_id: int, topk_ids: torch.Tensor):
self.device_cache.capture_fwd_routed_experts(layer_id, topk_ids)
def get_routed_experts(
self,
req_pool_idx: int,
seqlen: int,
req_to_token_pool: ReqToTokenPool,
):
cache_pool_idx = (
req_to_token_pool.req_to_token[req_pool_idx][: seqlen - 1].cpu().clone()
)
return self.get_host_cache().buffer[cache_pool_idx]
def on_forward_end(self, forward_batch, can_run_graph, cuda_graph_batch):
self._sync_fwd_experts_buffer_DtoH(
forward_batch=forward_batch,
can_run_graph=can_run_graph,
cuda_graph_batch=cuda_graph_batch,
)
def get_host_cache(self):
return self.host_cache
def get_device_cache(self):
return self.device_cache
class _RoutedExpertsCapturerNoop(RoutedExpertsCapturer):
def __init__(self):
pass
def _sync_fwd_experts_buffer_DtoH(
self,
forward_batch: ForwardBatch,
can_run_graph: bool,
cuda_graph_batch: int,
):
pass
def capture(self, layer_id: int, topk_ids: torch.Tensor):
pass
def get_routed_experts(
self,
req_pool_idx: int,
seqlen: int,
req_to_token_pool: ReqToTokenPool,
):
pass
def on_forward_end(self, forward_batch, can_run_graph, cuda_graph_batch):
pass
def get_host_cache(self):
pass
def get_device_cache(self):
pass
_global_expert_capturer: Optional[RoutedExpertsCapturer] = _RoutedExpertsCapturerNoop()
def get_global_experts_capturer():
return _global_expert_capturer
def set_global_experts_capturer(capturer: RoutedExpertsCapturer):
global _global_expert_capturer
_global_expert_capturer = capturer
def extract_routed_experts_from_meta_info(data):
# To solve the performance issue, we return the experts_ids in base64
# We left this function for user to change it back to normal int32
# See detokenizer_manager::_extract_routed_experts
routed_experts_base64 = data["meta_info"].get("routed_experts", None)
routed_experts = np.frombuffer(
pybase64.b64decode(routed_experts_base64.encode("utf-8")), dtype=np.int32
)
return routed_experts
+11 -1
View File
@@ -48,6 +48,7 @@ from sglang.srt.eplb.expert_location_dispatch import (
)
from sglang.srt.layers.dp_attention import is_allocation_symmetric
from sglang.srt.layers.moe import get_moe_runner_backend
from sglang.srt.layers.moe.routed_experts_capturer import get_global_experts_capturer
from sglang.srt.utils import (
cpu_has_amx_support,
get_bool_env_var,
@@ -203,6 +204,7 @@ class TopK(CustomOp):
self,
top_k: int,
*,
layer_id: Optional[int] = None,
use_grouped_topk: bool = False,
topk_group: Optional[int] = None,
num_expert_group: Optional[int] = None,
@@ -224,6 +226,7 @@ class TopK(CustomOp):
if use_grouped_topk:
assert num_expert_group is not None and topk_group is not None
self.layer_id = layer_id
self.topk_config = TopKConfig(
top_k=top_k,
use_grouped_topk=use_grouped_topk,
@@ -251,6 +254,7 @@ class TopK(CustomOp):
self.topk_config.torch_native = True
return select_experts(
hidden_states=hidden_states,
layer_id=self.layer_id,
router_logits=router_logits,
topk_config=self.topk_config,
num_token_non_padded=num_token_non_padded,
@@ -300,6 +304,7 @@ class TopK(CustomOp):
):
topk_output = select_experts(
hidden_states=hidden_states,
layer_id=self.layer_id,
router_logits=router_logits,
topk_config=self.topk_config,
num_token_non_padded=num_token_non_padded,
@@ -317,6 +322,7 @@ class TopK(CustomOp):
) -> TopKOutput:
return select_experts(
hidden_states=hidden_states,
layer_id=self.layer_id,
router_logits=router_logits,
topk_config=self.topk_config,
num_token_non_padded=num_token_non_padded,
@@ -847,6 +853,7 @@ def select_experts(
router_logits: torch.Tensor,
topk_config: TopKConfig,
*,
layer_id: Optional[int] = None,
num_token_non_padded: Optional[torch.Tensor] = None,
expert_location_dispatch_info: Optional[ExpertLocationDispatchInfo] = None,
) -> StandardTopKOutput:
@@ -974,7 +981,10 @@ def select_experts(
)
get_global_expert_distribution_recorder().on_select_experts(topk_ids=topk_ids)
get_global_experts_capturer().capture(
layer_id=layer_id,
topk_ids=topk_ids,
)
return StandardTopKOutput(topk_weights, topk_ids, router_logits)
@@ -21,6 +21,7 @@ from collections import OrderedDict
from typing import Dict, List, Union
import psutil
import pybase64
import setproctitle
import zmq
@@ -266,8 +267,24 @@ class DetokenizerManager(MultiHttpWorkerDetokenizerMixin):
return output_strs
def _extract_routed_experts(self, recv_obj: BatchTokenIDOutput) -> List[List[int]]:
output_routed_experts = None
if recv_obj.output_routed_experts is not None:
output_routed_experts = [
(
pybase64.b64encode(output_routed_experts.numpy().tobytes()).decode(
"utf-8"
)
if output_routed_experts is not None
else []
)
for output_routed_experts in recv_obj.output_routed_experts
]
return output_routed_experts
def handle_batch_token_id_out(self, recv_obj: BatchTokenIDOutput):
output_strs = self._decode_batch_token_id_output(recv_obj)
output_routed_experts = self._extract_routed_experts(recv_obj)
return BatchStrOutput(
rids=recv_obj.rids,
@@ -294,6 +311,7 @@ class DetokenizerManager(MultiHttpWorkerDetokenizerMixin):
output_token_ids_logprobs_idx=recv_obj.output_token_ids_logprobs_idx,
output_token_entropy_val=recv_obj.output_token_entropy_val,
output_hidden_states=recv_obj.output_hidden_states,
output_routed_experts=output_routed_experts,
placeholder_tokens_idx=None,
placeholder_tokens_val=None,
retraction_counts=recv_obj.retraction_counts,
+14
View File
@@ -23,6 +23,8 @@ from dataclasses import dataclass, field
from enum import Enum
from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional, Union
import torch
from sglang.srt.lora.lora_registry import LoRARef
from sglang.srt.managers.schedule_batch import BaseFinishReason
from sglang.srt.multimodal.mm_utils import has_valid_data
@@ -196,6 +198,8 @@ class GenerateReqInput(BaseReq, APIServingTimingMixin):
log_metrics: bool = True
# Whether to return hidden states
return_hidden_states: Union[List[bool], bool] = False
# Whether to return captured routed experts
return_routed_experts: bool = False
# The modalities of the image data [image, multi-images, video]
modalities: Optional[List[str]] = None
@@ -618,6 +622,7 @@ class GenerateReqInput(BaseReq, APIServingTimingMixin):
if isinstance(self.return_hidden_states, list)
else self.return_hidden_states
),
return_routed_experts=self.return_routed_experts,
modalities=self.modalities[i] if self.modalities else None,
session_params=self.session_params,
lora_path=self.lora_path[i] if self.lora_path is not None else None,
@@ -687,6 +692,9 @@ class TokenizedGenerateReqInput(BaseReq):
# Whether to return hidden states
return_hidden_states: bool = False
# Whether to return captured routed experts
return_routed_experts: bool = False
# The input embeds
input_embeds: Optional[Union[List[List[List[float]]], List[List[float]]]] = None
@@ -953,6 +961,9 @@ class BatchTokenIDOutput(
# Hidden states
output_hidden_states: List[List[float]]
# The routed experts for each output token
output_routed_experts: List[torch.Tensor]
# The information of placeholder tokens (e.g., image token)
# idx is the index of the token in the prompt after expansion.
# val is the length of padded tokens after expansion.
@@ -1032,6 +1043,9 @@ class BatchStrOutput(
# Hidden states
output_hidden_states: List[List[float]]
# The routed experts for each output token
output_routed_experts: List[List[int]]
# The information of placeholder tokens (e.g., image token)
# idx is the index of the token in the prompt after expansion.
# val is the length of padded tokens after expansion.
@@ -270,6 +270,9 @@ def _handle_output_by_index(output, i):
output_hidden_states=_extract_field_by_index(
output, "output_hidden_states", i, check_length=False
),
output_routed_experts=_extract_field_by_index(
output, "output_routed_experts", i, check_length=False
),
placeholder_tokens_idx=None,
placeholder_tokens_val=None,
retraction_counts=_extract_field_by_index(output, "retraction_counts", i),
@@ -489,6 +489,7 @@ class Req:
custom_logit_processor: Optional[str] = None,
require_reasoning: bool = False,
return_hidden_states: bool = False,
return_routed_experts: bool = False,
eos_token_ids: Optional[Set[int]] = None,
bootstrap_host: Optional[str] = None,
bootstrap_port: Optional[int] = None,
@@ -679,6 +680,12 @@ class Req:
self.output_topk_p = None
self.output_topk_index = None
# capture routed experts
self.return_routed_experts = return_routed_experts
self.routed_experts: Optional[torch.Tensor] = (
None # cpu tensor: shape (seqlen, topk)
)
# Embedding (return values)
self.embedding = None
@@ -1043,6 +1050,7 @@ class Req:
self.retraction_count += 1
self.prefix_indices = torch.empty((0,), dtype=torch.int64)
self.routed_experts = None
self.last_node = None
self.swa_uuid_for_lock = None
self.extend_input_len = 0
@@ -1219,6 +1227,9 @@ class ScheduleBatch(ScheduleBatchDisaggregationDecodeMixin):
# Whether to return hidden states
return_hidden_states: bool = False
# Whether to return captured experts
return_routed_experts: bool = False
# Whether this batch is prefill-only (no token generation needed)
is_prefill_only: bool = False
@@ -1266,6 +1277,7 @@ class ScheduleBatch(ScheduleBatchDisaggregationDecodeMixin):
device=req_to_token_pool.device,
spec_algorithm=spec_algorithm,
return_hidden_states=any(req.return_hidden_states for req in reqs),
return_routed_experts=any(req.return_routed_experts for req in reqs),
is_prefill_only=all(req.is_prefill_only for req in reqs),
chunked_req=chunked_req,
dllm_config=dllm_config,
+1
View File
@@ -1413,6 +1413,7 @@ class Scheduler(
custom_logit_processor=recv_req.custom_logit_processor,
require_reasoning=recv_req.require_reasoning,
return_hidden_states=recv_req.return_hidden_states,
return_routed_experts=recv_req.return_routed_experts,
eos_token_ids=self.model_config.hf_eos_token_id,
bootstrap_host=recv_req.bootstrap_host,
bootstrap_port=recv_req.bootstrap_port,
@@ -9,6 +9,7 @@ import torch
from sglang.srt.disaggregation.utils import DisaggregationMode
from sglang.srt.environ import envs
from sglang.srt.layers.logits_processor import LogitsProcessorOutput
from sglang.srt.layers.moe.routed_experts_capturer import get_global_experts_capturer
from sglang.srt.managers.io_struct import (
AbortReq,
BatchEmbeddingOutput,
@@ -62,6 +63,14 @@ class SchedulerOutputProcessorMixin:
trace_slice_batch(RequestStage.DECODE_FAKE_OUTPUT, batch.reqs)
self.stream_output(batch.reqs, batch.return_logprob)
def maybe_collect_routed_experts(self: Scheduler, req: Req):
"""Collect routed experts for a finished request."""
req.routed_experts = get_global_experts_capturer().get_routed_experts(
req_pool_idx=req.req_pool_idx,
seqlen=req.seqlen,
req_to_token_pool=self.req_to_token_pool,
)
def process_batch_result_prefill(
self: Scheduler,
batch: ScheduleBatch,
@@ -116,6 +125,7 @@ class SchedulerOutputProcessorMixin:
req.check_finished()
if req.finished():
self.maybe_collect_routed_experts(req)
release_kv_cache(req, self.tree_cache)
req.time_stats.completion_time = time.perf_counter()
elif not batch.decoding_reqs or req not in batch.decoding_reqs:
@@ -372,6 +382,8 @@ class SchedulerOutputProcessorMixin:
req.check_finished(new_accepted_len)
if req.finished():
self.maybe_collect_routed_experts(req)
if self.server_args.disaggregation_decode_enable_offload_kvcache:
# Asynchronously offload KV cache; release_kv_cache will be called after Device->Host transfer completes
if not self.decode_offload_manager.offload_kv_cache(req):
@@ -791,6 +803,7 @@ class SchedulerOutputProcessorMixin:
spec_accepted_tokens = []
retraction_counts = []
output_hidden_states = None
output_routed_experts = None
queue_times = []
forward_entry_times = []
@@ -985,6 +998,10 @@ class SchedulerOutputProcessorMixin:
if output_hidden_states is None:
output_hidden_states = []
output_hidden_states.append(req.hidden_states)
if req.return_routed_experts:
if output_routed_experts is None:
output_routed_experts = []
output_routed_experts.append(req.routed_experts)
if (
req.finished()
@@ -1034,6 +1051,7 @@ class SchedulerOutputProcessorMixin:
output_token_ids_logprobs_idx=output_token_ids_logprobs_idx,
output_token_entropy_val=None,
output_hidden_states=output_hidden_states,
output_routed_experts=output_routed_experts,
placeholder_tokens_idx=None,
placeholder_tokens_val=None,
retraction_counts=retraction_counts,
@@ -846,6 +846,7 @@ class TokenizerManager(TokenizerCommunicatorMixin, TokenizerManagerMultiItemMixi
custom_logit_processor=obj.custom_logit_processor,
require_reasoning=obj.require_reasoning,
return_hidden_states=obj.return_hidden_states,
return_routed_experts=obj.return_routed_experts,
data_parallel_rank=obj.data_parallel_rank,
priority=obj.priority,
extra_key=obj.extra_key,
@@ -1585,6 +1586,9 @@ class TokenizerManager(TokenizerCommunicatorMixin, TokenizerManagerMultiItemMixi
if getattr(recv_obj, "output_hidden_states", None):
meta_info["hidden_states"] = recv_obj.output_hidden_states[i]
if getattr(recv_obj, "output_routed_experts", None):
meta_info["routed_experts"] = recv_obj.output_routed_experts[i]
if isinstance(recv_obj, BatchStrOutput):
state.text += recv_obj.output_strs[i]
if self.server_args.stream_output and state.obj.stream:
+1 -9
View File
@@ -317,15 +317,7 @@ class TpModelWorker(BaseTpWorker):
# Profile number of tokens
self.max_total_num_tokens = self.model_runner.max_total_num_tokens
self.max_prefill_tokens = server_args.max_prefill_tokens
self.max_running_requests = min(
(
self.max_total_num_tokens // 2
if server_args.max_running_requests is None
else server_args.max_running_requests
// (server_args.dp_size if server_args.enable_dp_attention else 1)
),
self.model_runner.req_to_token_pool.size,
)
self.max_running_requests = self.model_runner.max_running_requests
assert self.max_running_requests > 0, "max_running_request is zero"
self.max_queued_requests = server_args.max_queued_requests
assert (
@@ -97,6 +97,11 @@ from sglang.srt.layers.dp_attention import (
set_is_extend_in_batch,
)
from sglang.srt.layers.logits_processor import LogitsProcessorOutput
from sglang.srt.layers.moe.routed_experts_capturer import (
RoutedExpertsCapturer,
get_global_experts_capturer,
set_global_experts_capturer,
)
from sglang.srt.layers.moe.utils import get_moe_a2a_backend
from sglang.srt.layers.pooler import EmbeddingPoolerOutput
from sglang.srt.layers.quantization.fp8_kernel import fp8_dtype
@@ -557,6 +562,21 @@ class ModelRunner:
server_args.max_running_requests,
server_args.max_total_tokens,
)
# Init max running requests
self.max_running_requests = min(
(
self.max_total_num_tokens // 2
if server_args.max_running_requests is None
else server_args.max_running_requests
// (server_args.dp_size if server_args.enable_dp_attention else 1)
),
self.req_to_token_pool.size,
)
# Init routed experts capturer
self.init_routed_experts_capturer()
if self.device == "cuda":
self.init_cublas()
self.init_attention_backend()
@@ -600,6 +620,40 @@ class ModelRunner:
# Initialize piecewise CUDA graph
self.init_piecewise_cuda_graphs()
def init_routed_experts_capturer(self):
# TODO: the redundant logic with TpModelWorker
max_running_requests = min(
(
self.max_total_num_tokens // 2
if self.server_args.max_running_requests is None
else self.server_args.max_running_requests
// (
self.server_args.dp_size
if self.server_args.enable_dp_attention
else 1
)
),
self.req_to_token_pool.size,
)
if not self.server_args.disable_shared_experts_fusion and hasattr(
self.model, "num_fused_shared_experts"
):
num_fused_shared_experts = self.model.num_fused_shared_experts
else:
num_fused_shared_experts = 0
set_global_experts_capturer(
RoutedExpertsCapturer.create(
enable=get_global_server_args().enable_return_routed_experts,
model_config=self.model_config,
num_fused_shared_experts=num_fused_shared_experts,
num_tokens=self.max_total_num_tokens + self.page_size,
max_running_requests=max_running_requests,
device=self.device,
)
)
def remote_instance_init_transfer_engine(self):
try:
from mooncake.engine import TransferEngine
@@ -2840,6 +2894,13 @@ class ModelRunner:
)
output.expert_distribution_metrics = recorder_outputs.get("metrics")
# Copy cached routing experts' buffers back to CPU cache
get_global_experts_capturer().on_forward_end(
forward_batch=forward_batch,
can_run_graph=output.can_run_graph,
cuda_graph_batch=getattr(self.graph_runner, "bs", None),
)
if self.eplb_manager is not None:
self.eplb_manager.on_forward_pass_end()
+1
View File
@@ -670,6 +670,7 @@ class DeepseekV2MoE(nn.Module):
self.topk = TopK(
top_k=config.num_experts_per_tok + self.num_fused_shared_experts,
layer_id=self.layer_id,
renormalize=config.norm_topk_prob,
use_grouped_topk=True,
num_expert_group=config.n_group,
+1
View File
@@ -87,6 +87,7 @@ class Ernie4Moe(nn.Module):
self.topk = TopK(
top_k=config.moe_k,
layer_id=layer_id,
renormalize=True,
use_grouped_topk=False,
correction_bias=self.gate.e_score_correction_bias,
+1
View File
@@ -394,6 +394,7 @@ class Glm4MoeSparseMoeBlock(nn.Module):
self.topk = TopK(
top_k=self.top_k + self.num_fused_shared_experts,
layer_id=self.layer_id,
renormalize=config.norm_topk_prob,
use_grouped_topk=True,
num_expert_group=config.n_group,
+1
View File
@@ -113,6 +113,7 @@ class GptOssSparseMoeBlock(nn.Module):
self.topk = TopK(
top_k=config.num_experts_per_tok,
renormalize=True,
layer_id=layer_id,
)
self.top_k = config.num_experts_per_tok
+1
View File
@@ -142,6 +142,7 @@ class Grok1MoE(nn.Module):
self.topk = TopK(
top_k=top_k,
renormalize=False,
layer_id=layer_id,
custom_routing_function=custom_routing_function,
)
+1
View File
@@ -150,6 +150,7 @@ class HunYuanSparseMoeBlock(nn.Module):
self.topk = TopK(
top_k=top_k,
layer_id=layer_id,
renormalize=True if top_k > 1 else False,
)
@@ -245,6 +245,7 @@ class LongcatFlashMoE(nn.Module):
renormalize=False,
use_grouped_topk=False,
correction_bias=self.router.e_score_correction_bias.data,
layer_id=layer_id,
)
self.topk.forward = self.topk.forward_native
+1
View File
@@ -161,6 +161,7 @@ class Qwen2MoeSparseMoeBlock(nn.Module):
self.topk = TopK(
top_k=config.num_experts_per_tok,
renormalize=config.norm_topk_prob,
layer_id=layer_id,
)
self.experts = get_moe_impl_class(quant_config)(
+1
View File
@@ -227,6 +227,7 @@ class Qwen3MoeSparseMoeBlock(nn.Module):
top_k=config.num_experts_per_tok,
renormalize=config.norm_topk_prob,
use_grouped_topk=False,
layer_id=layer_id,
)
self.experts = get_moe_impl_class(quant_config)(
+1
View File
@@ -129,6 +129,7 @@ class Step3TextMoEMLP(nn.Module):
top_k=config.moe_top_k,
renormalize=config.norm_expert_weight,
use_grouped_topk=False,
layer_id=layer_id,
)
self.experts = get_moe_impl_class(quant_config)(
+6
View File
@@ -573,6 +573,7 @@ class ServerArgs:
disable_fast_image_processor: bool = False
keep_mm_feature_on_device: bool = False
enable_return_hidden_states: bool = False
enable_return_routed_experts: bool = False
scheduler_recv_interval: int = 1
numa_node: Optional[List[int]] = None
enable_deterministic_inference: bool = False
@@ -4101,6 +4102,11 @@ class ServerArgs:
action="store_true",
help="Enable returning hidden states with responses.",
)
parser.add_argument(
"--enable-return-routed-experts",
action="store_true",
help="Enable returning routed experts of each layer with responses.",
)
parser.add_argument(
"--scheduler-recv-interval",
type=int,
+2
View File
@@ -120,6 +120,7 @@ DEFAULT_AWQ_MOE_MODEL_NAME_FOR_TEST = (
)
DEFAULT_ENABLE_THINKING_MODEL_NAME_FOR_TEST = "Qwen/Qwen3-30B-A3B"
DEFAULT_DEEPSEEK_W4AFP8_MODEL_FOR_TEST = "Barrrrry/DeepSeek-R1-W4AFP8"
DEFAULT_ENABLE_ROUTED_EXPERTS_MODEL_NAME_FOR_TEST = "Qwen/Qwen3-30B-A3B"
# Nightly tests
DEFAULT_MODEL_NAME_FOR_NIGHTLY_EVAL_TP1 = "meta-llama/Llama-3.1-8B-Instruct,mistralai/Mistral-7B-Instruct-v0.3,deepseek-ai/DeepSeek-Coder-V2-Lite-Instruct,google/gemma-2-27b-it"
@@ -772,6 +773,7 @@ def get_benchmark_args(
disable_tqdm=False,
disable_stream=disable_stream,
return_logprob=False,
return_routed_experts=False,
seed=seed,
disable_ignore_eos=disable_ignore_eos,
extra_request_body=None,