想象你是一家高级中餐厅的主厨,今晚要接待一桌贵客,菜单上写着六道菜。每一道菜都需要同一锅秘制高汤来提味——如果每做一道菜都要重新起锅、花两小时熬汤,六道菜光是熬汤就要耗掉大半天。而聪明的厨房总会有一口常备的高汤锅:提前熬好,随用随取,每道新菜只需往锅里补一点新料,就能立刻出餐。 大模型的自回归生成,就像主厨一道菜接一道菜地出餐:每生成一个新字(token),都要回顾前面所有已经生成的字,理解它们与当前字的关联,这靠的是注意力机制。冷酷的现实是,如果没有“备用高汤”——即KV Cache,每走一步都要把前面所有字的“风味档案”(K和V矩阵)从头重新算一遍,计算量随对话增长成倍膨胀。KV Cache正是那口常备高汤锅:把算过的K/V装进“冰箱”(显存),新字来了直接取用,只给新字补算自己的K/V。但冰箱不是无限的——KV Cache占用的显存随序列长度线性增长,在大模型推理部署中,它是比模型权重更隐蔽、也更惊人的成本大头。

岗位关联

理解KV Cache的原理和内存模型,是优化推理吞吐、降低部署成本的基本功。无论你是推理引擎工程师、MLOps还是应用算法工程师,几乎所有大模型部署调优都绕不开这口“高汤锅”的容量规划。

目录
  1. 可视化演示
  2. 自回归生成机制:流水线上的逐道菜品
  3. KV缓存核心原理:提前熬好的高汤
  4. 显存占用模型:冰箱里的账本
  5. 长上下文挑战:冰箱不够用了
  6. 主要优化方向:给冰箱做减法
  7. 部署实践考量:厨房的全面规划
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

展示系统处理流水线、实时性能指标曲线及多方案对比分析,包含数据流转、吞吐延迟监控与方案评估。

本节配有交互动效(进入视口后自动加载,约 3KB)。

系统可视化 处理流程:输入→处理→模型→输出 性能监控:实时指标 P=L×T

自回归生成机制:流水线上的逐道菜品

主厨出餐有个硬规矩:一道菜端上桌,客人尝完,下一道菜才开始做。大模型的生成也一样,它不会一口气吐出整篇文章,而是一个字一个字地“端出来”——每生成一个新token,都要把已经生成的全部token重新“回味”一遍,理解它们之间的关联。这个过程叫自回归生成,也就是“用自己生成的内容,作为生成下一步内容的依据”。

要理解这个“回味”过程,得先认识三个角色:Q、K、V。用厨房的话说,Q是“当前这道菜想找什么味道”(需求),K是“每道菜的标签,写着它是什么味道”,V是“每道菜的实际内容”。模型每走一步,都要把当前token的Q,与所有历史token的K逐一比对,算出相似度,再按相似度去加权取V中的内容。这套机制就是注意力机制,它让模型能把注意力集中在最相关的历史信息上。

问题出在“逐一比对”四个字上。不设缓存时,第t步生成新token,模型要把前0到t-1个token的K和V全部重新投影计算一遍;到第t+1步,又要重来一遍——之前算过的K/V全部作废重算。序列越长,重复劳动越多,这就是所谓的“历史重复计算痛点”。

回到厨房的比喻:没有高汤锅的主厨,每做一道新菜,都要把前面所有已上桌的菜重新端回来,逐一“尝味道”记录在案,再开始做下一道。六道菜下来,重复品尝的劳动量呈平方级爆炸——这显然不是一个好厨房该有的样子。

Attention(Q,K,V)=softmax(QK⊤dk)V\displaystyle \text{Attention}(Q, K, V) = \text{softmax}\left( \frac{Q K^{\top}}{\sqrt{d_k}} \right) V
这条公式描述的是:拿着当前token的“需求清单”Q,去比对全体历史token的“标签”K,算出权重,再按权重去“取货”V。
  1. Q与K转置的点积计算当前token与每个历史token的相似度得分,得分越高代表越相关。
    QK⊤Q K^{\top}
  2. 缩放因子√d_kd_k是每个注意力头的维度,点积结果随维度增大而膨胀,除以√d_k把数值拉回合理范围,避免softmax数值饱和。
    dk\sqrt{d_k}
  3. softmax归一化把原始得分转换成一组和为1的注意力权重,相当于给每道历史菜“派发关注度”。
    softmax(⋅)\text{softmax}(\cdot)
  4. 加权求和得输出按权重对所有V向量加权求和,得到当前token融合历史信息后的新表示。
    weights⋅V\text{weights} \cdot V

KV缓存核心原理:提前熬好的高汤

聪明的主厨会怎么做?提前把高汤熬好,装进保温桶放进冰箱。做新菜时,高汤直接取用,只需往锅里补上新加的料。KV Cache的核心思想一模一样:把历史token的K和V矩阵保存在显存里,每一步只计算新token自己的K和V,追加进缓存即可。

具体来说,生成第t个token时,模型只需对当前输入做投影,得到Q_t、K_t、V_t三个向量。Q_t拿去和缓存中的K_0到K_{t-1}以及新算出的K_t做点积,V同理。关键点在于:历史token的K/V是“只增不改”的——它们不会因为新token的到来而变化,所以缓存可以安全复用。这就是“增量更新”的含义:每次只往缓存尾巴上追加两个新向量,绝不回头重算旧值。

这也带来了一个易被忽略的好处:结果与不使用缓存完全等价。缓存省掉的是“重复”计算,而不是“不同”的计算——同一批矩阵投影,算一次和算一百次,数学结果一模一样。所以KV Cache不是近似优化,而是精确去掉冗余步骤。同时,模型的权重矩阵(W_Q、W_K、W_V)始终不变,缓存里只存K/V投影结果。

高汤锅常备,新菜只加新料——这就是KV Cache的全部哲学。它不改变味道(输出),只改变劳动量(计算)。

Ct=Ct−1∪{Kt,Vt}\displaystyle \mathcal{C}_t = \mathcal{C}_{t-1} \cup \{ K_t, V_t \}
这句话的意思是:第t步的缓存,等于上一步的缓存,再并上新token的一对K和V。整个过程是追加,不是重算。
  1. 上一步缓存C_{t-1}生成到第t-1个token时,缓存里已经躺着0到t-1全部历史token的K/V对。
    Ct−1={(K0,V0),(K1,V1),…,(Kt−1,Vt−1)}\mathcal{C}_{t-1} = \{ (K_0,V_0), (K_1,V_1), \dots, (K_{t-1},V_{t-1}) \}
  2. 新token的K_t与V_t当前步骤唯一需要新计算的投影结果,通过权重矩阵W_K、W_V得到。
    Kt=XtWK,Vt=XtWVK_t = X_t W_K, \quad V_t = X_t W_V
  3. 集合合并∪把新K/V追加到缓存末尾,整体参与后续步骤的注意力计算。
    Ct=Ct−1∪{Kt,Vt}\mathcal{C}_t = \mathcal{C}_{t-1} \cup \{ K_t, V_t \}

显存占用模型:冰箱里的账本

高汤锅虽好,但高汤也要占冰箱的格子。KV Cache最大的代价不是计算,而是显存。要规划好“冰箱”,我们必须先算清账:每个token的K和V,在每一层、每一个注意力头里都各有一份。

先看影响因素,一共五个:序列长度S(已经生成了多少token)、层数L(Transformer有多少层,每层各存各的缓存)、注意力头数H(每层的多个头互不共享缓存)、每头维度D(K/V向量的长度)、以及数据类型b(FP16每元素2字节,INT8每元素1字节)。把这些因素乘起来,再乘以2(K和V双份),就得到了KV Cache的显存公式。

用一个真实算例来感受数量级:一个常见的7B参数模型,32层、32个注意力头、每头维度128,以FP16(每元素2字节)运行,当对话长度达到4096个token时,KV Cache占用约2GB显存。这个数字已经逼近模型权重(约14GB)的七分之一,而且随着序列变长,它还在线性往上走。

这张“冰箱账本”的价值在于让你在部署前就能心算:给定模型结构、目标上下文长度和并发数,KV Cache大概要占多少显存,是不是会撑爆GPU。账算得清,才能决定要不要做优化、做哪种优化。

M=2×L×H×S×D×b\displaystyle M = 2 \times L \times H \times S \times D \times b
显存总占用 = K和V双份 × 层数 × 注意力头数 × 序列长度 × 每头维度 × 每元素字节数。六个因子互相独立,任何一个变大,总占用都线性放大。
  1. 系数2(K与V双份)每个token的缓存同时包含Key和Value两组矩阵,因此整体显存先乘以2。
    22
  2. 层数LTransformer每一层都有独立的注意力头和独立缓存,层数越多缓存份数越多。
    LL
  3. 头数H每层的多个注意力头各自维护一份K/V,互不共享,因此再乘H。
    HH
  4. 序列长度S缓存随已生成token数逐token追加,序列越长缓存越大。
    SS
  5. 头维度D每个token的K和V是维度为D的向量,D越大单条记录占位越多。
    DD
  6. 字节数bFP16每个元素占2字节,INT8占1字节,数据类型直接决定单元素大小。
    bb

长上下文挑战:冰箱不够用了

当菜单越拉越长——从几轮闲聊变成几十页文档问答、超长代码生成、多轮Agent任务——序列长度S持续增长,KV Cache的占用也毫不客气地线性膨胀。冰箱里的高汤罐子越来越多,直到塞满整个冷藏室。

更要命的是,生产环境从来不是只服务一个客人。推理服务器通常同时处理多条请求,每一条请求都有自己独立的KV Cache,互不共享。于是总显存还要再乘一个batch size。一个具体案例:假设上文的7B模型,32K上下文(输入+输出合计),批大小8,粗略估算KV Cache就超过32GB——单张A100(80GB)都会被它吃掉近一半,这还没算模型权重和激活值。

显存被KV Cache挤占,会引发连锁反应:显存不足→被迫减小批大小→GPU利用率下降、吞吐骤降;或者长请求直接OOM→重启服务→延迟飙升。用户体验变差,单位请求的GPU成本却涨了。这就是长上下文场景下“推理延迟上升、并发吞吐下降、部署成本压力”三大问题的共同根源。

八桌客人同时点长菜单,单靠加大冰箱是治标不治本——真正的解法是给高汤“减量”。

Mbatch=B×Msingle=2×B×L×H×S×D×b\displaystyle M_{\text{batch}} = B \times M_{\text{single}} = 2 \times B \times L \times H \times S \times D \times b
并发是KV Cache显存的“放大器”:批大小B条请求同时跑,每条请求各占一份完整缓存,总占用就是单条的B倍。
  1. 单序列缓存M_single一条请求在给定序列长度下占用的KV Cache,即上一节的公式。
    Msingle=2×L×H×S×D×bM_{\text{single}} = 2 \times L \times H \times S \times D \times b
  2. 批大小B并发处理B条请求时,每条请求都有独立的高汤罐,不能合并共用。
    BB
  3. 线性叠加总缓存等于单序列缓存乘以批大小,无共享、无摊薄。
    Mbatch=B×MsingleM_{\text{batch}} = B \times M_{\text{single}}

主要优化方向:给冰箱做减法

冰箱空间有限,办法总比困难多。业界围绕KV Cache的显存优化,大致有四条路线,对应四种“厨房管理学”。

第一是量化压缩,相当于把高汤从大罐子换成小罐子。KV Cache默认用FP16(每元素2字节),如果量化到INT8(每元素1字节),显存直接减半;更激进的还能压到4bit甚至更低。因为K/V是连续数值,量化带来的精度损失通常很小,但对显存是立竿见影的减负。

第二是分页管理显存,代表作是vLLM的PagedAttention。传统推理框架为每条请求预分配一整块连续显存,但实际生成的token数是动态变化的,提前分配会留下大量碎片和浪费。分页管理像操作系统管理内存一样,把KV Cache切成固定大小的块,按需分配、随用随取,能把显存利用率提升一个量级。

第三是缓存淘汰策略。既然历史token不是每个都重要,能不能只留关键的一部分?H2O(Heavy Hitter Oracle)会动态识别“重头戏”token并优先保留;StreamingLLM则用滑动窗口,只保留最近N个token加上开头的几个“锚点”token,把缓存上限锁死。代价是长程依赖可能被削弱,属于用少量精度换大量显存。

第四是前缀共享复用。多条请求如果共享同样的系统提示词(system prompt)或文档前缀,它们的KV Cache前半段是相同的,可以合并共用,新请求直接“继承”已有前缀的缓存,省掉重复计算和重复存储。

厨房里的道理再简单不过:高汤还是那锅高汤,只是换了更小的罐子、更聪明的摆放方式、更合理的用量控制。多管齐下,才能在有限的冰箱空间里接待越来越长的菜单。

Mint8=12×Mfp16=L×H×S×D×1\displaystyle M_{\text{int8}} = \frac{1}{2} \times M_{\text{fp16}} = L \times H \times S \times D \times 1
量化是性价比最高的第一步:字节数b从2降到1,公式里所有项不变,总显存却少了一半。
  1. FP16基准M_fp16原生缓存每个元素占2字节,是量化的比较基准。
    Mfp16=2×L×H×S×DM_{\text{fp16}} = 2 \times L \times H \times S \times D
  2. 系数1/2INT8每元素占1字节,恰为FP16的一半。
    12\frac{1}{2}
  3. 量化后总占用代入后整体缓存减半,在显存受限场景中收益立竿见影。
    Mint8=L×H×S×D×1M_{\text{int8}} = L \times H \times S \times D \times 1

部署实践考量:厨房的全面规划

真到了开餐厅(上线部署)那天,光会熬高汤是不够的,还得通盘规划整个后厨的布局。推理服务占用的显存绝不是只有KV Cache一项,而是四笔账加在一起:模型权重、激活值、KV Cache、预留余量。

模型权重是“固定设备”——买来就是那么大,7B FP16约14GB,不会随请求变化。激活值是“操作台面”——前向计算过程中的临时张量,批大小大时也不可小觑。KV Cache是“高汤库存”——按峰值并发数和最大序列长度来预算,这是最需要精细测算的部分。预留余量是“应急空间”——防止显存碎片或瞬时峰值导致OOM崩溃。

规划的方法论是“由峰值定预算”:先确定产品要支持的最大并发数B_max和最大上下文长度S_max,代入公式算出KV Cache上限,再加上权重和激活,就能判断单卡够不够、要不要张量并行、要不要上量化。上线之后还要盯监控指标:TTFT(首token延迟)、TPOT(每token生成耗时)、吞吐量(每秒生成token数),以及KV Cache的实际利用率——如果利用率低,说明分配策略还有优化空间。

一家好餐厅的竞争力,不只靠菜做得好吃,还靠后厨规划得当:多少灶台、多大冰箱、多少备货,都在开店前算清楚。KV Cache的预算规划,就是大模型推理服务开业前的那张后厨图纸。

Vtotal=Vweights+Mcache+Vact+Vreserve\displaystyle V_{\text{total}} = V_{\text{weights}} + M_{\text{cache}} + V_{\text{act}} + V_{\text{reserve}}
部署显存预算 = 模型权重 + KV Cache峰值 + 激活值 + 预留余量。其中KV Cache是唯一随请求动态变化的大头,必须按峰值场景计算。
  1. 权重V_weights模型参数本身占用的显存,固定不变,如7B FP16约14GB。
    VweightsV_{\text{weights}}
  2. 缓存M_cache按最大并发数与最大序列长度规划KV Cache上限,是动态预算的核心。
    Mcache=Bmax⁡×Msingle(Smax⁡)M_{\text{cache}} = B_{\max} \times M_{\text{single}}(S_{\max})
  3. 激活V_act前向计算中的临时激活值,批大小大时不可忽视。
    VactV_{\text{act}}
  4. 预留V_reserve为内存碎片与峰值波动预留的余量,避免OOM抖动。
    VreserveV_{\text{reserve}}

AI 应用场景

vLLM 推理服务部署

阶段:模型推理部署与性能优化

解决的问题:传统推理框架为每条请求预分配一整块连续显存,KV Cache动态增长导致大量碎片浪费;长上下文高并发时频繁OOM,服务吞吐被显存卡死。vLLM用PagedAttention分页管理KV Cache,按需分配固定大小的块,碎片近乎消失。

具体案例:某团队用vLLM部署Llama-3-8B的线上聊天服务,8张A100支持150路并发;未优化前同样硬件只能支撑32路并发,长对话还经常超时。核心改动就是把KV Cache从“整块预分配”改成“分页按需分配”。

边缘端小模型的KV量化压缩

阶段:模型压缩与资源受限部署

解决的问题:手机、边缘盒子等设备显存(内存)只有几个GB,FP16的KV Cache在长会话中很快吃满。将KV量化到INT8甚至INT4,内存占用减半以上,流式对话不再因为缓存超限而被迫截断历史。

具体案例:某智能音箱厂商在端侧跑7B量级模型,多轮对话超过20轮后延迟明显劣化。对KV Cache做INT8量化后,同长度对话的显存占用从1.8GB降到0.9GB,TPOT延迟降低约35%,用户感知的“越聊越卡”问题消失。

长对话智能体的滑动窗口缓存

阶段:应用层推理优化与成本控制

解决的问题:Agent类应用动辄累积数万token的对话历史,KV Cache随轮次无限增长,成本压力巨大。滑动窗口只保留最近N轮token和开头的系统提示锚点,将缓存上限锁死,用可接受的精度损失换取可控的成本。

具体案例:某客服Agent平均每轮对话消耗20K token的KV Cache,高峰期显存告急。引入StreamingLLM式滑窗后,缓存上限固定为8K token,单卡并发数提升3倍,回答质量在评测集上仅下降1.2%。

Python 演示

python49 行
import numpy as np  def simulate_kv_cache(seq_len=12, d_model=16):    """模拟自回归生成,对比有无KV Cache的K/V投影计算量,并给出显存趋势。"""    np.random.seed(42)    q_all = np.random.randn(seq_len, d_model)    k_all = np.random.randn(seq_len, d_model)    v_all = np.random.randn(seq_len, d_model)    scale = np.sqrt(d_model)     def flops_no_cache():        ops = 0        for t in range(seq_len):            # 无缓存:每一步重新投影前 t+1 个token的K和V            ops += 2 * (t + 1) * d_model        return ops     def flops_with_cache():        ops = 0        for t in range(seq_len):            # 有缓存:每步只投影当前token的K和V            ops += 2 * d_model        return ops     # 用向量化写法演示缓存的增量使用    last_out = None    for t in range(seq_len):        scores = q_all[t] @ k_all[:t + 1].T / scale        weights = np.exp(scores - scores.max())        weights /= weights.sum()        last_out = weights @ v_all[:t + 1]     no_cache_ops = flops_no_cache()    with_cache_ops = flops_with_cache()    print("序列长度: %d, 特征维度: %d" % (seq_len, d_model))    print("无KV Cache K/V投影计算量: %d 次乘加" % no_cache_ops)    print("有KV Cache K/V投影计算量: %d 次乘加" % with_cache_ops)    print("节省比例: %.1f%%" % (100 * (1 - with_cache_ops / no_cache_ops)))     layers, heads, head_dim = 32, 32, 128    bytes_elem = 2  # FP16下每元素2字节    for s in (1024, 4096, 8192, 32768):        mem_gb = 2 * layers * heads * s * head_dim * bytes_elem / (1024 ** 3)        print("序列长度 %6d -> KV Cache %.2f GB" % (s, mem_gb))  if __name__ == "__main__":    simulate_kv_cache()
目的:用一个小型模拟实验验证KV Cache的核心结论——K/V投影计算量从平方级降为线性级,同时直观看到序列长度翻倍时显存线性翻倍。
  1. scores = q_all[t] @ k_all[:t + 1].T / scale当前token的Q与全部历史token的K做点积,历史K来自累积的缓存。
  2. weights = np.exp(scores - scores.max())softmax前先减去最大值,数值上更稳定。
  3. weights /= weights.sum()归一化成和为1的注意力权重。
  4. ops += 2 * (t + 1) * d_model无缓存时第t步要重算t+1个token的K和V,这是平方级冗余的根源。
  5. ops += 2 * d_model有缓存时每步只算当前token的K/V,与历史长度无关。
  6. mem_gb = 2 * layers * heads * s * head_dim * bytes_elem / (1024 ** 3)复现显存公式,把字节数换算成GB。

常见误区

误区我觉得KV Cache和量化、剪枝一样,都是牺牲一点精度来换速度,所以开不开缓存生成的内容会不一样。

正解KV Cache恰恰相反,它缓存的是同一批矩阵投影的数学结果——同一份高汤,提前熬好和现熬现用,味道完全一样。只有浮点运算的舍入误差级别差异,远小于量化带来的精度变化。它是精确去冗余,不是近似。

为何常见:常见的加速手段(量化、剪枝、蒸馏)几乎都有精度损失,初学者很容易把KV Cache也归入“近似优化”的类别。

误区KV Cache是个优化技巧,应该像免费午餐一样,省了计算还不用额外付出什么。

正解KV Cache是典型的“用空间换时间”:把K/V矩阵缓存在显存里,省去重复计算,但每一步都要把新的K/V追加进显存。长上下文下KV Cache动辄数GB甚至数十GB,是推理显存的最大变量。

为何常见:优化手段被宣传为“加速”,人们天然关注收益而忽略成本;且KV Cache的显存占用不在模型参数里,不深入部署就看不见。

误区KV Cache既然省了K/V投影,那是不是整个注意力计算都变轻了,长上下文也不怕?

正解KV Cache只省掉K/V投影这部分重复计算。注意力中的QK^T、softmax、加权求和依然要扫过全部历史token,这部分计算量随序列长度线性增长。缓存解决的是“重复投影”的浪费,不是“历史遍历”的复杂度本身。

为何常见:容易把“省掉重复计算”误听成“省掉所有历史相关计算”,忽略了注意力的核心运算仍然要遍历历史。

误区训练也在不停前向反向,应该也会用到KV Cache来加速吧?

正解KV Cache只服务推理阶段的自回归生成。训练时整个序列是并行前向的,不存在“逐步生成、逐步回顾”的场景;且训练需要保存中间结果做反向传播,缓存收益完全失效。真正和KV Cache打交道的是部署与推理工程师。

为何常见:初学者常混淆训练和推理的计算模式——训练是“一次给全序列”,推理是“一个字一个字往外蹦”,两者的计算特征截然不同。

面试题

Q1. KV Cache在推理阶段缓存的是哪组矩阵?

单选题 · easy

  • A.所有层的K和V矩阵← 正确答案
  • B.所有层的Q矩阵
  • C.模型的权重矩阵
  • D.全部中间激活值

答案:A。KV Cache缓存每层每个注意力头的K/V矩阵,它们是历史token的“风味档案”,供后续步骤反复取用。Q只与当前token相关,用完即弃;权重矩阵本来就不动;全部激活值范围太大且不需要。就像高汤锅只存“风味档案”,不存菜谱。

Q2. KV Cache主要解决什么问题?

单选题 · easy

  • A.训练时的过拟合问题
  • B.生成时的重复计算问题← 正确答案
  • C.模型参数量过大的问题
  • D.数据预处理慢的问题

答案:B。自回归生成每走一步都要重新计算历史token的K/V投影,序列越长重复劳动越多。KV Cache把历史K/V存下来,新token只算自己的K/V,精确消除重复计算。过拟合、参数量、数据预处理都不是它的职责范围。

Q3. 与不使用KV Cache相比,使用KV Cache的生成结果是?

单选题 · easy

  • A.数学上完全等价← 正确答案
  • B.结果完全不同
  • C.多数情况不同
  • D.仅训练有所不同

答案:A。KV Cache缓存的是同一批矩阵运算的结果,省的是“重复计算”而非“不同计算”,因此数学上与全量重算完全等价,最多有浮点舍入级的微差。它不像量化会改变数值精度,而是把已经算过的东西原样保存下来。

Q4. KV Cache显存占用与哪组因素成正比?

单选题 · mid

  • A.训练样本数量
  • B.模型词表大小
  • C.序列长度与层数← 正确答案
  • D.激活函数类型

答案:C。显存公式M = 2×L×H×S×D×b中,序列长度S和层数L是核心乘数,注意力头数H和头维度D同样影响。词表大小、训练样本数、激活函数类型都不出现在公式里,与KV Cache显存无关。

Q5. 32层32头、头维度128的模型在FP16下生成4096个token,KV Cache显存约为多少?

单选题 · mid

  • A.约0.5GB显存
  • B.约1GB显存
  • C.约2GB显存← 正确答案
  • D.约4GB显存

答案:C。代入公式M = 2×32×32×4096×128×2字节 = 2,147,483,648字节,约等于2GB。这类心算是部署规划的基本功:给定模型结构和上下文长度,立刻能估算出KV Cache的量级,判断是否需要在量化、分页上做文章。

Q6. 下列哪项不是KV Cache的优化方向?

单选题 · mid

  • A.量化压缩缓存
  • B.分页管理显存
  • C.增加模型层数← 正确答案
  • D.滑动窗口缓存

答案:C。优化方向是给缓存“减量”:量化缩小单元素字节数,分页管理消除显存碎片,滑动窗口控制缓存上限。增加模型层数反而让L变大,KV Cache显存成倍上涨,与优化目标背道而驰。

Q7. 关于KV Cache,下列说法正确的有哪些?

多选题 · mid

  • A.生成结果与无缓存一致← 正确答案
  • B.会增加额外显存开销← 正确答案
  • C.只用于训练阶段
  • D.能提升生成速度← 正确答案

答案:ABD。KV Cache通过多占显存换取更快的生成速度,缓存的是精确结果所以输出一致。它只用于推理阶段的自回归生成,训练时序列并行前向、无需逐步缓存。因此A、B、D正确,C错误。

Q8. 不使用KV Cache时,生成到第t+1个token,K/V投影需要重新计算多少个token?(token编号从0开始)

单选题 · hard

  • A.仅当前1个token
  • B.前t个历史token
  • C.共t+1个token← 正确答案
  • D.全部S个token

答案:C。无缓存时每一步都要把当前及全部历史token的K/V重新投影一遍。生成第t+1个token时,序列里有编号0到t共t+1个token,所以需要重算t+1个token的K/V。有了KV Cache后,这一步只需算编号t这一个token。

Q9. 下列哪些因素会增大KV Cache的显存占用?

多选题 · hard

  • A.注意力头数← 正确答案
  • B.模型层数← 正确答案
  • C.词表大小
  • D.序列长度← 正确答案

答案:ABD。显存公式M = 2×L×H×S×D×b中,层数L、注意力头数H、序列长度S都是直接乘数。词表大小决定的是嵌入层参数体量,与KV Cache无关——缓存只关心“已经生成了多少token”,不关心“总共可能有多少种token”。

Q10. 批大小从1增加到B且序列长度不变时,KV Cache总显存的变化是?

多选题 · extreme

  • A.增长为原来的B倍← 正确答案
  • B.增长为原来的S倍
  • C.增长为原来的2倍
  • D.增长为原来的B乘S倍

答案:A。每条请求拥有独立的KV Cache,互不共享,总缓存 = B×M_single,因此是原来的B倍。S倍或B乘S倍都是混淆了变量:此处序列长度S不变,只有批大小B在变化。这也是高并发部署显存告急的根本原因——并发数每涨一倍,缓存显存就涨一倍。