实测与故障演练:测什么、怎么测、怎样记录
实验主题 · GPU 与 kernel 基础 · 设计图册 · 专家面试总纲
资料里的数字都是推导或公开数据,不是你的实测。第 2 节整理了常见硬件和项目的公开数据,可以先用来估算。面试时,能拿出自己测过的数据和做过的故障演练,比背公式有说服力得多。这一页给出测什么、怎么测、记什么、怎样算通过,结果需要你在真实环境里填写。
安全提醒:所有压测和故障注入只在独立的测试环境中进行。不要在生产集群上杀进程、断网或清空缓存。
1. 先记录环境
没有环境信息的数字无法比较。每次实验先填这张表:
| 项目 | 怎么查 | 记录 |
|---|---|---|
| GPU 型号、数量、驱动、CUDA | nvidia-smi | |
| GPU 与网卡拓扑 | nvidia-smi topo -m | |
| PCIe 代数与宽度 | nvidia-smi -q 中的 PCIe 信息,或 lspci -vv | |
| RDMA 网卡与端口状态 | ibv_devinfo | |
| NUMA 布局 | numactl --hardware | |
| 软件版本 | 引擎、LMCache、Mooncake 的版本或 commit | |
| 模型与 KV 配置 | 模型 revision、dtype、KV dtype、TP/PP、block/chunk 大小 |
2. 常见场景的公开参考数据
没有自己的测量数据时,可以先用公开规格和公开测试结果做估算,等有环境后再用实测替换。下面的数字分三类,引用时要说清是哪一类:厂商规格(理论峰值)、常见实测量级(公开测试中经常看到的范围,具体平台差异大)、项目公开结果(某个项目在特定配置下报告的数据)。
2.1 GPU 显存带宽与算力(厂商规格)
| GPU | 显存 | 显存带宽 | BF16 稠密算力 | 平衡点(算力 ÷ 带宽) | GPU 间互连 | 主机接口 |
|---|---|---|---|---|---|---|
| A100 80GB SXM | 80 GB HBM2e | 约 2.0 TB/s | 约 312 TFLOPS | 约 153 FLOP/byte | NVLink 600 GB/s | PCIe Gen4 |
| H100 SXM | 80 GB HBM3 | 约 3.35 TB/s | 约 989 TFLOPS | 约 295 FLOP/byte | NVLink 900 GB/s | PCIe Gen5 |
| H200 SXM | 141 GB HBM3e | 约 4.8 TB/s | 约 989 TFLOPS | 约 206 FLOP/byte | NVLink 900 GB/s | PCIe Gen5 |
说明:算力是不含稀疏加速的稠密 BF16 值;NVLink 带宽是双向合计。用这些数字推出的 Decode 下界(以资料示例模型为例):
| 估算项 | A100 | H100 | H200 |
|---|---|---|---|
| 8B 模型 BF16 权重(16 GB)每步读一遍 | 约 7.8 ms | 约 4.8 ms | 约 3.3 ms |
| 一个 8K 上下文请求每步读一遍 KV(1 GiB) | 约 0.53 ms | 约 0.32 ms | 约 0.22 ms |
这些是只考虑显存带宽的理论下界,实际还有 kernel 效率、调度和通信开销。原理见 GPU 与 kernel 基础。
2.2 PCIe 与 RDMA 网卡
| 链路 | 理论带宽(每方向) | 常见实测量级 | 传 1 GiB 约需 |
|---|---|---|---|
| PCIe Gen4 x16(锁页内存 H2D) | 约 32 GB/s | 约 24–26 GB/s | 约 41–45 ms |
| PCIe Gen5 x16(锁页内存 H2D) | 约 64 GB/s | 约 50–55 GB/s | 约 20–22 ms |
| 200 Gb/s 网卡(如 HDR IB、200GbE RoCE) | 25 GB/s | 大消息 ib_write_bw 常见约 23–24 GB/s | 约 45–47 ms |
| 400 Gb/s 网卡(如 NDR IB、400GbE RoCE) | 50 GB/s | 大消息 ib_write_bw 常见约 46–48 GB/s | 约 22–23 ms |
- 可分页内存的 H2D 通常明显低于锁页内存,而且不能和计算真正重叠,具体数值因平台差异很大。
- 小消息延迟:同一交换机下 RDMA 单边写的小消息延迟常见在几微秒量级(直连 InfiniBand 约 1–2 微秒);TCP 通常在几十微秒量级。小对象的耗时主要由这些固定延迟决定,而不是带宽。
- 多块 GPU 共用 PCIe 交换芯片、网卡跨 NUMA 节点时,实测会明显低于上表。
2.3 项目公开结果
| 来源 | 场景与配置 | 报告结果 | 怎样理解 |
|---|---|---|---|
| Mooncake README | 传输 40 GB 数据(相当于 LLaMA3-70B 128K token 的 KV),4×200 Gbps RoCE | 约 87 GB/s,约为 TCP 的 2.4 倍 | 线速合计 100 GB/s,利用率约 87% |
| 同上 | 同样数据,8×400 Gbps RoCE | 约 190 GB/s,约为 TCP 的 4.6 倍 | 线速合计 400 GB/s,利用率约 48%;网卡越多,越难线性扩展 |
| Transfer Engine 文档 | transfer_engine_bench 示例运行,持续 10 秒 | 约 19.87 GiB/s | 文档注明超过了该主机单卡的最大吞吐(使用了多卡) |
| Mooncake README 引用的 Kimi K2 部署 | 128 张 H200,PD 分离 + 大规模专家并行 | Prefill 约 22.4 万 token/s,Decode 约 28.8 万 token/s(集群合计) | 是整套部署的结果,不能直接归因于某一个组件 |
核对一下第一行:LLaMA3-70B 是 80 层、8 个 KV head、head 维度 128,BF16 下每 token 2 × 80 × 8 × 128 × 2 = 320 KiB,128K token 正好 40 GiB(约 43 GB),和 README 写的“40 GB”是同一量级,README 用的是十进制口径。按 87 GB/s 传完约需 0.49 秒,按 190 GB/s 约 0.23 秒。
2.4 用公开数据做一次完整估算
场景:LLaMA3-70B 级别模型,8 张 H100,一个 32K token 的共享前缀被远端命中。
KV 大小:320 KiB × 32,768 ≈ 10 GiB(约 10.7 GB)
加载(串行估算):
单张 400G 网卡按 46 GB/s:10.7 ÷ 46 ≈ 0.23 s
若 8 张 GPU 各自用 Gen5 H2D 并行接收,每张约 1.34 GB,按 50 GB/s:≈ 0.03 s
再加查询和同步的固定开销,合计约 0.3 s 量级
重算 Prefill:
主体计算 ≈ 2 × 70e9 × 32,768 ≈ 4.6e15 FLOP
8 × 989 TFLOPS × 50% 利用率 ≈ 4.0e15 FLOP/s
≈ 1.2 s(未计入注意力项,长上下文下实际更长)
结论:在这些假设下,远端加载明显比重算快。
换成 1K token 的短前缀,KV 只有约 0.3 GiB,重算只要几十毫秒,查询和网络的固定开销占比就变大,收益会缩小甚至变负。可以把这些公开数字填进互动盈亏计算器试试。有了自己的环境后,按下一节逐层实测,替换这里的公开数字。
3. 分层测量:从硬件到端到端
按从下到上的顺序测。每一层的结果解释上一层:端到端慢了,先看是不是某一层本身就达不到预期。
3.1 H2D 与 D2H 带宽
目的:确认 CPU↔GPU 拷贝的实际带宽,比较锁页内存与可分页内存。
import torch
size = 1 << 30 # 1 GiB
for pinned in (True, False):
host = torch.empty(size, dtype=torch.uint8, pin_memory=pinned)
dev = torch.empty(size, dtype=torch.uint8, device="cuda")
for _ in range(3):
dev.copy_(host, non_blocking=True)
torch.cuda.synchronize()
start = torch.cuda.Event(enable_timing=True)
end = torch.cuda.Event(enable_timing=True)
start.record()
for _ in range(10):
dev.copy_(host, non_blocking=True)
end.record()
torch.cuda.synchronize()
gib_s = 10 * size / 2**30 / (start.elapsed_time(end) / 1000)
print(f"pinned={pinned}: H2D {gib_s:.1f} GiB/s")
记录:两种内存的 H2D、D2H 带宽;GPU 编号与所在 NUMA。通过标准:锁页内存接近该 PCIe 代数的常见实际值(Gen4 约 25 GB/s,Gen5 约 50 GB/s);明显偏低时检查拓扑和 NUMA 绑定。
3.2 RDMA 链路带宽
目的:确认两台机器之间网卡的实际带宽,排除网络配置问题。
# 服务端
ib_write_bw -d <设备名> --report_gbits
# 客户端
ib_write_bw -d <设备名> --report_gbits <服务端地址>
ib_write_bw 来自 perftest 工具包。记录:不同消息大小下的带宽和延迟。通过标准:大消息带宽接近网卡线速的合理比例;小消息延迟稳定。
3.3 Transfer Engine 吞吐
目的:在 Mooncake TE 层测量读写吞吐,看软件层相对裸链路损失了多少。Mooncake 编译后提供 transfer_engine_bench:先以 --mode=target 启动目标节点,再在发起节点指定 --segment_id 运行;可调 --operation(read/write)、--block_size、--batch_size、--threads、--duration。完整参数见 Transfer Engine 文档。
记录:block 大小从 64 KiB 到 64 MiB 的吞吐曲线;线程数与吞吐的关系。重点观察:小 block 时吞吐下降多少——这决定了 KV 对象(chunk × rank)不宜太小。
3.4 Store 读写与加载链路
目的:测量一次完整的对象写入和读取(含元数据查询),再测 LMCache 从远端加载到 GPU 的时间分解。
记录:对象大小、并发数下的 put/get 延迟分布(P50/P99);加载链路中查询、远端读、H2D 各段耗时。通过标准:能说清每段耗时占比,并与 互动盈亏计算器的估算对比,解释差距来源。
3.5 端到端服务
目的:在相同负载下比较“不开缓存 / 只开本地缓存 / 开远端缓存”三种配置。可用引擎自带的压测工具(例如 vllm bench serve)。
记录:TTFT、ITL 的分布,吞吐,goodput,实际少算的 token 数,远端读取字节数。通过标准:结论注明负载(前缀共享率、长度分布、到达率),并包含至少一个缓存“不划算”的对照场景。
4. 故障演练
每个演练都按“预期 → 注入 → 观察 → 恢复 → 结论”记录。重点不是证明系统不出错,而是确认出错时不产生错误输出、资源最终能回收、服务能按预期降级。
| 演练 | 怎么注入(测试环境) | 预期行为 | 要观察的指标 |
|---|---|---|---|
| 存储节点退出 | 停止一个贡献 segment 的进程 | 相关读取失败并回到重算;有副本时改读其他副本;不出现错误输出 | miss 原因、重算速率、TTFT 变化、错误输出数(应为 0) |
| 目录主节点切换 | 停止主节点,观察 HA 切换 | 切换期间查询按 miss 处理;恢复后旧位置失效;服务限流预热 | 切换耗时、恢复期 SLO、命中率回升曲线 |
| 网络变慢 | 在测试机上注入延迟或限速 | 加载超时后隔离目标并重算;超时任务最终完成或取消 | 超时数、隔离中的 buffer 数、内存是否回落 |
| 租约过期 | 在读取路径人为增加排队延迟,超过租约时长 | 完成后检测到过期并拒绝数据 | 过期拒绝次数、是否有数据被误用 |
| 请求取消 | 加载进行中取消请求 | 不立即复用目标页;底层完成后再释放 | 迟到回调是否被丢弃、显存与 host 内存是否泄漏 |
| 缓存全冷 | 清空测试缓存后施加正常负载 | GPU 承担全部 Prefill;按优先级准入或限流 | GPU 队列、拒绝率、恢复到基线的时间 |
| 格式不匹配 | 用不同 KV dtype 或模型版本的实例读取同一命名空间 | 因身份或格式不同而不命中,或校验失败后拒绝 | 错误输出数(应为 0) |
正确性检查方法:固定随机种子和贪心解码,比较开缓存与关缓存时的输出;或比较 logits 差异在数值误差范围内。任何一次输出不一致都要当作严重问题追查。
5. 结果记录模板
实验名称:
日期 / 执行人:
环境:(引用第 1 节表格)
假设:例如“远端加载 8K 前缀比重算快 50% 以上”
配置:负载、并发、缓存配置、对照组
结果:原始数据位置、关键数字(含 P50/P99)
与估算的差距:差多少,原因是什么
结论:假设成立 / 不成立 / 有条件成立
未验证项与后续:
把三份以上这样的记录整理进面试材料,就能回答“你怎么证明”的追问。