夜雨聆风学习资料网

ARTICLE · 1295750

《Nano-vLLM源码逐行解析》第1篇 config.py、sampling_params.py

《Nano-vLLM源码逐行解析》第1篇 config.py、sampling_params.py

前文图文版讲清了nano-vllm的架构和概念,但参数为什么这么设、张量形状怎么变、流程如何串起来,只有逐行读源码才能理解。这个系列贴出逐行中文注释的源码,建议和图文版对照阅读。

注释版源码:https://github.com/llm-infer/nano-vllm-annotated

nano-vllm 代码结构

  • • nanovllm/:
    • • llm.py:对外接口LLM,直接继承LLMEngine
    • • config.py:引擎级配置(调度上限、显存比例、KV block等)👈 本篇
    • • sampling_params.py:请求级采样参数(temperature、最大生成长度、是否忽略 EOS)👈 本篇
  • • engine/:推理引擎核心,负责调度、kv cache管理和模型执行
    • • llm_engine.py:推理入口,自己不做计算,把 tokenizer、scheduler、model_runner 串起来
    • • scheduler.py:调度器,决定每个step计算哪些序列、各算多少token
    • • sequence.py:一个请求对应一条序列,记录token、状态和 block table
    • • block_manager.py:在 CPU 上管理 KV block 的分配、回收和前缀缓存
    • • model_runner.py:每张 GPU 一个,负责加载模型、分配 KV cache、执行前向和采样
  • • layers/:模型的基础组件,大多支持张量并行
    • • attention.py:用 Triton kernel把K/V写入KV cache,再调用 FlashAttention
    • • linear.py:按列、按行切分的并行线性层
    • • embed_head.py:词表并行的Embedding和LM Head
    • • rotary_embedding.py:旋转位置编码RoPE
    • • layernorm.py:RMSNorm
    • • activation.py:SwiGLU 激活
    • • sampler.py:按温度从 logits 中采样下一个token
  • • models/:具体的模型结构
    • • qwen3.py:Qwen3 模型定义
  • • utils/:工具
    • • context.py:一次前向所需的注意力元数据,避免层层传参
    • • loader.py:从 safetensors 文件加载权重到模型

config.py

import osfrom dataclasses import dataclassfrom transformers import AutoConfig# dataclass:根据类里的字段声明,自动帮你生成样板方法,不用再手写 __init__、__repr__、__eq__# slots=True:#   作用:为每个字段预留一个固定位置,而不是用__dict__存放字段;#   好处:省内存,一个空字典本身就要占几十到上百字节,而且字典为了查找效率会预留空间。槽位只存指针,没有这层开销# 其他常用选项:#   frozen=True:实例不可变#   eq=False:不生成 __eq__@dataclass(slots=True)classConfig:# 模型所在的本地目录路径    model: str# 单个step允许处理的最大token# 主要约束prefill:把多个prompt token请求拼接到一个batch,token总和不能大于它;decode每个seq只处理一个token,不会触发上限约束# 调大: #   吞吐更高:权重读取(每次计算qkv,都要读一遍权重)、固定开销(kernel launch、h2d、d2h)被摊薄#   单步更慢:PD一体计算,ITL会变大,新请求等待上一个大prefill step,TTFT会变大,缓解办法是 chunked prefill(把长 prompt 切成块,和 decode 混在同一步里)#   激活显存更大:导致可用kv block变小,能同时服务的在线请求变少;容易导致OOM    max_num_batched_tokens: int = 16384# 单个step允许处理的序列数上限# decode是访存受限,每一个step基本由"读权重"决定,和 batch 里有几条序列几乎无关,所以 batch 里多放一条序列,几乎是白赚的一个 token# 但batch也存在上限,实际 batch 受限显存大小, = min(max_num_seqs, 当前在线请求数, KV cache 能容纳的序列数)    max_num_seqs: int = 512# 单序列的最大上下文长度(prompt+生成的token之和),超过即截断# 不能超过模型自身存在最大上限    max_model_len: int = 4096# 允许引擎使用的 GPU 显存比例(0~1)# 总显存 × gpu_memory_utilization = 模型权重 + 峰值激活 + KV cache# 总显存 × (1 - gpu_memory_utilization) = CUDA 上下文、碎片、其他进程    gpu_memory_utilization: float = 0.9# 把每层权重切到多张卡上    tensor_parallel_size: int = 1# True = 强制 eager 模式, 不使用 CUDA Graph。# CUDA Graph 把多个kernel录制到一个流水线上,能显著降低 decode 阶段的 kernel 启动开销    enforce_eager: bool = False# 模型配置对象(层数、head 数、隐藏维度、最大上下文等)    hf_config: AutoConfig | None = None# 结束符,遇到这个token就停止生成# -1表示未设置# 通常从tokenizer.eos_token_id中读取    eos: int = -1# 一个kv block能容纳多少个 token# block不必再显存中连续,减少显存碎片,支持前缀复用计算# 值越大,管理的block数量越少,但资源浪费越大(最后一个block可能填不满)    kvcache_block_size: int = 256# kv block的数量# 一般不指定,由引擎通过剩余显存计算出来    num_kvcache_blocks: int = -1# 钩子函数,__init__执行完后自动执行# 通常用来校验参数和初始化派生字段def__post_init__(self):# 校验模型目录是否存在assert os.path.isdir(self.model)# paged attention对kv block大小有要求,必须是256的倍数assertself.kvcache_block_size % 256 == 0# 限制只能在单机下运行assert1 <= self.tensor_parallel_size <= 8# 从本地模型目录读取 config.json,填入hf_configself.hf_config = AutoConfig.from_pretrained(self.model)# 把max_model_len限制在模型支持的最大长度以内self.max_model_len = min(self.max_model_len, self.hf_config.max_position_embeddings)

sampling_params.py

from dataclasses import dataclass@dataclass(slots=True)# 配置如何对模型输出的logits(每个词的分数)进行采样classSamplingParams:# 对分数进行缩放,再计算softmax得到概率:softmax(logits / temperature)# temperature < 1,分布变尖,高概率的词更占优,输出更确定更保守# temperature > 1,分布变平,低概率词更容易被选中,输出更随机、更发散# temperature 趋向 0,每次都选概率最大的值,即贪心解码# 例如:#   候选词     很    真     不      比#   logits    3.0      2.0  1.0    0.0#   e的指数: 20.1, 7.4, 2.7, 1.0   (总和 31.2)#    概率:   64%,  24%,  9%,  3%# logits/0.5  6    4    2    0#   e的指数: 403, 54.6, 7.4, 1.0   (总和 466)#    概率:  86.5%,11.7%,1.6%,0.2%    temperature: float = 1.0# 本次请求最多生成多少token(不含prompt)# 防止出现一直停不下来的情况    max_tokens: int = 64# 是否忽略结束符# False:模型生成了 EOS token 就立刻停止, 这是正常的对话/补全行为# True:模型生成了 EOS token 仍继续,常用于性能测试时固定输入输出长度,便于做数据对比    ignore_eos: bool = Falsedef__post_init__(self):assertself.temperature > 1e-10, "greedy sampling is not permitted"

回首页,搜索你想要的文档

在首页搜索框输入关键词,快速定位资料

文库首页搜索指引 立即搜索

相关学习资料