路线图

面试 · 原理追问型

星辉 2026-07-02 阅读 6 min 1,162 字 路线图
面试 · 原理追问型 封面

考察目标:能否解释底层机制,展示对"为什么"的深刻理解 约 17 题 答题框架:是什么 → 为什么 → 不这样会怎样 → 如何验证


Q1:LLM 推理为什么需要 KV Cache?KV Cache 为什么大量占显存?#

是什么:KV Cache 是 LLM 推理中缓存在 GPU 显存中的 Key 和 Value 矩阵,避免 Decode 阶段对之前 token 的重复 Attention 计算。

为什么需要:自回归生成中,每次生成新 token 都要与之前所有 token 做 Attention 计算。如果不缓存之前的 K/V,每步都要重新计算——O(n²) 的计算量,生成 500 个 token 就要 500 次完整重算。

不这样会怎样:没有 KV Cache,Decode 阶段每步的计算量随上下文长度平方增长。128K 上下文生成一个 token 要重算 128K token 的 attention,延迟从毫秒级飙到秒级。

为什么大量占显存

  • 每一层 Transformer 都存(如 80 层 ×80)
  • 每个 token 都存(以 GQA 的 LLaMA 70B 计,128K × 320KB/token ≈ 40GB;纯 MHA 约为其 8 倍)
  • 每个并发请求独立一份

KV Cache 经常是推理显存的"第一消耗者"(长上下文场景占比 > 权重)。

如何验证:vLLM 的 vllm:gpu_cache_usage_perc 指标显示 KV Cache 占显存比例;nvidia-smi 看显存占用随并发数线性增长。


Q2:Continuous Batching 为什么能提升吞吐?#

是什么:Iteration-level Scheduling——每生成一个 token 就检查哪些请求已完成、哪些等待中的请求可以加入。

为什么能提升:Static Batching 等整批完成再换下一批,短请求完成后 GPU 空闲等长请求。Continuous Batching 让 GPU 几乎不等待。

不这样会怎样:Static Batching 下,一批 10 个请求,1 个回 500 token,9 个回 10 token。9 个完成后 GPU 空转约 490 步等最后那个,利用率极低。

如何验证:吞吐提升 3-10 倍(取决于请求长度方差)。vLLM 论文有 benchmark 数据。生产环境开/关 Continuous Batching 对比 tokens/s 即可验证。


Q3:PagedAttention 解决了什么问题?#

是什么:类比操作系统虚拟内存分页,把 KV Cache 切成固定大小的 Block(默认 16 token),通过 Block Table 映射,不要求连续显存。

为什么需要:传统 KV Cache 需要预分配连续显存空间。不知道请求最终多长 → 要么浪费(按最大长度分配),要么碎片化(动态 realloc)。

不这样会怎样:显存利用率约 60%(大量碎片 + 预分配浪费),同样显存能服务的并发数少 40%。

如何验证:vLLM 开启 PagedAttention 后显存利用率从 ~60% 提升到 96%+。监控 vllm:gpu_cache_usage_perc 指标。


Q4:Prefix Cache 适合什么场景?Chunked Prefill 为什么改善长上下文?#

是什么

  • Prefix Cache:缓存相同前缀(system prompt、RAG 文档、few-shot 示例)的 KV Cache
  • Chunked Prefill:将长 prompt 的 prefill 切成多个小块,与 decode 交替调度

为什么适合/改善

  • Prefix Cache:多请求共享相同前缀,命中时首 token 延迟可降 30-60%
  • Chunked Prefill:防止一个 128K 的 prefill 把整个 batch 憋死,让 decode 阶段的长尾延迟更平稳

不这样会怎样

  • 没 Prefix Cache:每次请求都全量 prefill,RAG 场景 system prompt + 文档重复计算
  • 没 Chunked Prefill:128K prefill 占满 GPU 2-3 秒,同 batch 的 decode 请求全部等待

如何验证:Prefix Cache 命中率指标(vLLM vllm:prefix_cache_hit_rate);Chunked Prefill 开启前后 decode 请求的 P95 TPOT 对比。


Q5:Speculative Decoding 为什么能加速?量化为什么降显存和成本?#

Speculative Decoding 加速原理:LLM 的 Decode 是串行的(每次只生成 1 token),GPU 大量时间在等待。用小模型"猜"多个 token,大模型一次验证——把串行变成批处理验证,提升 GPU 利用率。吞吐提升 1.5x-3x(取决于 draft model 准确率和任务可预测性)。

追问:为什么投机解码不损失质量?(高频追问,务必答对) 关键在验证用的是修正版拒绝采样(modified rejection sampling),不是"猜对才用"这么朴素:对草稿的每个 token,按 min(1, p_target/p_draft) 的概率接受;一旦拒绝,就从校正后的残差分布 (p_target − p_draft)⁺ 重采一个 token。可以数学证明这样产出的 token 分布与直接用目标模型采样完全一致,所以是 lossless——只改速度不改输出分布(严格说仅受两模型 logits 的硬件数值精度差异影响,可忽略)。

  • 替代方案 & 为什么不用:也可以"草稿 + 只在 greedy(temperature=0) 下比对是否相同"(更简单,但只适用于贪心解码);或用 Medusa/EAGLE 的自草稿头省掉独立 draft model(省了额外一个模型,但草稿质量依赖训练)。拒绝采样方案的通用性最好——支持任意 temperature/top-p 且严格保证分布一致,所以是主流。
  • 上线拿什么证明有效/无害:固定随机种子,对同一批 prompt 对比开/关投机解码的输出与 benchmark 分数(应基本一致 → 证明无损)+ 对比 tokens/s、TPOT(证明有加速)+ 监控接受率(accept rate),接受率过低说明 draft 与 target 不匹配、收益转负,要动态回退到普通 decode。

量化降成本原理:用更少的 bit 存同样的信息。FP16→FP8 权重减半,KV Cache 也减半。省显存 → 同样 GPU 服务更多请求 → 成本下降。但精度有损失(FP8 一般无感,INT4 长生成场景可能露馅)。

不这样会怎样

  • 没 Speculative Decoding:decode 串行,GPU 利用率低(20-40%)
  • 没量化:显存占用大,同样 GPU 服务的并发少,成本高

如何验证:Speculative Decoding 对比 tokens/s;量化前后对比显存占用和输出质量(benchmark 分数)。


Q6:为什么 TTFT 高不一定 TPOT 高?为什么 GPU 利用率高不代表性能好?#

TTFT 高 ≠ TPOT 高:TTFT 是 Prefill 阶段(compute-bound,受 GPU 算力影响),TPOT 是 Decode 阶段(memory-bound,受显存带宽影响)。可能 GPU 算力足够但显存带宽不足 → TTFT 低但 TPOT 高。反之亦然——短 prompt + 高并发,TTFT 低但 TPOT 高。

GPU 利用率高 ≠ 性能好:GPU 利用率是 SM 的繁忙比例。在高 batch 下 SM 确实忙,但每个请求等待时间也变长。一个利用率 95% 但 P99 延迟爆炸的系统,不如利用率 70% 但延迟平稳。

如何验证:监控 TTFT 和 TPOT 的相关性——如果两者不相关,说明瓶颈在不同阶段。监控 GPU 利用率 vs P99 延迟——如果利用率高但 P99 也高,说明 batch 太大。


Q7:为什么 batch size 过大可能延迟变差?为什么长上下文拖慢整体?#

batch size 过大 → 延迟变差:每步 forward 的 token 数 = batch_size × seq_len。batch 越大,单步越慢。虽然吞吐高,但每个请求的单 token 延迟上升。这需要在吞吐和延迟间权衡(max-num-batched-tokens)。

长上下文拖慢整体:一个 128K prompt 的 Prefill 计算量极大,会占用整个 batch 的计算时间,其他小请求在同一个 batch 里被"憋死"。Chunked Prefill 就是为缓解这个设计的。

不这样会怎样

  • batch 无上限:单步 forward 越来越慢,P99 延迟爆炸
  • 长 prompt 不分块:短请求被长请求拖累,P95 延迟飙升

如何验证:不同 batch size 下的 TPOT 曲线(先升后降的拐点);长 prompt 进 batch 前后短请求的 TPOT 变化。


Q8:为什么多模型共享 GPU 会显存碎片?为什么 AI 任务需要队列和配额?#

显存碎片:不同模型的权重大小不同、释放时间不同。如模型 A 权重 40GB,模型 B 权重 20GB,卸载模型 A 后留下的 40GB 空隙不一定能塞进模型 C(需要 50GB,但空隙只有 40GB——碎片化)。

队列和配额:AI 任务(训练/推理)的资源需求往往超过集群容量。没有队列 → 先到先得,后到的抢不到。没有配额 → 一个团队可能吃掉全部 GPU。需要 HPC 级别的 Queuing + Quota + Priority。

不这样会怎样

  • 无碎片治理:集群利用率从 80% 掉到 40%,大作业凑不齐整节点
  • 无队列配额:一个团队吃光资源,其他团队饿死

如何验证:监控 GPU 节点的显存碎片率(nvidia-smi 看空闲块分布);监控 Volcano/Kueue 的 pending jobs 数和等待时长。


Q9:MLA 为什么能压缩 KV Cache?#

是什么:MLA 在 K 和 V 的计算路径中插入低秩瓶颈(latent projection),实际需要缓存的 latent vector 远小于 K/V 原始维度。压缩到 1/8~1/16。

为什么能压缩:传统 MHA 中 K 和 V 的维度 = num_heads × head_dim,每个 head 的 K/V 都要完整存储。MLA 发现这个维度有冗余(低秩),用一个小维度 latent vector 就能恢复出完整 K/V。

不这样会怎样:纯 MHA 的 KV Cache 在长上下文显存爆炸——同规模每 token 约为 GQA 的 8 倍(GQA ≈ 320KB/token,等规模 MHA ≈ 2.5MB/token)。超大上下文模型若用 MHA,百万级上下文的 KV 会到 TB 级,单机根本装不下;即便退到 GQA(320KB/token),1M 上下文也要约 320GB。MLA 把每 token 压到 latent 级别,才使 1M 上下文可行。(口径提醒:320KB 是 GQA 数字,别用它论证"MHA 爆显存"。)

与 GQA/MQA 的区别:GQA/MQA 通过减少 KV head 数量来压缩,但会损失精度。MLA 通过低秩投影压缩,decode 时从 latent vector 恢复完整 K/V,质量接近 MHA。本质是用计算换显存。

追问:恢复 K/V 的计算代价压在 prefill 还是 decode?(高频追问) MLA 缓存的是压缩后的 latent(c_KV),每个 decode step 都要从 latent 重建/吸收出 K、V 再算 attention,所以恢复计算主要压在 decode 每一步(prefill 也做,但 decode 逐 token 反复做、累积代价更大)。工程上的关键优化是 weight absorption——把"latent→K/V"的上投影矩阵吸收进 Q/O 投影里,避免显式还原出完整 K/V,从而少一份大矩阵乘和显存搬运;FlashMLA 进一步把这步融进 kernel。所以 MLA 是"用 decode 端算力换显存",配合 weight absorption 后额外算力被压得很小,净收益(省显存 → 高并发 → 长上下文)远大于代价。

  • 替代方案 & 为什么不用:GQA 实现简单、无恢复计算,但压缩比有限(约 1/8 上限)且质量随分组变差;MLA 压缩比更高(1/8~1/16)、质量更接近 MHA,代价是实现复杂 + decode 多一步(已被 weight absorption 摊薄)。追求极致长上下文/高并发选 MLA,够用选 GQA。

如何验证:对比 DeepSeek-V3(MLA)和 LLaMA 70B(GQA)的 KV Cache 大小;同样显存下的并发数对比;以及开/关 weight absorption 时 decode 的 TPOT 差异。


Q10:MoE 推理里 EPLB 解决什么问题?#

是什么:EPLB(Expert Parallelism Load Balancer)动态调整 expert 到 GPU 的映射,解决 MoE 推理中专家路由不均衡。

为什么需要:MoE 模型有多个 expert(如 DeepSeek-V4 有数百个 expert),请求通过 Router 分配给不同 expert 处理。但实际业务中 expert 负载极度不均衡——热门 expert 过载,冷门 expert 空闲。生产数据显示热门 expert 负载是冷门的 10-20 倍。

不这样会怎样:集群吞吐被热门 expert 所在 GPU 卡死。某卡显存 95% QPS 到上限,另一卡显存 25% GPU 空闲——整体吞吐被最慢的卡限制。

EPLB 做法:实时监控每个 expert 的请求量;将热门 expert 的副本分散到多张空闲 GPU(replication);冷门 expert 合并到同一张 GPU;周期性重平衡。

如何验证:开启 EPLB 前后各 GPU 的显存和 SM 利用率分布——从差异巨大到均匀分布。


Q11:PD 分离为什么能同时改善 TTFT 和吞吐?#

是什么:将 Prefill 和 Decode 分配到不同 GPU 节点,各自独立优化。

为什么能改善:Prefill(compute-bound)和 Decode(memory-bound)的资源需求相反。放同 GPU 时:

  • Prefill 算力突发抢 decode 的显存带宽 → TTFT 升高
  • Decode 的持续性访问占用显存,prefill 的矩阵乘算力利用率低 → 吞吐下降

分离后:

  • Prefill 节点:全力计算 prompt,不受 decode 干扰 → TTFT 降低
  • Decode 节点:专注逐 token 生成,可服务更多并发 → 吞吐提升

不这样会怎样:混合部署时一个 128K prefill 进来占满 GPU 2-3 秒,同 batch 的 20 个 decode 请求全部等待,TPOT 从 30ms 飙到 500ms。

代价:需要高速网络传输 KV Cache(100Gbps+ RDMA),增加了架构复杂度。KV 传输可能成为新瓶颈。

如何验证:DeepSeek 生产数据——PD 分离后 TTFT 降低 30-50%,整体吞吐提升 2-3x。


Q12:分级 KV(GPU→CPU→远端)命中率怎么影响成本?#

是什么:KV Cache 在三级存储中的分布决定了每次请求的 prefill 计算量。

为什么影响成本

  • L0 命中(GPU 内):零额外开销,最优
  • L1 命中(CPU DRAM):需要从 CPU 拷贝到 GPU(~10μs),无 prefill 计算
  • L2 命中(远端 SSD):拷贝延迟 ~100μs,无 prefill 计算
  • 全 Miss:需要在 GPU 上重新做完整 prefill(compute-bound,延迟最高)

(本库的规范口径,其他文件引用此数字都指回这里) 业界常引的"128K 上下文、80% 命中率 → 省约 69% prefill 成本"就出自这类场景:约 80% 的请求/前缀命中缓存、跳过 prefill,省下的 prefill 计算接近命中比例。但这是特定 benchmark(特定上下文长度、特定数据集与前缀分布)下的实测值,不是通用常数——换场景(不同前缀占比 / 命中率 / 上下文长度)数字就会变,别把它当"万能公式"套到 RAG / 长上下文 / 多轮所有场景。

不这样会怎样:全放 GPU 显存太贵(128K KV = 40GB,H100 总共 80GB);全 Miss 则每个请求全量 prefill,算力成本高。

命中率每下降 10%,prefill 成本增加约 10%(近似线性关系)。所以业务前缀稳定性(system prompt 不变、文档集稳定)是关键——前缀一变,缓存全部失效。

如何验证:监控 LMCache 的命中率指标;对比开启前后 GPU 算力消耗(prefill FLOPS)。


Q13:为什么 system prompt 稳定性影响所有缓存优化?#

是什么:Prefix Cache、RadixAttention、分级 KV 都依赖前缀稳定。system prompt 里如果有动态字段(时间戳、随机 ID),所有缓存命中率归零。

为什么影响:缓存按 token 序列匹配。system prompt 第一个 token 不一样,整条前缀匹配失败,KV Cache 全部失效。即使只差一个时间戳 token,后续 6K token 的 KV 都要重算。

不这样会怎样:system prompt 带时间戳 → 缓存命中率 0% → prefill 全量计算 → TTFT 高 + 成本高。6K system prompt 每个 request 都重算,浪费极大。

如何验证:监控 vllm:prefix_cache_hit_rate;检查 system prompt 是否含动态字段;A/B 测试固定 vs 动态 system prompt 的 TTFT 差异。

如何解决:把动态内容(时间戳、用户 ID)移到 user message 里,system prompt 完全固定。


Q14:max-model-len 设太大到底有没有代价?"并发砍到 1/16"这种说法对吗?#

先纠正一个常见误解:网上常说"max_model_len 从 8K 调到 128K,并发直接掉到 1/16(256→16)"——这是错的,而且和 PagedAttention 按需分配(见 Q3)自相矛盾。KV Cache 池的实际大小由 gpu-memory-utilization 决定(启动时按这个比例把剩余显存整块划给 KV 池),与 max_model_len 无关;PagedAttention 按 block 给每个请求按实际长度分配,短请求不会因为 max_model_len 大就多占显存。

那 max_model_len 是什么:它只是单个请求上下文长度的上限(prompt + 输出),超过就拒绝/截断,本身不为每个请求预扣显存。

日志里的 "maximum concurrency":vLLM 启动会打印一个"最大并发",它是**"假设每个请求都用满 max_model_len"算出来的理论最坏值**,不是真实并发。真实并发取决于请求的实际平均长度——max_model_len=128K 时这个数字很小,但业务请求平均才几 K,实际能跑的并发远高于它。

max_model_len 设大真正的代价

  1. 启动时显存 profiling 预留:引擎按 max_model_len 跑一次 profiling 估算峰值激活/临时缓冲并预留,设很大会挤占本可给 KV 池的显存。
  2. 历史版本约束:较老的 vLLM 要求 max_num_batched_tokens ≥ max_model_len,设成 128K 会连带抬高单步 batch token 数、影响调度(新版已放宽)。
  3. 极端长请求真会吃大量 KV:允许 128K,就可能真进来一个 128K 请求,瞬间吃掉大块 KV 池、挤走其他请求——这是"允许长请求"的代价,不是"每个请求都变贵"。

如何验证:固定 gpu-memory-utilization、只改 max_model_len,看 vllm:gpu_cache_usage_perc 和真实可跑并发——会发现 KV 池大小基本不随 max_model_len 变(推翻"1/16"),变的是启动预留和日志里的理论并发数。

如何解决:按业务真实需求设(99% 场景 8K-32K 足够)+ 对超长请求单独分流/限流,而不是靠"调小 max_model_len 去省显存"这个错误心智。


Q15:为什么 scale-to-zero 会导致冷启动超时?#

是什么:scale-to-zero 是空闲时把推理实例缩到零,有请求时再启动。冷启动指从零启动到能服务请求的过程。

为什么会超时:模型权重加载是冷启动的主要耗时。70B 模型权重 140GB,从对象存储加载到 GPU 显存要 3-5 分钟。如果客户端 timeout 是 30s,第一个请求必然超时。

不这样会怎样:不做 scale-to-zero → 空闲时 GPU 也常驻,成本高;做了不优化冷启动 → 第一个请求超时,用户体验差。

如何验证:记录从 scale-to-zero 到第一个请求成功的耗时;对比不同模型大小(7B vs 70B vs 405B)的冷启动时间。

如何解决:思路是"少加载、别缩零、给足时间、先预热"——权重预缓存本地 NVMe + keep-warm ≥ 1 副本 + startupProbe 放宽 + 客户端 timeout 匹配 + 预热请求。完整优化清单见场景题 4-Q12。


Q16:FP4 相比 INT4 的取舍是什么?#

是什么:FP4 是浮点 4bit(有指数+尾数),INT4 是定点 4bit(纯整数)。

为什么有取舍

  • FP4 优势:动态范围大(浮点指数),精度更好,适合激活值波动大的场景;新硬件(B200/昇腾 950PR)原生支持
  • INT4 优势:生态成熟(AWQ/GPTQ),通用 GPU(Ampere+)都能用,工具链完善
  • FP4 劣势:只新硬件支持,老硬件用不了;工具链还在发展
  • INT4 劣势:定点精度有限,激活值波动大时需要校准(SmoothQuant);长生成场景可能精度露馅

不这样会怎样

  • 只用 INT4:新硬件(B200/950PR)的 FP4 原生算力用不上
  • 只用 FP4:老硬件(A100)跑不了

如何验证:同一模型在 B200 上 FP4 vs INT4 的精度(benchmark 分数)和性能(吞吐)对比。

如何选:B200/昇腾 950PR → FP4(原生支持,精度好);A100 → INT4(AWQ);H100 → FP8(折中,不纠结 4bit)。


Q17:跨租户共享前缀缓存 / KV Cache 有什么安全隐私风险?#

是什么:为省 prefill,前缀缓存(vLLM APC、RadixAttention、LMCache)会让不同请求、甚至不同租户复用同一段前缀的 KV。一旦跨租户共享,就引入了信息泄露面——这是很多人只谈性能、忽略的一环。

风险机制

  • 时序侧信道(timing side-channel):攻击者构造一段前缀去请求,如果这段前缀刚被别的租户用过(已在缓存里),他的 TTFT 会明显更低(命中 vs 未命中)。通过测 TTFT,攻击者能探测"某段文本 / 某文档 / 某密钥是否被别人输入过"——即使拿不到内容,也能确认"是否存在"(membership 泄露)。危害:探测别的租户是否问过某敏感问题、某私有文档是否在别人上下文里。
  • 内容层面:若实现允许跨租户直接复用别人产出的输出/KV(如共享语义缓存),更严重——可能直接读到别人的数据。
  • RAG 放大:私有知识库文档的 KV 若跨租户缓存,能被探测"某文档是否存在于系统"。

不这样防会怎样:多租户平台开了全局前缀共享 + 无隔离 → 一个租户可探测另一个租户的输入存在性;合规/隐私审计过不了。

怎么防

  • 按租户/按 Key 做缓存隔离:缓存 key 里带 tenant_id,默认不跨租户复用(只在同租户内共享)。牺牲一点跨租户命中率换安全,多租户平台必须这么做。
  • 只对公共前缀(平台统一 system prompt、公开文档)开放跨租户共享,私有内容一律隔离。
  • 需要极致省成本又要防时序探测时,可对缓存命中引入固定延迟/抖动削弱 timing 信道(有性能代价,按需取舍)。

如何验证:红队用"命中 vs 未命中"的 TTFT 差异做探测测试;审计缓存 key 是否包含租户维度;检查语义缓存是否会跨租户返回他人结果。