跳转至
推理系统手册 求职 下载 notebook

系统设计题

"设计一个大模型推理服务"是推理岗、特别是偏平台方向岗位的常见大题。它没有标准答案,考察的是能否把需求翻译成数字、把数字翻译成配置、把配置组织成一个可运维的系统,并说清每个取舍。这一章给出一个答题框架,用一道完整的例题(含容量估算的代码)演示如何作答,最后列出其他常见题目的要点。

答题框架

  1. 澄清需求(先问再答):模型与规模、流量(峰值 QPS、提示词/输出长度分布、是否共享前缀)、SLO(P99 TTFT、P99 TPOT/ITL)、可用性、成本约束、功能(流式、工具调用、多模态、LoRA);
  2. 容量估算:需要多少 token/s 的 prefill 与 decode 能力 → 单实例在 SLO 下能承担多少 → 需要多少实例、多少张卡;
  3. 单实例设计:推理引擎、并行方式、量化、KV 管理、调度参数;
  4. 多实例架构:网关、路由(负载感知、缓存感知)、PD 分离、KV 缓存层、弹性伸缩;
  5. 可靠性与运维:健康检查、故障转移、限流与降级、模型发布、冷启动;
  6. 监控与成本:关键指标、每百万 token 的成本、优化方向。

面试时间有限,每一步给出决策 + 理由 + 数字,比面面俱到更重要。

例题:一个 70B 模型的对话服务

为一个对话产品设计推理服务:模型为 70B 稠密模型(LLaMA-3-70B 规模),峰值 200 QPS,平均提示词 2000 token(含系统提示词与多轮历史)、平均输出 500 token,要求 P99 TTFT < 2 s、P99 TPOT < 50 ms,可用性 99.9%。

1. 澄清与假设

先确认几件会改变设计的事:多轮对话的历史是否在服务端保存(决定前缀缓存的命中率)、有没有超长上下文请求(决定是否需要单独的资源池)、流量的日内波动(决定弹性策略)、是否允许量化(决定硬件需求)。这里假设:允许 FP8 量化,前缀共享率中等,偶有 32K 以上的长请求。

2. 容量估算

需求:prefill 每秒 200 × 2000 = 40 万 token,decode 每秒 200 × 500 = 10 万 token。

单实例:70B 的 FP8 权重约 70 GB,选 8 张 H100 做 TP=8(机内 NVLink),FP8 KV Cache 每 token 160 KB。用压测一章的模拟器估算这样一个实例在 SLO 下能承担的请求速率(8 张卡的带宽和算力合计,MFU 按 40% 计,每步固定开销 1.5 ms 用来近似 TP 通信与调度开销):

import math
from sim import Setup, simulate, summarize

kv_per_token = 2 * 80 * 8 * 128 * 1                                  # FP8 KV
instance = Setup(params=70.6e9, weight_bytes=70.6e9, kv_bytes_per_token=kv_per_token,
                 kv_capacity_tokens=int((8 * 80e9 * 0.9 - 70.6e9) / kv_per_token),
                 peak_flops=8 * 1979e12, bandwidth=8 * 3.35e12, mfu=0.4, step_overhead=1.5e-3,
                 max_num_batched_tokens=8192, max_num_seqs=512)
low = summarize(simulate(instance, 1, 300, 2000, 500), 2.0, 0.05)
print(f"低负载:TTFT {low['ttft_p50'] * 1e3:.0f} ms,TPOT {low['tpot_p50'] * 1e3:.1f} ms")

lo, hi = 1.0, 60.0
for _ in range(12):
    mid = (lo + hi) / 2
    ok = summarize(simulate(instance, mid, 1500, 2000, 500), 2.0, 0.05)["slo_ok"] >= 0.99
    lo, hi = (mid, hi) if ok else (lo, mid)
instances = math.ceil(200 / lo * 1.3)                                  # 留 30% 余量应对突发与故障
print(f"单实例满足 SLO 的最大速率约 {lo:.1f} req/s;200 QPS 需要 {math.ceil(200 / lo)} 个实例,"
      f"加 30% 余量为 {instances} 个({instances * 8} 张 H100)")

gpu_hour_cost = 2.5                                                    # 假设每张 H100 每小时 2.5 美元
output_tokens_per_hour = 200 * 500 * 3600
print(f"按每卡每小时 ${gpu_hour_cost} 计:每小时 ${instances * 8 * gpu_hour_cost:.0f},"
      f"约合每百万输出 token ${instances * 8 * gpu_hour_cost / output_tokens_per_hour * 1e6:.2f}")
低负载:TTFT 48 ms,TPOT 4.3 ms
单实例满足 SLO 的最大速率约 16.8 req/s;200 QPS 需要 12 个实例,加 30% 余量为 16 个(128 张 H100)
按每卡每小时 $2.5 计:每小时 $320,约合每百万输出 token $0.89

面试中不需要跑模拟器,但要能说出同样的推理:低负载时 TPOT 约 4 ms(读 70 GB 权重 / 26.8 TB/s),瓶颈在高负载时 prefill 与 decode 争抢算力;单实例约 15~20 req/s;200 QPS 需要十几个实例、一百多张卡;成本在每百万 token 一美元的量级。之后一定要说明:这是估算,上线前用真实负载压测校准。

3. 单实例设计

决策 选择 理由
引擎 vLLM 或 SGLang 成熟、支持下面所有特性;按团队熟悉度选
并行 TP=8(机内) 70B FP8 单卡放不下;TP 降低单请求延迟,TPOT 满足 50 ms
量化 FP8 权重 + FP8 KV 容量约翻倍(量化部署);上线前做精度评测
调度 分块 prefill,预算约 4K~8K 平衡 TTFT 与 ITL;按压测调整
缓存 前缀缓存 + CPU 分层缓存 系统提示词与多轮历史复用,降低 TTFT 与计算量
CUDA Graph 开启 decode 的 CPU 开销
投机解码 视负载开启(EAGLE) 低峰期降低延迟;高峰期 batch 大时收益消失甚至变负,可动态关闭

4. 多实例架构

客户端 → API 网关(鉴权、限流、配额、计费)
           │
           ▼
        路由器 ────── 实例状态(队列长度、KV 使用率、缓存的前缀)
     (负载感知 + 缓存感知 + 会话亲和)
      ┌────┼─────────────┬──────────────┐
      ▼    ▼             ▼              ▼
   实例 1 … 实例 N      长上下文专用池   (可选)PD 分离:prefill 池 + decode 池
      │                                  │
      └──────── 分布式 KV 缓存(Mooncake / LMCache)───┘
           监控:TTFT/TPOT/ITL 分位数、goodput、队列、KV 使用率、抢占、命中率

要点:

  • 路由:同一会话的请求尽量发往同一实例(会话亲和),共享前缀的请求发往已经缓存它的实例(缓存感知),同时避免热点(负载感知),三者要加权平衡;
  • 长上下文隔离:32K 以上的请求放到单独的资源池(更大的 TP 或上下文并行),避免它们拖慢普通请求的 TTFT 与 ITL;
  • PD 分离:在这个规模(十几个实例)可以考虑,prefill 与 decode 分别扩缩容,消除干扰;但会增加复杂度和 KV 传输,建议先用分块 prefill 上线,确认 TPOT 抖动是主要问题后再引入;
  • 弹性伸缩:按排队长度、KV 使用率、TTFT 趋势扩缩容,而不是 GPU 利用率(decode 时 GPU 利用率天然不高);70B 模型的冷启动(加载权重、编译、录制 CUDA Graph)要几分钟,需要保留热备实例,并优化权重加载(本地 NVMe 缓存、并行加载)。

5. 可靠性

  • 健康检查不能只看进程存活,要发真实的探测请求;
  • 实例故障时,正在进行的流式请求会中断:客户端重试,或在网关层用已生成的内容作为前缀续写;
  • 限流与降级:超出容量时优先保证付费用户与交互式请求(优先级调度),离线任务降级到低峰执行;
  • 模型发布:灰度发布、按比例切流量,对比新旧版本的质量与延迟指标,保留快速回滚。

6. 监控与持续优化

盯住这几个指标:TTFT/TPOT/ITL 的 P50 与 P99、goodput、排队长度、KV 使用率、抢占次数、前缀缓存命中率、每百万 token 成本。优化方向按收益排序:提高前缀缓存命中率 → 量化 → 调度参数 → PD 分离 → 投机解码(低峰期)→ 升级硬件。

其他常见题目

冲刺计划里的 10 道系统设计题都有完整的参考答案(含可运行的容量估算):见参考答案(一)与参考答案(二)。

题目 关键点
离线批量推理(每天处理 10 亿 token 的数据标注) 不关心延迟,只关心吞吐与成本:最大 batch、关闭投机解码、按长度分桶、用最便宜的硬件与最激进的量化;利用前缀共享(相同指令);断点续跑与重试
RL rollout 服务 长尾(partial rollout、异步)、与训练共置时的显存切换与权重同步、返回采样 logprobs、batch 不变性(见 RL 训练中的推理)
多租户 LoRA 平台(上千个微调版本) 共享基座、多 LoRA 批量计算(分段 GEMM)、LoRA 权重的分层缓存与按需加载、按租户路由以提高 LoRA 命中、配额隔离
Agent 平台 多轮、上下文不断增长、工具调用:前缀缓存与会话亲和至关重要,KV 分层缓存(用户思考、工具执行期间 KV 可以卸载),结构化输出,长上下文隔离
RAG 服务(输入长、输出短) prefill 为主:FP8、前缀缓存(公共文档放在提示词前部)、分块 prefill、prefill 实例多于 decode 实例;投机解码收益小
超大 MoE(DeepSeek 规模)部署 DP Attention + EP、DeepEP、EPLB、两批重叠、PD 分离且两侧 EP 规模不同(见专家并行)
多模态服务 分辨率限制、encoder cache、EPD 分离、预处理的 CPU 瓶颈(见多模态推理)

怎么讲清楚

系统设计题最常见的失分点是没有数字和没有取舍。"用 vLLM 部署、加 PD 分离、加缓存"谁都会说;能说出"单实例约 17 req/s,所以需要 16 个实例;PD 分离先不上,因为分块 prefill 在压测中已经满足 ITL,引入它会增加 KV 传输和运维复杂度",才是面试官想听的。另一个加分点是主动讨论如何验证:每个估算最后都要落到压测上。

小结

  • 框架:澄清需求 → 容量估算 → 单实例 → 多实例 → 可靠性 → 监控与成本。
  • 容量估算:需要的 prefill/decode token 速率,除以单实例在 SLO 下的能力,加冗余;单实例能力用屋顶线或模拟器估算,再用压测校准。
  • 多实例的关键是路由(会话亲和、缓存感知、负载感知)、长请求隔离、按排队而非 GPU 利用率伸缩、热备应对冷启动。
  • 每个决策都要给理由和数字,并说明取舍与验证方式。

本章练习题(1 道)

在浏览器里直接写代码、跑测试;也可以在本地用 python practice/judge.py 判题。

发现错误或有更好的写法?在 GitHub 上编辑此页提交问题