Block ID 需要结合层索引、Shape、Stride 和数据类型,才能落到具体字节地址。本文用一组可手算的地址比较 Layer-major 与 Block-major,说明跨层连续分配如何改变访问与搬运方式。源码术语以 vLLM v0.29.0 为参照,算例采用均匀、无额外 Padding 的缓存组。
Block Table 已经告诉我们“访问 P7”。但 P7 仍只是缓存池中的编号:同一段 Token 在每层都有独立的 KV,层内又有 Head 和 Token 等维度。访问指令最终需要的是这些数据的地址。
先区分分配与寻址
推理引擎通常先向设备申请一片 KV Cache Pool,再在请求运行期间分配其中的 Block。前者决定池子占用多少设备内存,后者决定哪些请求使用哪些块。
Tensor 在这片 Storage 上提供访问视图。理解视图需要同时看 Shape、Stride、Storage Offset 和 dtype:Shape 给出各维长度;Stride 指定某维索引增加 1 时跨过多少个存储元素;Storage Offset 指定视图的起点。
对索引 ,若 Stride 与 Offset 都以元素为单位:
固定一个能手算的缓存池
设模型有 4 层,缓存池有 4 个 Block,每块 4 个 Token,2 个 KV Heads,Head Dimension 为 2,BF16 每元素 2 字节。
一个 Token 在一层的 K/V 共占 字节。因此:
| 对象 | 大小 |
|---|---|
| 一个 Block 在某层的数据片段,即 Layer Page | B |
| 一个 Block 的四层 KV 合计 | B |
| 整个缓存池 | B |
“一个 Block 的四层 KV 合计 256 B”只说明总数据量,尚未承诺这 256 B 连续。
vLLM 的 KVCacheTensor 对同形层给出 Page 起点的字节偏移形式:
是层索引, 是 Physical Block ID, 是该组起点, 与 分别是层和块的字节步长。这里已经采用字节单位,不能再乘一次 dtype 大小。
Layer-major:同层的 Block 连续
先放完第 0 层的 P0–P3,再放第 1 层,以此类推。每层占 256 B,每个 Layer Page 占 64 B,于是:
P0 在四层的起点分别为 0、256、512、768 B。四段 Page 都能准确找到,但中间夹着其他 Block 的数据。第 2 层的 P0–P3 则从 512 B 开始连续排列。
这一组织方式让同一层的数据比较集中,便于理解逐层 Attention 的访问需求。
Block-major:同块的 Layer 连续
现在先放 P0 的四层数据,再放 P1 的四层数据。对同一个算例:
P0 在四层的起点变成 0、64、128、192 B,合起来正好是连续的 256 B。第 2 层的 P0–P3 起点则为 128、384、640、896 B。
| 访问目标 | Layer-major | Block-major |
|---|---|---|
| 第 2 层 P0 的起点 | 512 B | 128 B |
| P0 的全部四层 | 四段分散 Page | 连续 256 B |
| 第 2 层全部四个 Block | 连续 256 B | 四段分散 Page |
| 池内总 Block 数与 Payload | 4 个,1024 B | 4 个,1024 B |
如何读懂 LBHNC 与 BLHNC
vLLM v0.29.0 的 KVCacheLayout 使用逻辑轴 [L,B,H,N,C]:L 为层,B 为块,H 为 Head,N 为块内 Token 或 stored state,C 为内容维度。其字节布局接口中的 C 表示内容字节宽度,需要查看对应 KV Spec,不能直接当作上下文长度。
| 名称 | 从外到内的物理轴顺序 | 直观差别 |
|---|---|---|
| LBHNC | Layer、Block、Head、State、Content | 同层 Block 优先排列 |
| LBNHC | Layer、Block、State、Head、Content | 同层 Block 内交换 N/H 次序 |
| LHBNC | Layer、Head、Block、State、Content | 同层先组织 Head |
| BLHNC | Block、Layer、Head、State、Content | 同 Block 的层优先排列 |
| BLNHC | Block、Layer、State、Head、Content | 跨层 Block 内交换 N/H 次序 |
| BHLNC | Block、Head、Layer、State、Content | 同 Block 内先组织 Head |
上面的手算分别对应 LBHNC 与 BLHNC。NHD/HND 常用于描述层内 Token 与 Head 的排列;引入 L、B 后,还能描述跨层与跨 Block 的组织。六种名字无需先背下来,先看清主导操作需要哪些维度相邻。
跨层连续分配,何时值得做
Attention Kernel 每次通常处理某一层;KV 搬运组件却可能希望一次搬走某个 Block 的全部层。两者对连续性的需求可能不同。
Cross-layer Allocation 让同一 Block 的多层数据在分配中相邻,从而有机会合并传输、减少描述符与管理开销。它保留各层独立的 KV 数值。Cross-layer KV Sharing 则涉及不同层复用状态,属于模型计算语义,不能由地址相邻推导出来。vLLM PR #27743记录了跨层分配的工程动机。
最后,软件 Block ID、Token Slot、Tensor 偏移和设备虚拟地址处于不同层次。设备地址到实际显存位置的转换由更底层处理。这里的“连续”描述分配与访问可见的地址组织,不能据此断言底层物理页也连续。
源码入口:KVCacheLayout、KVCacheTensor 与视图创建。
上一篇:KV Cache 如何分页、共享与回收 · 下一篇:容量、带宽与时延:KV 卸载值不值得。