SGLang 双 Stream 重叠调度:如何把 CPU 后处理藏到 GPU 计算背后

大模型自回归推理看起来是纯 GPU 工作:每一步做一次模型前向,采样一个 token,再把 token 喂给下一步。然而在高性能推理系统里,GPU 计算只是流水线的一部分。采样结果回传、EOS 判断、请求回收、KV Cache 管理、下一批组装和元数据准备,都可能在两次前向之间制造空洞。 SGLang 的 Overlap Scheduling 解决的正是这个问题。它不是简单地“多开一条 CUDA stream”,而是同时完成两件事: 让当前步产生的 token 留在 GPU,直接作为下一步输入,解除下一次 forward 对 CPU 读值的依赖; 用 scheduler stream 和 engine stream 构造软件流水线,让 GPU 计算当前批次时,调度器处理上一批次的结果。 本文以 Mini-SGLang 的 python/minisgl/scheduler/scheduler.py 为线索,解释 overlap_loop、_forward 和 _process_last_data 背后的依赖重排。 先看结论:优化的不是“提交速度”,而是依赖关系 CUDA kernel 的提交本来就是异步的。CPU 把工作放进 stream 后,不必等待 GPU 完成。因此一个自然的问题是:只用一条 stream,只要 CPU 提交得足够快,不也能让 GPU 连续工作吗? 如果系统只有计算任务,这个判断基本成立。但自回归推理存在一个控制依赖:调度器通常要知道采样出的 token,才能判断请求是否命中 EOS、是否应该释放资源,以及下一轮有哪些请求仍应留在 batch 中。 串行执行会形成如下链条: GPU: forward(B1) ── idle ── forward(B2) ── idle ── forward(B3) CPU: 处理 B1 处理 B2 读 token 读 token 判 EOS 判 EOS 组 B2 组 B3 GPU 完成 B1 后,CPU 才读取结果并决定 B2。此时 GPU 没有可执行的新工作,只能等待。这不是 CPU 调用 CUDA API 太慢,而是 B2 在逻辑上依赖 B1 的结果。 ...

2026年8月30日 · 4 分钟 · Hellokitty

tiny-llm:在 Apple Silicon 上从 Transformer 算子走到推理系统与 Agent

如果你已经知道 Transformer 的基本结构,却仍然不清楚 vLLM 一类推理系统为什么需要 KV Cache、Continuous Batching、Paged Attention,以及这些技术最终如何支撑 Agent,skyzh/tiny-llm 是一条很好的动手路径。 它不是一个追求生产可用的迷你框架,而是一门以 Qwen3 和 MLX 为载体的系统课程:先用可读的数组运算实现模型,再围绕真实性能瓶颈写 Metal 内核,随后搭出动态批处理和分页缓存,最后把同一个本地模型接入带工具、检查点、压缩、评测和分支选择的 Agent 循环。 截至 2026 年 8 月 27 日,项目约有 4.5k stars,使用 Apache-2.0 许可证。课程主体分为四周,已覆盖从注意力到受控 Agent 的完整主线。 项目定位:不是“再写一个 Transformer” 很多教学项目止步于“加载权重并生成一句话”。tiny-llm 的不同之处,是把模型实现当作起点,而不是终点。 它试图回答三个递进的问题: 一个现代开源模型如何由矩阵运算变成文本? 一个正确但缓慢的实现,如何通过测量和内核优化接近成熟框架? 一个推理引擎如何进一步成为可暂停、可审计、可恢复的 Agent 执行器? 项目选择 Apple Silicon、MLX 和 Qwen3。统一内存让 CPU、GPU 和模型权重处在相对简单的硬件环境里;MLX 提供熟悉的 Python 数组接口,同时允许下沉到 Metal;Qwen3 则带有 GQA、QK Norm、BF16 激活和 4-bit 权重,足以暴露真实推理系统中的带宽、注意力与缓存问题。 代码结构刻意分成两套: src/tiny_llm/ 是学习者逐步补全的实现; src/tiny_llm_ref/ 是参考实现,用于测试和基准对照; src/extensions/ 与 src/extensions_ref/ 分别放置 Metal/C++ 扩展; tests_refsol/ 按周和天组织验收测试; book/ 是完整课程正文。 这种布局把“阅读—实现—测试—测量”闭成了环。 ...

2026年8月27日 · 3 分钟 · Hellokitty