<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>4. 调度与内存 on Machine Learning 学习笔记</title><link>https://kingye.me/study-ml/docs/book/sglang/scheduling-and-memory/</link><description>Recent content in 4. 调度与内存 on Machine Learning 学习笔记</description><generator>Hugo</generator><language>en</language><atom:link href="https://kingye.me/study-ml/docs/book/sglang/scheduling-and-memory/index.xml" rel="self" type="application/rss+xml"/><item><title>4.1 Scheduler、批次与 KV Cache</title><link>https://kingye.me/study-ml/docs/book/sglang/scheduling-and-memory/scheduler-and-kv-cache/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://kingye.me/study-ml/docs/book/sglang/scheduling-and-memory/scheduler-and-kv-cache/</guid><description>&lt;h1 id="scheduler批次与-kv-cache"&gt;Scheduler、批次与 KV Cache&lt;a class="anchor" href="#scheduler%e6%89%b9%e6%ac%a1%e4%b8%8e-kv-cache"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;h2 id="这章解决什么问题"&gt;这章解决什么问题&lt;a class="anchor" href="#%e8%bf%99%e7%ab%a0%e8%a7%a3%e5%86%b3%e4%bb%80%e4%b9%88%e9%97%ae%e9%a2%98"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;这一章解决的不是“请求从哪进来”，而是“请求已经进来之后，系统怎样决定谁先跑、谁继续跑，以及这些请求占用的 &lt;code&gt;KV cache&lt;/code&gt; 怎样被映射、复用和回收”。如果没有这层理解，你读 &lt;code&gt;scheduler.py&lt;/code&gt; 时会只看到大量条件分支；读 &lt;code&gt;memory_pool.py&lt;/code&gt; 时会只看到张量分配；两边都看了，仍然不知道它们为什么必须一起工作。&lt;/p&gt;
&lt;p&gt;这里最值得先抓住的事实，是 &lt;code&gt;python/sglang/srt/managers/schedule_batch.py&lt;/code&gt; 文件头写出的那条数据流：&lt;code&gt;ScheduleBatch -&amp;gt; ModelWorkerBatch -&amp;gt; ForwardBatch&lt;/code&gt;。这条注释说明调度阶段并不是直接操作 GPU forward 所需的最低层张量，而是先构造一个更高层的 batch 表示，再逐步降到执行层。调度与内存的配合，也正是围绕这条转换链组织的。&lt;/p&gt;
&lt;h2 id="scheduler-看的不是单个请求而是当前批次状态"&gt;&lt;code&gt;Scheduler&lt;/code&gt; 看的不是单个请求，而是“当前批次状态”&lt;a class="anchor" href="#scheduler-%e7%9c%8b%e7%9a%84%e4%b8%8d%e6%98%af%e5%8d%95%e4%b8%aa%e8%af%b7%e6%b1%82%e8%80%8c%e6%98%af%e5%bd%93%e5%89%8d%e6%89%b9%e6%ac%a1%e7%8a%b6%e6%80%81"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;python/sglang/srt/managers/scheduler.py&lt;/code&gt; 里的 &lt;code&gt;Scheduler&lt;/code&gt; 初始化时会建立等待队列、&lt;code&gt;running_batch&lt;/code&gt;、&lt;code&gt;last_batch&lt;/code&gt;、&lt;code&gt;tree_cache&lt;/code&gt;、&lt;code&gt;req_to_token_pool&lt;/code&gt; 等状态。它不是简单地从队列里拿一个请求就调用模型，而是持续维护“现在已经在跑什么、下一轮还能塞什么、哪些请求应该进入 prefill、哪些请求应该继续 decode”。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;get_next_batch_to_run()&lt;/code&gt; 很适合当作阅读入口。这个方法先处理 timeout、过滤完成请求、把上一轮 prefill batch 合并进 &lt;code&gt;running_batch&lt;/code&gt;，然后决定本轮是取新的 prefill batch，还是推进已有 decode batch。也就是说，调度器真正管理的是 batch 生命周期，而不是单个 request 生命周期。&lt;/p&gt;
&lt;p&gt;继续往下看 &lt;code&gt;get_new_batch_prefill()&lt;/code&gt; 和 &lt;code&gt;_get_new_batch_prefill_raw()&lt;/code&gt;，你会看到 &lt;code&gt;PrefillAdder&lt;/code&gt; 被用来在 token 预算、batch 大小、LoRA 约束、priority scheduling 与 chunked prefill 之间做折中。这里的重点不是把每个条件背下来，而是理解：SGLang 把“能不能接新请求”建模成一次 batch 构造问题，而不是某个全局开关。&lt;/p&gt;
&lt;p&gt;这部分如果只靠文字，很容易把 &lt;code&gt;Scheduler&lt;/code&gt;、&lt;code&gt;ScheduleBatch&lt;/code&gt; 和 cache 看成三块并列知识。下面这张图专门用来解释它们之间的状态流转关系：请求怎样从 waiting queue 进入 batch，batch 怎样触发前向，cache 怎样在这个过程中决定复用和回收。&lt;/p&gt;</description></item></channel></rss>