mirror of
https://github.com/deepseek-ai/DeepInfer.git
synced 2026-10-26 16:35:05 +00:00
229 lines
9.3 KiB
Python
229 lines
9.3 KiB
Python
# SPDX-License-Identifier: Apache-2.0
|
|
# Derived from vLLM; modified by DeepSeek.
|
|
|
|
import torch
|
|
|
|
from deepinfer import envs
|
|
from deepinfer.engine.engine_type import EngineType
|
|
from deepinfer.model_config_loader import load_model_config
|
|
|
|
|
|
def _get_runtime_max_model_len(max_model_len: int) -> int:
|
|
"""Return the exclusive upper bound of every runtime position."""
|
|
nextn = envs.DEEPINFER_USE_NEXTN_DECODE
|
|
scheduler_overhead = nextn + (nextn + 1 if envs.DEEPINFER_ZERO_OVERHEAD_SCHEDULING else 0)
|
|
draft_overhead = nextn
|
|
return max_model_len + scheduler_overhead + draft_overhead
|
|
|
|
|
|
class ModelConfig:
|
|
"""Configuration for the model.
|
|
|
|
Args:
|
|
model: Name or path of the huggingface model to use.
|
|
tokenizer: Name or path of the huggingface tokenizer to use.
|
|
trust_remote_code: Trust remote code (e.g., from HuggingFace) when
|
|
downloading the model and tokenizer.
|
|
load_format: Weight initialization mode:
|
|
"auto" loads the HF safetensors index and shards from the model
|
|
directory.
|
|
"dummy" initializes synthetic weights for profiling, using the
|
|
same HF model configuration.
|
|
seed: Random seed for reproducibility.
|
|
revision: The specific model version to use. It can be a branch name,
|
|
a tag name, or a commit id. If unspecified, will use the default
|
|
version.
|
|
tokenizer_revision: The specific tokenizer version to use. It can be a
|
|
branch name, a tag name, or a commit id. If unspecified, will use
|
|
the default version.
|
|
max_model_len: Maximum length of a sequence (including prompt and
|
|
output). If None, will be derived from the model.
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
model: str,
|
|
tokenizer: str,
|
|
trust_remote_code: bool,
|
|
load_format: str,
|
|
seed: int,
|
|
max_num_seqs: int,
|
|
revision: str | None = None,
|
|
tokenizer_revision: str | None = None,
|
|
max_model_len: int | None = None,
|
|
max_num_batched_tokens: int | None = None,
|
|
ignore_len: int = -1,
|
|
served_model_name: str = "deepinfer-model",
|
|
engine_type: EngineType = EngineType.NORMAL,
|
|
) -> None:
|
|
self.model = model
|
|
self.tokenizer = tokenizer
|
|
self.trust_remote_code = trust_remote_code
|
|
self.load_format = load_format
|
|
self._verify_load_format()
|
|
self.seed = seed
|
|
self.revision = revision
|
|
self.tokenizer_revision = tokenizer_revision
|
|
|
|
self.max_num_seqs = max_num_seqs
|
|
self.max_model_len = max_model_len if max_model_len is not None else 4096
|
|
self.runtime_max_model_len = _get_runtime_max_model_len(self.max_model_len)
|
|
self.max_num_batched_tokens = _get_max_num_batched_tokens(self.max_model_len, max_num_batched_tokens, max_num_seqs)
|
|
|
|
self.ignore_len = ignore_len
|
|
self.served_model_name = served_model_name
|
|
self.dtype = torch.bfloat16
|
|
self.use_compressed_tokenizer = False
|
|
self.engram_tokenizer = self.tokenizer
|
|
self.engine_type = engine_type
|
|
|
|
self._load_model_cfg()
|
|
|
|
_GPT_RUNTIME_ATTRS = (
|
|
"max_num_seqs",
|
|
"max_num_batched_tokens",
|
|
"runtime_max_model_len",
|
|
"engine_type",
|
|
)
|
|
|
|
def _load_model_cfg(self) -> None:
|
|
self.model_cfg = load_model_config(self.model)
|
|
self.architecture = self.model_cfg.architecture
|
|
|
|
for attr in self._GPT_RUNTIME_ATTRS:
|
|
setattr(self.model_cfg.gpt, attr, getattr(self, attr))
|
|
|
|
def __getstate__(self) -> dict:
|
|
# model_cfg 不可 pickle,改为只传递必要的运行时标量,
|
|
# 由 worker 进程在 __setstate__ 中重新 load model_cfg 后回填。
|
|
state = self.__dict__.copy()
|
|
model_cfg = state.pop("model_cfg", None)
|
|
overrides: dict = {}
|
|
if model_cfg is not None:
|
|
overrides = {k: model_cfg.gpt[k] for k in self._GPT_RUNTIME_ATTRS}
|
|
state["_gpt_runtime_overrides"] = overrides
|
|
return state
|
|
|
|
def __setstate__(self, newstate: dict) -> None:
|
|
overrides = newstate.pop("_gpt_runtime_overrides", {})
|
|
self.__dict__.update(newstate)
|
|
self.model_cfg = load_model_config(self.model)
|
|
self.architecture = self.model_cfg.architecture
|
|
for key, value in overrides.items():
|
|
setattr(self.model_cfg.gpt, key, value)
|
|
|
|
def _verify_load_format(self) -> None:
|
|
load_format = self.load_format.lower()
|
|
supported_load_format = ["auto", "dummy"]
|
|
assert load_format in supported_load_format
|
|
self.load_format = load_format
|
|
|
|
def get_vocab_size(self) -> int:
|
|
return self.model_cfg.gpt.vocab_size
|
|
|
|
|
|
class CacheConfig:
|
|
def __init__(
|
|
self,
|
|
block_size: int,
|
|
gpu_memory_utilization: float,
|
|
lookahead_tokens: int = 0,
|
|
) -> None:
|
|
self.block_size = block_size
|
|
self.gpu_memory_utilization = gpu_memory_utilization
|
|
self._verify_args()
|
|
|
|
# Will be set after profiling.
|
|
self.num_gpu_blocks = None
|
|
# Will be set after init cache.
|
|
self.num_state_seqs = None
|
|
self.bytes_of_state_per_seqs = None
|
|
self.bytes_per_block = None
|
|
|
|
self.lookahead_tokens = lookahead_tokens
|
|
|
|
def _verify_args(self) -> None:
|
|
if self.gpu_memory_utilization > 1.0:
|
|
raise ValueError(f"GPU memory utilization must be less than 1.0. Got {self.gpu_memory_utilization}.")
|
|
|
|
|
|
class ParallelConfig:
|
|
def __init__(
|
|
self,
|
|
data_parallel_size: int,
|
|
context_parallel_size: int,
|
|
expert_parallel_size: int,
|
|
engram_parallel_size: int,
|
|
engine_rank: int,
|
|
engine_group_rank: int,
|
|
engine_type: EngineType = EngineType.NORMAL,
|
|
) -> None:
|
|
if data_parallel_size == 0:
|
|
data_parallel_size = max(1, expert_parallel_size // context_parallel_size)
|
|
self.data_parallel_size = data_parallel_size
|
|
self.context_parallel_size = context_parallel_size
|
|
self.expert_parallel_size = expert_parallel_size
|
|
self.engine_rank = engine_rank
|
|
self.data_parallel_rank = engine_rank % data_parallel_size
|
|
self.engine_group_rank = engine_group_rank
|
|
self.engine_type = engine_type
|
|
|
|
self.model_parallel_size = self.context_parallel_size
|
|
self.engram_parallel_size = engram_parallel_size or (self.model_parallel_size * data_parallel_size)
|
|
self.world_size = max(self.model_parallel_size * data_parallel_size, self.engram_parallel_size)
|
|
self._verify_args()
|
|
|
|
def _verify_args(self) -> None:
|
|
assert self.expert_parallel_size > 0
|
|
assert (self.model_parallel_size * self.data_parallel_size) % self.expert_parallel_size == 0
|
|
assert self.engram_parallel_size > 0
|
|
assert self.world_size % (self.model_parallel_size * self.data_parallel_size) == 0
|
|
assert self.world_size % self.engram_parallel_size == 0
|
|
assert 0 <= self.engine_rank < self.world_size // self.model_parallel_size
|
|
|
|
|
|
class SchedulerConfig:
|
|
def __init__(
|
|
self,
|
|
max_num_batched_tokens: int | None,
|
|
max_num_seqs: int,
|
|
max_model_len: int,
|
|
stop_preempted_request: bool = False,
|
|
chunked_prefill: bool = False,
|
|
prefill_attempt_interval: int = envs.DEEPINFER_PREFILL_ATTEMPT_INTERVAL,
|
|
prefill_any_attempt_interval: int = envs.DEEPINFER_PREFILL_ANY_ATTEMPT_INTERVAL,
|
|
) -> None:
|
|
self.max_num_batched_tokens = _get_max_num_batched_tokens(max_model_len, max_num_batched_tokens, max_num_seqs)
|
|
self.max_num_seqs = max_num_seqs
|
|
self.max_model_len = max_model_len
|
|
self.runtime_max_model_len = _get_runtime_max_model_len(max_model_len)
|
|
self.stop_preempted_request = stop_preempted_request
|
|
self.chunked_prefill = chunked_prefill
|
|
# NORMAL 引擎每隔多少个 decode step 尝试一次 prefill(ALL 投票点:全员有活才做)
|
|
self.prefill_attempt_interval = prefill_attempt_interval
|
|
# NORMAL 引擎 ANY 保底投票点间隔:任一 rank 有活即集体 prefill,防饿死
|
|
self.prefill_any_attempt_interval = prefill_any_attempt_interval
|
|
self._verify_args()
|
|
|
|
def _verify_args(self) -> None:
|
|
if (not self.chunked_prefill) and self.max_num_batched_tokens < self.max_model_len:
|
|
raise ValueError(
|
|
f"max_num_batched_tokens ({self.max_num_batched_tokens}) is smaller than max_model_len ({self.max_model_len}). This effectively limits the maximum sequence length to max_num_batched_tokens and makes DeepInfer reject longer sequences. Please increase max_num_batched_tokens or decrease max_model_len."
|
|
)
|
|
if self.prefill_attempt_interval < 1:
|
|
raise ValueError(f"prefill_attempt_interval ({self.prefill_attempt_interval}) should be >= 1")
|
|
if self.prefill_any_attempt_interval < 1:
|
|
raise ValueError(f"prefill_any_attempt_interval ({self.prefill_any_attempt_interval}) should be >= 1")
|
|
|
|
|
|
def _get_max_num_batched_tokens(
|
|
max_model_len: int,
|
|
max_num_batched_tokens: int | None,
|
|
max_num_seqs: int,
|
|
) -> int:
|
|
max_num_decoding_tokens = max_num_seqs * (1 + envs.DEEPINFER_USE_NEXTN_DECODE)
|
|
if max_num_batched_tokens is not None:
|
|
assert max_num_batched_tokens >= max_num_decoding_tokens
|
|
return max_num_batched_tokens
|
|
return max(max_model_len, 4096, max_num_decoding_tokens)
|