Skip to content

Latest commit

 

History

History
79 lines (56 loc) · 12.5 KB

source-map.md

File metadata and controls

79 lines (56 loc) · 12.5 KB

DeepInfer 固定版本源码地图

第一章:请求生命周期 · 架构导读 · English architecture guide

这份地图按要回答的问题组织源码入口。所有上游链接固定在 deepseek-ai/DeepInfer@8b16149741a4935e1dec8cea27853f273b374c5b,方便把文档中的状态变化与同一版本的实现逐项对照。

第一遍只沿主链阅读:LLM.generate → LLMEngine.add_request 建立请求;随后 LLM._run_engine → LLMEngine.step → Scheduler → Worker.execute_model_step → 后处理 → RequestOutput 推进各轮并收集结果。先分清外层循环、一轮编排与调度选择,再找清各模块的输入、持有状态、返回值和释放时机。

1. 请求如何进入,又如何返回

要回答的问题 固定源码入口 要追踪的状态
输入如何组织成多条请求? LLM.generate 输入集合、默认采样参数、请求加入顺序
请求身份在哪里建立? LLM._add_request request ID、复制后的参数、规范化的输入 IDs
入队前建立了哪些内部记录? LLMEngine.add_request 请求记录、序列初始状态、附加输入与等待条件
请求参数有哪些语义? SamplingParams 停止条件、长度限制、采样与结构化输出选项
循环何时继续,何时收尾? LLM._run_engine、has_unfinished_requests 未完成请求、已经就绪的输出、请求顺序与在途结果

_run_engine 很适合作为阅读起点和终点:先看它期待 step 返回什么,再进入引擎,最后回来确认结果如何被保存和排序。没有未完成请求时,它仍会调用 flush_inflight_reqs,收集延迟后处理的结果。

2. 调度器如何维护请求状态

要回答的问题 固定源码入口 要追踪的状态
等待、运行和各种结束原因如何表示? SequenceStatus 状态枚举、结束判断与异常分支
一条请求跨轮保留什么? Sequence、SequenceMetadata 长期序列记录与本轮执行元数据的区别
本轮计划包含哪些内容? SchedulerOutputs 已调度序列、结束序列、阶段标记和缓存状态操作
本轮选择哪类工作? Scheduler::_schedule 等待与运行队列、阶段选择、参与者之间的协调
等待请求如何进入 prefill? _schedule_prefill 准入条件、分块边界、本轮预算、无法分配时的处理
已运行请求如何继续 decode? _schedule_decode 可继续的序列、追加位置、资源限制与退出条件

阅读时给两条请求各留一行账本,记录“请求状态、本轮位置数、已有块、待完成操作”。沿一个分支走完,再解释为什么某条请求进入本轮、继续等待或结束。不要把一次 step 等同于每条请求恰好增加一个 token。

3. 状态如何分配、共享与回收

要回答的问题 固定源码入口 要追踪的状态
块记录包含什么?谁持有管理责任? Blocks、BlockManager 块标识、空闲资源、序列与块之间的关系
序列何时能获得所需资源? BlockManagerMutexAcquire::try_allocate_seq 分配条件、失败路径、受保护的管理操作
缓存记录与状态块如何表示? KVCache、StateBlock 保存的状态、块组织与跨轮存活范围
分配记录如何落实为缓存操作? KVCacheManager、CacheEngine 缓存存储、状态克隆、清理以及完成后的释放

把逻辑 token 位置、逻辑块和物理存储分别记账。前缀相同只是共享状态的条件之一;还需要兼容的计算与位置状态,以及不会破坏其他请求的后续写入。引擎内部的这些机制,也应与额外服务组件提供的缓存能力分开理解。

4. 一轮工作如何跨模块、跨轮次推进

要回答的问题 固定源码入口 要追踪的状态
调度结果如何进入执行链? LLMEngine.step、_execute_model_async_step 本轮元数据、状态操作、worker 调用与延迟释放记录
当前轮与前一轮怎样衔接? Worker.execute_model_step 当前前向、前一轮后处理、当前采样与延迟后处理
滞后一轮的序列元数据如何修正? Worker._correct_seqlens_qk 序列长度、上一轮接受结果与当前输入之间的关系
执行输入与采样输入怎样分工? ModelRunner.execute_main_model、execute_sample 各阶段需要的元数据、阶段返回值及其消费者
后处理如何写回请求结果? SamplerOutput、LLMEngine._process_model_outputs 接受的输出位置、结束标记与待返回结果
已返回对象能保存多久? RequestOutput 绑定与 clone、RequestOutputPool 池中对象、独立副本以及引用与所有权的区别
最后一轮之后如何收集在途结果? LLMEngine.flush_inflight_reqs 延迟后处理、待返回输出与收尾释放

Worker.execute_model_step 的注释给出了很好的阅读顺序。给每份元数据标上所属轮次,观察当前轮提交工作后,为什么可以处理前一轮结果。再回到 LLMEngine.step 的返回值说明和 _run_engine 的 clone,确认调用方如何安全保留输出。

5. 沿附加状态扩展阅读

主链清楚以后,再进入这些机制。它们都要维持请求身份、序列位置和状态所有权之间的一致关系。

要回答的问题 固定源码入口 要追踪的状态
结构化输出约束何时准备并进入请求? SOMatcherBuilder、LLMEngine.step matcher 的就绪状态、请求关联与失败处理
Draft 输入如何衔接接受位置? DFlashMetadataMixin.build_dflash_input 请求起始行、已接受偏移、位置与写入槽位
本轮输入和采样元数据怎样拆分? input_metadata.py、sampling_inputs.py 布局、选中位置与各阶段读取的字段
路由结果怎样对应到专家处理结果? v4_modules/moe.py、v4_modules/mlp.py 逻辑专家 ID、token 对应关系、dispatch 与 combine
负载均衡改变了哪一层映射? rebalance_experts_global 专家放置、物理副本与逻辑专家映射

读 DFlash 元数据时,留意浅复制后的对象仍共享未覆盖的张量,不能据此把副本中的每个字段都当成独立可写状态。读专家负载均衡时,则把物理放置调整与 token 的逻辑专家选择分开。

完成一遍阅读后,试着只用五个动词复述一条请求:入队、准入、提交、收集、释放。每个动词都应能找到状态变化和对应源码;暂时找不到的地方,就是下一遍需要补齐的依赖。

官方文章与开源边界

DeepSeek 官方《DeepSeek V4.1 Flash 大规模推理部署实践》提供阶段分离、缓存状态、请求负载、DSpark、异步重叠和结构化输出的设计背景。可先读中文问题导读或 English reading guide,再回来核对本表中的固定版本源码。

文章明确说明,KVCache 管理和负载均衡组件与内部基础设施耦合,尚未完整开源。上表中的引擎内缓存与调度入口不代表完整服务组件;请求负载均衡也不同于 eplb/policy.py 中的专家放置与副本策略。文章提到的 API 解析属于另一个独立项目 deepseek-recipe,不能据此归为 DeepInfer 内置模块。