s02推理核心22 分钟阅读

Inside an Execution Step

拆开一次执行轮次

理解 Worker 与 ModelRunner 如何传递输入元数据、缓存引用和采样结果。

框架边界围绕输入、状态与依赖划分,不能只看函数调用顺序。

01先读输入元数据,再进入模型

调度器给出哪些序列参与、每条序列本轮处理多少位置,以及缓存块如何定位。ModelRunner 将这些信息整理为执行输入。读 execute_main_model 时先追踪序列长度、位置与缓存引用,再看前向入口;框架的关键在于让同一轮中的不同请求保持各自语义。

s02_execution_step.py
# Framework pseudocode summarizing Worker responsibilities.
metadata = prepare_inputs(scheduled_sequences, cache_state)
main_output = model_runner.execute_main_model(metadata)

post_process(previous_step_state)
sampled = model_runner.execute_sample(main_output, metadata)
current = StepState(metadata, sampled)

if is_prompt_step or not zos_enabled:
    ready_outputs = post_process(current)
    previous_step_state = None
else:
    previous_step_state = current

# Concrete metadata and method signatures live upstream.

02前向与采样是两个边界

execute_main_model 返回模型输出及相关元数据;execute_sample 再准备 sampler 输入并调用模型的 sample 路径。把两者拆开,才能承载约束输出、draft 验证和状态后处理。教学代码不展开模型层内部计算,而是关注这些对象由谁创建、传给谁,以及何时能够消费。

03Worker 决定依赖顺序

Worker.execute_model_step 的顺序是当前前向、上一轮后处理、当前采样。prefill 或关闭 ZOS 时当轮后处理;ZOS decode 则保留当前 StepState,供下一轮处理。因此示意图中的箭头是数据依赖,不意味着当前采样必然在当前调用结束前完成请求状态更新。

TRY IT YOURSELF

动手想一想

把 Worker 的四个阶段画成两轮时间线,分别标出 main_output、sampler_output 和 StepState 的产生与消费位置。

把这个机制,加入你的工具箱。