LLM Infer Infra 学习资料库

基础面试与算例:能解释、能算账、能处理失败

返回主题 · 上一篇:学习计划 · 资料与版本

这章把四套问答收束为一条面试主线,优先覆盖 KV Cache 存储。它整理常见考察主题,不声称是任何公司的真实题库或频率统计。

本篇是基础回答和计算题底座。专家岗位按总纲继续追问系统约束、跨层协议、故障、成本、落地与演进,不能把能答完本篇等同于达到 P7/P8。

建议每题先独立说两分钟,再核对机制、适用条件和证据。回答“通常如此”以后,要能举出一个不成立的场景。

1. 五分钟讲清请求全链路,再落到具体实现

可以按下面的逻辑组织,但不要背成固定话术:

请求先经过入口与路由,再由运行时调度执行,GPU KV 管理器负责活跃状态。重复输入可以通过前缀缓存减少 Prefill;更大的保留容量或跨实例复用需要分层管理与共享存储。缓存管理层处理语义、布局、分层和加载协议,存储层管理对象位置、可读性、副本与回收,传输层搬运字节。最终必须确认状态已恢复到正确位置并满足设备消费条件,才允许引擎减少计算。是否值得加远端缓存,要比较省下的计算和新增查询、传输、排队成本。

随后选自己读过的一个实现,指出请求状态、缓存发布、读保护和完成通知的具体入口。面试官追问哪个环节,就展开对应路径,给出源码或实验依据。

2. 公共基础必会的六题

Q1:KV Cache 到底节省了什么?

它避免对固定历史前缀重复执行生成历史 K/V 所需的前向计算。当前 token 的 Q/K/V、读取历史 KV 的 attention,以及其他模型层仍要执行。长上下文 Decode 仍可能被 KV 读取限制。

追问准备:写出 attention 公式,解释为什么普通 Decode 不需要历史 Q;说明完整 prompt 命中后仍有加载、调度和首 token 处理。

Q2:算一条 8K 请求的 KV

普通 GQA 模型:32 层、8 个 KV heads、head dimension 128、BF16。

2 × 32 × 8 × 128 × 2 = 128 KiB/token
8,192 × 128 KiB = 1 GiB

若 TP=4 且 KV heads 均匀切分、无复制,则每 rank 为 256 MiB。还需加页尾、对齐、临时 buffer 等。MLA、滑窗、混合状态另算。

Q3:同一段文档为什么不能随便复用?

文档前面的 system prompt、其他文档及位置会影响隐藏状态和深层 KV。精确复用要求完整前缀计算语义兼容,并且目标能正确解释物理布局。只比较文本或 hash 算法不够。

追问准备:更换 LoRA、模型 revision、租户 namespace、文档顺序各会影响什么。temperature 通常在 logits 后采样,不直接改变固定 prompt KV;生成 token 变了则后续前缀会变。

Q4:分页与前缀树为什么不冲突?

分页解决 GPU KV 的物理存放与地址映射,前缀树解决共享前缀的匹配和管理。树节点可以关联分页存储中的位置。FlashAttention 又优化单次 attention 内部的访存,是另一层。

Q5:什么时候远端拉取比重算更慢?

比较相同负载下的查询、加载关键路径、额外排队与节省的 Prefill。若加载花 100 ms、只省 60 ms,延迟上没有收益;若省 700 ms,值得进一步实测。还要算写入、复制和并发争用是否降低整体 goodput。

Q6:命中率 90% 能说明什么?

先问分母是什么:请求、token、字节,还是仅查询对象?在哪一层?是否成功加载并减少计算?许多请求只命中一个很短 system prompt,也能有很高请求命中率,但总体节省有限。

3. KV 存储岗位必须讲通的八题

Q7:一次外部缓存加载经过哪些状态?

识别候选前缀 → 查到对象 → 保护/选择副本
→ 加载到 staging/host → 成功发布或保留
→ 恢复 GPU block → 满足同步 → 引擎按有效范围执行

不同实现会合并步骤或采用直接路径,但“查到”和“可执行”不能合并。多个 rank/group 还要确认一致的可用状态。

Q8:写入为什么要 staging 或 PutStart/PutEnd?

分配出地址并不代表内容完整。先写入、确认完成、再发布,能避免读者读半成品。失败写者需要清理,重复写者要处理竞争,且清理不能提前回收仍在传输中的内存。

LMCache L1 发布和 Mooncake Store 发布是不同层的状态,不能用本地完成冒充远端可读。

Q9:lease、引用计数、CUDA event 能互相替代吗?

不能。lease 是有限时间的对象读保护,引用/锁保护各自管理域中的使用者,CUDA event 表达设备执行依赖,传输状态表达网络任务完成。一个对象跨多层流动时,要同时满足相关边界。

追问时画出数据所有者和最后一个使用者。若不知道谁会最后写这块内存,就还不能证明可以释放。

Q10:超时后为什么不能直接释放 buffer?

超时通常只表示上层不愿继续等待;设备或网络任务可能仍在运行。立即复用会让旧任务覆盖新请求的数据。需要按后端协议取消、确认结束或排空,再交还内存;不能拿失败 buffer 继续推理。

Q11:如何设计 chunk 和 key?

先明确精确前缀依赖,再定义模型/配置/共享范围以及 rank/group/layout 身份。chunk 与引擎 page 对齐,避免只有半块有效而无法恢复。通过实验扫描粒度:同时看实际前缀覆盖、元数据对象数、带宽、加载延迟和最终 SLO。

不要只回答“用 SHA256,chunk 越大越快”。正确 hash 输入和物理布局兼容性比算法名字更重要。

Q12:两个副本是不是可以容忍一台机器故障?

要确认两个副本实际完成且可用,位于不同主机/目标故障域,客户端有发现失效与切换能力。两个 segment 可能同属一台主机。Master HA 保护目录服务,不自动恢复丢失 payload。

Q13:为什么有时不用 Redis 或普通对象存储?

先看数据大小、访问频率、延迟预算和现有运维条件。KV payload 往往较大,可能需要直接写入注册 buffer、减少中间拷贝、配合 GPU 和布局,专门的数据路径有价值。但普通服务可能足以完成原型或低频场景。

比较的是序列化/拷贝、数据路径、对象和并发粒度、容错需求与成本,不能简单说“Redis 慢”或“RDMA 必然好”。

Q14:缓存服务故障时怎么回退?

miss 和可恢复读取失败可以回到计算,但必须保证目标内存安全、引擎状态一致。坏数据、未知完成状态不能当作命中。若故障同时影响很多请求,还要熔断、限流和有限重试,防止重算风暴把 GPU 打满。

“都重算就好了”只回答了功能恢复,没有回答容量与 SLO。

4. 性能与系统取舍四题

Q15:命中提高,TTFT 却变差,怎么排查?

固定请求集和负载,确认命中口径。再拆 lookup、队列、远端读、H2D、重排、同步、剩余 Prefill;看哪段延迟增加。随后用 local-only、无预取、限制等待、不同 chunk 等对照定位,避免一次改多个因素。

Q16:多个 NIC 为什么没线性扩展?

可能受 CPU 单线程、NUMA 远端访问、PCIe、DRAM 带宽、GPU 拷贝、对象热点或任务分配限制。先看各 NIC 实际流量和拓扑,再判断数据是否真的分散;不要仅按链路数相加做承诺。

Q17:什么时候做 PD 分离?

当 Prefill 与 Decode 干扰明显,且独立资源配置带来的收益能覆盖 KV 交接、路由和负载失衡成本时。先测单实例调度改善,再测分离。短输入、低并发、网络慢时可能没有收益。

Q18:怎样证明是远端缓存带来的收益?

同一版本、模型、负载和 SLO 下,分别测无复用、本地复用、host、远端增量。需要确认请求实际从远端加载,而不是仍命中原引擎 GPU。重启引擎可作为一种方法,但前提是缓存持有者仍存活且远端写已完成。

同时验证输出/数值或任务质量,保留原始日志、配置与错误计数。

5. 一个完整系统设计算例

需求与假设

设计多实例长文档问答的共享 KV 层。沿用上面的模型,输入 8,192 token,其中可复用精确前缀 6,144 token。峰值 20 req/s,保留 1,000 个不同热前缀,TP=4、均匀分片,chunk=256,每 rank/chunk 一个对象,单 group。

先确认:前缀是真的相同吗?目标 P99 TTFT/ITL 是多少?输出有多长?峰值持续多久?租户是否允许共享?网络和故障可用率要求是什么?以下只是教学预算,不能替代这些输入。

容量

每条热前缀 = 6,144 × 128 KiB = 0.75 GiB
1,000 条逻辑数据 = 750 GiB
两份副本 payload = 1,500 GiB
若 payload 预算占物理池 70%,物理预算约 2,143 GiB

剩余 30% 要明确分给元数据、staging、碎片和预留;各节点的 LMCache/HiCache host 缓存、GPU KV 等还需单独核算。若前缀间可共享子块,这个独立保存估算可作为保守起点,再按实际去重调整。

对象与元数据

每前缀 6,144 / 256 = 24 chunks
对象身份 = 1,000 × 24 × 4 = 96,000
双副本记录 = 192,000
每 rank/chunk payload = 256 × 128 KiB / 4 = 8 MiB

一份对象身份可以有多个副本。增加 group 或按层拆对象会增加元数据数量;batch RPC 减少调用次数,但没有消除对象管理工作。

带宽与在途量

若每个请求都从远端读完整 6K 前缀:

读 payload = 20 × 0.75 = 15 GiB/s ≈ 128.85 Gb/s
200 Gb/s 线速 ≈ 23.28 GiB/s
若按 70% 有效带宽预算,约 16.30 GiB/s,读流量已接近这个预算

若 10% 请求产生新的完整 8K 数据,双副本写 payload 约 20 × 0.1 × 1 × 2 = 4 GiB/s。按实际拓扑区分 NIC 收发方向和共享总线,不能把全双工两方向相加就直接判定超过线速。

平均加载停留 100 ms 时,读流量对应约 15 × 0.1 = 1.5 GiB 在途数据。这是稳定均值估算,P99 与突发还需预留。

架构与请求

入口鉴权/限流
→ 同时考虑缓存位置与排队的路由
→ 推理引擎 / 本地 KV
→ LMCache 或 HiCache 的加载管理
→ Mooncake Store + 数据资源池

先复用 GPU/host,远端加载设预算,miss 重算。新对象按收益准入,热点考虑副本或局部缓存,目录事件帮助路由但不替代实际读取校验。

必须覆盖的失败矩阵

故障 处理方向 需要观察
查询有、加载无 释放适用保留,按有效状态重算 stale hit、load miss、TTFT
某个 rank 加载失败 确定共同安全范围与一致执行 分片结果、collective 次序
写者中途退出 不发布半成品,清理孤立状态 未完成对象与空间回收
数据节点退出 尝试有效副本,否则降级重算 失效发现、切换延迟、回源压力
Master 切换 重连/恢复并验证对象状态 目录恢复时间、旧主隔离
传输超时 按协议结束在途访问后回收 buffer 保留、取消完成、泄漏
缓存总体失效 限制重试和回源,保护 Decode 队列、拒绝率、goodput

6. 做一个可以展示的项目

选择一个主题即可,重点是闭环。

项目 最小范围 证明什么
共享 KV 缓存收益边界 一个引擎、一种后端、冷热/低复用三组负载 何时加载比重算划算
异步对象生命周期 普通读写 + 超时/取消/写者退出 半成品不可见、在途内存不提前复用
缓存感知调度模拟 排队、缓存位置、搬运成本的离线模型 最热节点未必最快,结论标为模拟
传输瓶颈定位 对象大小与并发扫描,分 TE/Store/引擎测量 峰值带宽与请求延迟差在哪里

最小报告包含:问题与假设、版本/硬件、工作负载、正确性验证、基线、单因素对照、原始数据、收益与退化条件、故障行为、未验证项。

五分钟项目讲述

第一分钟说明业务和瓶颈;第二分钟画原路径与改后路径;第三分钟讲最难的正确性问题;第四分钟给相同条件下的结果;第五分钟解释失效条件和下一步。没有实测时,直接说已完成源码推演/模拟,后面哪些指标待硬件验证。

7. 面试验收与原题库使用方法

能力 合格表现
解释 不靠术语堆叠,把一个请求讲到底
计算 独立完成容量、对象数、带宽与单位换算
源码 指出发布、完成、失败和回收的实际入口
取舍 至少给出一个当前方案不适用的场景
实践 有可复现证据,能区分事实、估算和假设

通过上面主线后,再按薄弱主题进入实现题库:vLLM 60 题、SGLang 36 题、LMCache 60 题、Mooncake 50 题。重复基础题只选一份主答,其余用于补追问,不要求逐份背完。

输入主题、术语或问题