推理与 KV Cache 03:从 Block ID 到字节地址

作者:CherryYang 发布时间: 2026-11-12 阅读量:5 评论数:0

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 指定视图的起点。

对索引 (i0,…,in)(i_0,\ldots,i_n),若 Stride 与 Offset 都以元素为单位:

byte address=storage base+(storage offset+∑kiksk)×element size\text{byte address}=\text{storage base} +\left(\text{storage offset}+\sum_k i_ks_k\right)\times\text{element size}
Logical Block B0 T0 · T1 · T2 · T3 Block Table B0 → P7 Physical Slot P7 一个统一索引 P7 @ Layer 0 Layer-specific Page P7 @ Layer 1 Layer-specific Page P7 @ Layer 2 Layer-specific Page … @ Layer 3 Layer-specific Page P7 是统一 Slot ID;每个 Layer 的 Page 地址由 Layout / Stride 决定。
图 1:Block Table 不必为每个 Layer 建立一套独立的 Block ID。一个 Slot ID 可以定位同一 Token 区间在各 Layer 中对应的 Page。

固定一个能手算的缓存池

设模型有 4 层,缓存池有 4 个 Block,每块 4 个 Token,2 个 KV Heads,Head Dimension 为 2,BF16 每元素 2 字节。

一个 Token 在一层的 K/V 共占 2×2×2×2=162\times2\times2\times2=16 字节。因此:

对象 大小
一个 Block 在某层的数据片段,即 Layer Page 4×16=644\times16=64 B
一个 Block 的四层 KV 合计 4×64=2564\times64=256 B
整个缓存池 4×4×64=10244\times4\times64=1024 B

“一个 Block 的四层 KV 合计 256 B”只说明总数据量,尚未承诺这 256 B 连续。

vLLM 的 KVCacheTensor 对同形层给出 Page 起点的字节偏移形式:

offset⁡(l,p)=o+l sL+p sB\operatorname{offset}(l,p)=o+l\,s_L+p\,s_B

ll 是层索引,pp 是 Physical Block ID,oo 是该组起点,sLs_L 与 sBs_B 分别是层和块的字节步长。这里已经采用字节单位,不能再乘一次 dtype 大小。

Layer-major:同层的 Block 连续

先放完第 0 层的 P0–P3,再放第 1 层,以此类推。每层占 256 B,每个 Layer Page 占 64 B,于是:

sL=256,sB=64,offset⁡(l,p)=256l+64ps_L=256,\qquad s_B=64,\qquad \operatorname{offset}(l,p)=256l+64p
LBHNC · Layer Major · 每个 Page = 64 B Layer 0Layer 1Layer 2Layer 3 P0[0,64) P1[64,128) P2[128,192) P3[192,256) P0[256,320) P1[320,384) P2[384,448) P3[448,512) P0[512,576) P1[576,640) P2[640,704) P3[704,768) P0[768,832) P1[832,896) P2[896,960) P3[960,1024) 横向箭头:同一 Layer 内地址递增;向下箭头:进入下一 Layer蓝色的四个 P0 切片合起来代表同一个跨层 Slot P0,但地址分散。
图 2:LBHNC 先排列 Layer。每层自己的 4 个 Block Page 连续;同一 Slot P0 在不同 Layer 中的 Page 之间隔着其他 Block。

P0 在四层的起点分别为 0、256、512、768 B。四段 Page 都能准确找到,但中间夹着其他 Block 的数据。第 2 层的 P0–P3 则从 512 B 开始连续排列。

这一组织方式让同一层的数据比较集中,便于理解逐层 Attention 的访问需求。

Block-major:同块的 Layer 连续

现在先放 P0 的四层数据,再放 P1 的四层数据。对同一个算例:

sL=64,sB=256,offset⁡(l,p)=64l+256ps_L=64,\qquad s_B=256,\qquad \operatorname{offset}(l,p)=64l+256p
BLHNC · Block Major · 每个 Page = 64 B Slot P0Slot P1Slot P2Slot P3 Layer 0[0,64) Layer 1[64,128) Layer 2[128,192) Layer 3[192,256) Layer 0[256,320) Layer 1[320,384) Layer 2[384,448) Layer 3[448,512) Layer 0[512,576) Layer 1[576,640) Layer 2[640,704) Layer 3[704,768) Layer 0[768,832) Layer 1[832,896) Layer 2[896,960) Layer 3[960,1024) 横向箭头:同一 Slot 内地址递增;向下箭头:进入下一 SlotP0 的四个 Layer-specific Page 连续组成 [0,256);其他 Slot 依次排列。
图 3:BLHNC 先排列 Block。一个 Slot 的多层 Page 被打包在一起,适合把完整跨层 Block 作为一个对象搬运。

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 卸载值不值得。

评论