Memory Pool、allocator 与页级状态管理#

这章解决什么问题#

前两章已经把 scheduler、cache reuse 和 eviction 讲清楚了一大半,但还缺少一个很关键的中间层:逻辑请求状态到底怎样落到物理缓存槽位上。没有这一层,读者会知道 ReqToTokenPoolTokenToKVPoolAllocator 很重要,却仍然不清楚它们为什么会被拆开,以及 allocator 真正在管理什么。

这一章的目标,就是把“逻辑请求状态”和“物理 cache 布局”之间的映射关系补实。这样一来,调度与内存这一节才不会只剩“策略”和“树结构”,而会真正接上底层状态承载层。

为什么 ReqToTokenPoolTokenToKVPoolAllocator 要分开#

memory_pool.pymodel_runner_kv_cache_mixin.py 共同揭示了这件事:系统并不想用一个结构同时承担“请求级状态索引”和“物理缓存分配”。因为前者更像逻辑映射,后者更像设备级资源管理。如果把它们混在一起,调度器很难一边按请求推进,一边按页级状态复用。

这种拆分并不是形式主义。它直接决定了 system 在 request / batch / allocator 三个层之间是否能保持清晰边界。对读者来说,这一章的重点就是看到:逻辑索引层和物理分配层为什么都不可少。

allocator 在 runtime 里真正回答什么问题#

allocator 的问题不是“有没有空位”这么简单,而是“这些空位怎样以当前 page 模型被组织起来,是否还能支持接下来的 batch / reuse / speculative 路径”。这也是为什么项目里会存在 TokenToKVPoolAllocatorPagedTokenToKVPoolAllocator、SWA 相关 allocator,以及一系列和 KV cache 实现配套的分配器。

也就是说,allocator 不是 memory_pool.py 里的配角,而是决定缓存状态怎样真正落地的关键层。只要 allocator 模型理解错了,读者就会把 cache 行为误读成“树结构的命中问题”,而忽略物理布局本身也会限制系统行为。

一张把映射关系讲清楚的图#

flowchart TD
    A["逻辑请求状态"] --> B["ReqToTokenPool"]
    B --> C["token slots / request-local positions"]
    C --> D["TokenToKVPoolAllocator"]
    D --> E["paged / physical KV layout"]
    E --> F["KVCache buffers used by execution"]

这张图补的是“从逻辑到物理”的那一层。前两章更强调 batch 和 cache 策略,而这张图让读者看到:中间还有一个 allocator 层,负责把逻辑请求真正落成设备上可消费的状态布局。

本章对应哪些代码路径#

这一章最重要的锚点包括 python/sglang/srt/mem_cache/memory_pool.pypython/sglang/srt/mem_cache/allocator.pypython/sglang/srt/model_executor/model_runner_kv_cache_mixin.py 以及相关 unit tests,例如 test/registered/unit/mem_cache/ 下的 allocator / radix cache / swa 系列测试。

小结#

调度与内存这一节真正厚起来的关键之一,就是把 allocator 拉出来单讲。因为只有这样,读者才会意识到:从请求状态到 cache 命中之间,并不是一个黑箱,而是一条明确的映射链。