假设你是一家餐厅的老板,后厨只有一位主厨——GPU。客人们不停地下单,每桌菜的等待时间就是推理延迟。你最该盯的不是平均上菜速度,而是最倒霉的那 1% 客人等了多久:这就是 P99 延迟。菜为什么慢?要拆开看备菜(预处理)、炒菜(推理)、装盘(后处理)各花了多久;主厨是不是真的在忙,要看 GPU 利用率;要不要几桌菜一锅炒,则是动态批处理的取舍。这篇文章就用这套餐厅经营逻辑,带你搭建实时推理性能监控体系:先学会延迟分解与 P99 计算,再掌握 GPU 利用率监控与动态批处理,最后谈延迟与吞吐的权衡,并用 SLO 看板和告警守住系统底线。读完你就能像老练的餐厅经理一样,一眼看出瓶颈在哪、该动哪个旋钮。

岗位关联

掌握 P99 与 GPU 利用率监控,是 AI 推理工程师调优线上服务的核心基本功,直接决定你能否守住 SLO、省下 GPU 成本。

目录
  1. 可视化演示
  2. 延迟分解定位
  3. P99延迟指标
  4. GPU利用率监控
  5. 动态批处理
  6. 延迟吞吐权衡
  7. 监控告警体系
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

展示AI推理服务的实时性能监控面板,包含P50/P95/P99延迟时序曲线与SLO阈值告警,以及多GPU节点利用率、显存、温度、功耗的多维仪表盘视图。

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

推理服务实时可观测性 P50: 中位延迟基线 P95: 尾部延迟监控 P99: SLO违约告警阈值100ms SLO可用性 = (1 - P99违约请求数 / 总请求数) × 100%

延迟分解定位

客人催菜时,有经验的老板不会急着换菜谱,而是先跑到后厨门口,看时间到底耗在哪个环节:备菜间人手不够?主厨颠勺太慢?还是装盘师傅在磨洋工?对推理服务来说,这也是排查延迟的第一板斧——延迟分解。

一个推理请求从进入服务到返回结果,同样要经过三道固定工序:预处理、推理、后处理。预处理做数据清洗、tokenize 或图像缩放,通常跑在 CPU;推理是模型在 GPU 上的核心计算;后处理做解码、排序和格式化返回。把三段分开计时,总延迟的构成就一目了然。

实现时在代码里对三个阶段分别打点,输出结构化日志或用 OpenTelemetry 生成 span,再聚合成瀑布图。线上定位时,先看哪一段占比最大——比如 P99 是 200ms,推理占了 150ms,那优化方向就该朝模型侧使劲,而不是去抠预处理的正则表达式。

就像给每道工序都装一个计时器,老板一眼就能看出是备菜间在排队还是主厨在摸鱼。延迟分解的价值,就是让优化不再靠猜。

Ltotal=Tpreprocess+Tinference+Tpostprocess\displaystyle L_{\text{total}} = T_{\text{preprocess}} + T_{\text{inference}} + T_{\text{postprocess}}
推理总延迟等于预处理、推理、后处理三段耗时的累加,公式的价值是让“时间去哪了”变得可计算、可对比。
  1. 预处理耗时指数据清洗、tokenize、图像缩放等 CPU 上的准备工作,相当于备菜切菜。
    TpreprocessT_{\text{preprocess}}
  2. 推理耗时指模型在 GPU 上的前向计算,是整道菜下锅翻炒的核心环节。
    TinferenceT_{\text{inference}}
  3. 后处理耗时指输出解码、排序、格式化返回等收尾动作,相当于装盘上桌。
    TpostprocessT_{\text{postprocess}}
  4. 相加得总延迟三段工序串联执行,端到端延迟就是三者之和。
    Ltotal=Tpreprocess+Tinference+TpostprocessL_{\text{total}} = T_{\text{preprocess}} + T_{\text{inference}} + T_{\text{postprocess}}

P99延迟指标

把 100 桌客人的等菜时间从短到长排成一列,从最短数到第 99 位,那一桌的等待时间就是 P99。它的含义是:100 桌里 99 桌都能在这个时间内上齐菜。剩下那 1 桌,就是餐厅口碑的隐藏炸弹。

为什么不用平均值?因为一两桌等了两小时的极端情况会把平均拉高,掩盖大多数人的真实体验;而最大值又太“脆”,一次网络抖动就能飙到天上去。P99 既过滤了离群噪音,又忠实反映尾部 1% 的体验,是 SLO 里最常用的尾延迟指标。

计算方式不复杂:把采样窗口内(比如 5 分钟)所有请求延迟排序,取 99% 分位。窗口太小,P99 会剧烈抖动、没法告警;窗口太大,高峰期的恶化会被平均掉。所以工程上通常同时看 P50、P95、P99 三条曲线,互相印证。

餐厅经理最怕的不是平均上菜慢,而是周末最忙那 1% 的客人等出怨气——P99 就是这个“怨气指标”,它把模糊的尾部体验变成了一个可监控、可告警的数字。

P99=v⌈0.99×N⌉\displaystyle P_{99} = v_{\lceil 0.99 \times N \rceil}
P99 就是把 N 个延迟升序排列后,取第 99% 个位置上的延迟值;窗口大小决定统计的稳定性和敏感性。
  1. 升序排序把采样窗口内所有请求的延迟从小到大排列。
    v1≤v2≤⋯≤vNv_1 \leq v_2 \leq \cdots \leq v_N
  2. 计算位置用请求总数乘以 0.99,并向上取整得到目标下标。
    ⌈0.99×N⌉\lceil 0.99 \times N \rceil
  3. 取值取出该位置对应的延迟值,即为 P99。
    P99=v⌈0.99×N⌉P_{99} = v_{\lceil 0.99 \times N \rceil}

GPU利用率监控

主厨真的在忙吗?你在后厨装一个感应器,统计他每秒有多少时间在真的炒菜——这就是 GPU 利用率。如果感应器显示主厨只有 30% 时间在颠勺,那要么是没客人(低 QPS),要么是食材没备好(数据加载阻塞)。

nvidia-smi、DCGM 这类工具给出的 GPU 利用率,本质是采样周期内 GPU 执行计算任务的时间占比。除了算力利用率,还要盯显存占用:显存就像后厨的冰箱和货架,堆得太满会 OOM,请求直接失败;而显存占用高不代表算力用满了,两者是不同维度。

注意利用率高不等于吞吐好:可能是 kernel 空转、显存带宽瓶颈,或者请求排队导致 GPU 一直在“做无用功”。SM 占用率(occupancy)反映每个流式多处理器上并发线程块的饱满程度,配合利用率一起看,才能判断 GPU 是真的在高效产出,还是在瞎忙。

主厨满身大汗可能是厨房太挤、食材备不上。光看出汗量不够,还要看翻台率——GPU 利用率必须和吞吐、延迟放在一起看,才有意义。

U=TbusyTtotal×100%\displaystyle U = \frac{T_{\text{busy}}}{T_{\text{total}}} \times 100\%
GPU 利用率 = 采样窗口内真正执行计算的时间 ÷ 总采样时间。它回答“主厨有多少时间在炒菜”,而不是“炒出了多少桌菜”。
  1. 忙时采样窗口内 GPU 真正执行计算 kernel 的时间。
    TbusyT_{\text{busy}}
  2. 总时长本次采样的整个时间窗口长度。
    TtotalT_{\text{total}}
  3. 利用率忙时占总时长的百分比,反映 GPU 的忙碌程度。
    U=TbusyTtotal×100%U = \frac{T_{\text{busy}}}{T_{\text{total}}} \times 100\%

动态批处理

主厨炒菜有个讲究:一份一份炒,客人等得短,但主厨来回跑、锅一直空着;几份一锅炒,省火省力,但先到的客人要等后来的凑齐才一起下锅。动态批处理,就是让主厨自己决定“攒几份再开火”。

请求先进入队列等待。系统设两个阈值:攒够 max_batch 个请求立刻发车;或者第一个请求已经等了超过 max_wait,哪怕没攒满也发车。这两个参数一配合,就同时控制了最大批次和最长等待,防止请求在队列里被饿死。

batch 越大,GPU 越“省火”:模型计算里有固定开销(kernel 启动、显存搬运),被更多样本分摊后,单请求成本下降。但代价是队列等待变长,P99 上升。NVIDIA Triton、TorchServe 等推理服务都内置了这套 dynamic batching 参数,实际调优就是在 max_batch 和 max_wait 之间找平衡。

好的主厨会看排队长度动态调整“一锅炒几份”:高峰期多攒几份提效率,空闲期凑够一桌就开火。动态批处理做对时,GPU 利用率和吞吐双双上涨,P99 还能稳在原位。

Throughput=BTwait+Tcompute(B)\displaystyle \text{Throughput} = \frac{B}{T_{\text{wait}} + T_{\text{compute}}(B)}
吞吐 = 批次大小 ÷(排队等待 + 批次计算)。batch 增大让计算分摊更划算,但会让等待时间变长,这就是批处理的本质权衡。
  1. 批次大小一次凑齐并发送给 GPU 并行计算的请求数量,是调节吞吐的旋钮。
    BB
  2. 等待时间请求在队列里等待凑批的时间,batch 越大等待越久。
    TwaitT_{\text{wait}}
  3. 计算时间随 batch 增大而增长,但增速慢于 batch 本身,即亚线性增长。
    Tcompute(B)T_{\text{compute}}(B)
  4. 吞吐公式批次大小除以单请求的总耗时,得到每秒能完成的请求数。
    Throughput=BTwait+Tcompute(B)\text{Throughput} = \frac{B}{T_{\text{wait}} + T_{\text{compute}}(B)}

延迟吞吐权衡

batch 调到多大最好?这是一场延迟与吞吐之间的拔河比赛。调大 batch,GPU 利用率上升、吞吐增加,但请求排队时间变长,P99 被推高;调小 batch,P99 降下来了,主厨却开始频繁开火、锅温没上来又停了,GPU 大量闲置。

用压测画出权衡曲线,会发现一个典型形态:吞吐先随 batch 快速上升,之后边际收益递减、趋于饱和;而 P99 几乎随着 batch 线性上涨。两条曲线的“拐点”附近就是甜点区——再增大 batch,延迟暴涨但吞吐几乎不再增加。

工程上的做法是把曲线和业务目标叠在一起看:SLO 规定 P99 必须小于某个阈值(如 100ms),就在阈值线下方取最大的 batch;如果吞吐还不够,就得扩容或用更快的模型,而不是继续拧大 batch。这本质上是一道带约束的优化题。

就像餐厅既不想主厨闲着,也不想客人等到发火:甜点区就是“SLO 允许”和“GPU 吃饱”的交集。找到它,延迟和成本就能兼得。

min⁡B LP99(B)s.t.Throughput(B)≥Ttarget\displaystyle \min_{B} \ L_{P99}(B) \quad \text{s.t.} \quad \text{Throughput}(B) \geq T_{\text{target}}
目标是在吞吐不低于业务要求的约束下,选择让 P99 最小的 batch 大小。甜点区 = SLO 允许的最大 batch。
  1. 决策变量batch 大小是需要动态调节的核心旋钮。
    BB
  2. 目标函数最小化 P99 尾延迟,让最倒霉的 1% 请求也别等太久。
    min⁡B LP99(B)\min_{B} \ L_{P99}(B)
  3. 吞吐约束batch 不能无限调小,吞吐必须守住业务底线。
    Throughput(B)≥Ttarget\text{Throughput}(B) \geq T_{\text{target}}

监控告警体系

餐厅经理不用站在后厨,也能指挥全局——靠的是大堂里那块大屏:翻台率(吞吐)、等位时间(P99)、主厨负荷(GPU 利用率)实时跳动,任何一项越线就亮红灯。推理服务的监控大盘,就是这块大屏。

大盘上把前面所有指标汇总:QPS、P50/P95/P99 延迟、GPU 利用率与显存、batch 大小、队列长度。核心是 SLO 达标率:以 5 分钟为一个统计周期,每个周期检查 P99 是否 ≤ 阈值(如 100ms),达标的周期数占总周期数的比例就是 SLO。常见标准是“30 天 99.9% 的周期达标”。

告警要分级:P99 连续 3 个窗口超阈值触发 warning,失败率陡增或超时率飙升触发 critical。告警之后是根因分析,按固定顺序排查:先看延迟分解——哪一段涨了;再看 GPU 利用率——是计算变慢还是排队积压;再查 batch 配置和 QPS——是不是流量高峰导致 batch 被撑大。

有了这套看板,你就是那个不用进后厨也能指挥全局的餐厅经理:指标透明、告警自动、根因有路径。守住了 SLO,就守住了那最倒霉的 1% 客人的体验。

S=NokNtotal×100%\displaystyle S = \frac{N_{\text{ok}}}{N_{\text{total}}} \times 100\%
SLO 达标率 = 满足 P99 ≤ 阈值的周期数 ÷ 总周期数。它回答“系统在过去一段时间里,有多少时间在守约”。
  1. 统计周期以固定时间窗口(如 5 分钟)为单位,逐个计算 P99。
    Δt=5 min\Delta t = 5\ \text{min}
  2. 达标判定每个窗口内判断 P99 是否不超过 SLO 阈值。
    P99≤SLOP_{99} \leq \text{SLO}
  3. 达标率达标窗口数占总窗口数的比例,就是对外承诺的兑现率。
    S=NokNtotal×100%S = \frac{N_{\text{ok}}}{N_{\text{total}}} \times 100\%

AI 应用场景

大规模 LLM 推理服务调优

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

解决的问题:传统优化只盯平均延迟和吞吐,长尾请求导致 token 输出卡顿、用户体验差。利用延迟分解定位 prefill / decode / 后处理各段耗时,用 P99 监控保障输出流畅度,比盲目换模型更有效。

具体案例:某 RAG 问答服务高峰期 P99 从 800ms 飙到 3s。延迟分解发现检索结果的后处理(rerank 加拼接上下文)占了 60% 耗时,优化后处理逻辑并行化后,P99 降到 1.2s,GPU 利用率从 55% 升到 80%。

GPU 集群资源调度

阶段:资源调度与弹性伸缩

解决的问题:凭经验预留 GPU 副本,要么浪费资源要么高峰期不够用。通过持续监控 GPU 利用率与 P99 延迟,建立“利用率过高且 P99 超阈值 → 自动扩容”的联动策略。

具体案例:某推荐系统晚高峰 QPS 翻倍,监控发现 GPU 利用率冲到 95% 且 P99 越过 SLO 红线。自动扩容两个副本后,利用率回落到 70%,P99 重新达标,避免了人工半夜救火。

实时推理 SLO 保障

阶段:可观测性与在线告警

解决的问题:故障发现靠用户投诉,复盘靠拍脑袋。建立以 P99 为核心的 SLO 监控与分级告警体系,让每次性能劣化都能被及时捕获并快速定位根因。

具体案例:广告 CTR 预估服务设定“P99 ≤ 50ms、30 天 99.9% 达标”的 SLO。某次模型更新后告警触发,按“延迟分解 → GPU 利用率 → batch 配置”的顺序排查,定位到新版模型引入的冗余计算,回滚后 P99 恢复 40ms。

Python 演示

python63 行
import random def compute_percentile(sorted_values, p):    if not sorted_values:        return 0.0    idx = int(p * len(sorted_values)) - 1    idx = max(0, min(idx, len(sorted_values) - 1))    return sorted_values[idx] def compute_p99(latencies):    return compute_percentile(sorted(latencies), 0.99) class DynamicBatcher:    def __init__(self, max_batch=8, max_wait=0.02, base_compute=5.0):        self.max_batch = max_batch        self.max_wait = max_wait        self.base_compute = base_compute        self.queue = []        self.latencies = []        self.batch_count = 0     def enqueue(self, request_id, arrive_time):        self.queue.append((request_id, arrive_time))     def drain(self, now):        while self.queue:            batch = self.queue[:self.max_batch]            self.queue = self.queue[self.max_batch:]            compute_ms = self.base_compute + len(batch) * 0.4 + random.uniform(0, 2)            for _, arrive_time in batch:                self.latencies.append((now - arrive_time) + compute_ms)            self.batch_count += 1     def drain_if_ready(self, now):        if not self.queue:            return        first_arrive = self.queue[0][1]        if len(self.queue) >= self.max_batch or (now - first_arrive) >= self.max_wait:            self.drain(now) def simulate(use_batching, n_requests=2000, interval_ms=2.0):    batcher = DynamicBatcher()    now_ms = 0.0    if not use_batching:        for _ in range(n_requests):            now_ms += interval_ms            batcher.latencies.append(20 + random.uniform(0, 30))        avg = sum(batcher.latencies) / len(batcher.latencies)        return compute_p99(batcher.latencies), avg, 0    for i in range(n_requests):        now_ms += interval_ms        batcher.enqueue(i, now_ms)        batcher.drain_if_ready(now_ms)    batcher.drain(now_ms)    avg = sum(batcher.latencies) / len(batcher.latencies)    return compute_p99(batcher.latencies), avg, batcher.batch_count if __name__ == "__main__":    p99_single, avg_single, _ = simulate(use_batching=False)    p99_batch, avg_batch, n_batch = simulate(use_batching=True)    print("single serving   : P99 = %.1f ms, avg = %.1f ms" % (p99_single, avg_single))    print("dynamic batching : P99 = %.1f ms, avg = %.1f ms, batches = %d" % (p99_batch, avg_batch, n_batch))    print("P99 reduction    : %.1f%%" % ((1 - p99_batch / p99_single) * 100))
目的:用两个模拟实验对比“单样本服务”与“动态批处理”的 P99 延迟和 GPU 利用效果,跑完后直接打印 P99 降幅,验证动态批处理的价值。
  1. idx = int(p * len(sorted_values)) - 1先把延迟排序,再算出 99% 位置的下标,对应思维导图中的“排序取值”。
  2. self.queue.append((request_id, arrive_time))请求先入队等待凑批,对应“队列等待”。
  3. if len(self.queue) >= self.max_batch or (now - first_arrive) >= self.max_wait:攒够 8 个或首请求等满 20ms 就发车,对应“超时机制”与“批次大小”。
  4. compute_ms = self.base_compute + len(batch) * 0.4 + random.uniform(0, 2)batch 越大计算时间增长越慢,模拟摊薄固定开销。
  5. batcher.drain(now_ms)模拟结束时清空剩余队列,保证每个请求都被统计到。
  6. print("dynamic batching : P99 = %.1f ms, ...")输出对比结果,直观看到批处理对 P99 的改善。

常见误区

误区“P99 不就是所有请求里最慢的那个吗?把 100 个请求排序,第 99 个和第 100 个差不多,所以 P99 约等于最大延迟。”

正解P99 是排序后位于 99% 位置的延迟,最大值对应的是 P100。一次网络抖动可以让最大值瞬间飙到几秒,但 P99 依然稳定。就像 100 桌客人里最倒霉那桌可能因为打翻盘子等了两小时,但第 99 桌的等待时间才是大多数尾部客人的真实体验。用 P99 做 SLO,就是不被极端离群点绑架。

为何常见:“99%“听起来像”几乎最大“,加上对”尾部延迟”这个词的望文生义,很容易把 P99 和最大值混为一谈。

误区“GPU 都跑满 100% 了,说明算力一点没浪费,系统状态肯定最佳。”

正解利用率高可能是真的在高效计算,也可能是在空转等待、反复搬运数据、执行低效 kernel。真正要看的指标组合是“高利用率 + P99 不涨 + 吞吐上升”,三者同时满足才是健康状态。主厨满身大汗可能在擦灶台而不是炒菜,要看他端出了多少桌菜。

为何常见:利用率是最容易看到的指标,直觉上“忙碌 = 高效”,忽略了 GPU 也可能在无效忙碌。

误区“一锅能炒 100 份饭,那 batch 直接拉到 128,吞吐不就翻好几倍了?”

正解batch 增大后,计算时间的增长是亚线性的,但请求排队等待时间是线性增长的。超过拐点后,P99 暴涨、吞吐几乎不再增加,还可能因为显存不够直接 OOM。一锅饭太多锅铲翻不动、火候不均,客人也不愿意等。正确做法是压测出权衡曲线,在 SLO 允许的 P99 阈值下选最大 batch。

为何常见:只看到“摊薄固定开销”的收益,忽略了排队延迟这个隐形成本,是初学者最容易踩的坑。

误区“线上平均延迟才 50ms,用户肯定觉得很快,不用做性能优化了。”

正解平均延迟会掩盖长尾。如果 P99 是 500ms,意味着 1% 的请求慢 10 倍——在高并发场景下,这 1% 可能就是每天几十万次糟糕体验。餐厅平均上菜 20 分钟,但周末有 1% 客人等了两小时,口碑照样崩。所以监控必须看延迟分布(P50/P95/P99),而不是只盯平均值。

为何常见:平均值计算简单、直觉友好,且大部分监控系统默认展示平均值,容易让人产生“系统还不错”的错觉。

误区“把 P99 的统计窗口设成 1 秒,这样任何延迟抖动都能立刻发现。”

正解窗口太短,样本量太少,P99 会剧烈抖动,产生大量假告警;窗口太长(如 1 天),高峰期的问题会被平均掉,等发现时已经晚了。一秒钟看一次主厨等于在眨眼,一天看一次等于慢放录像。工程上常用 5 分钟窗口做告警,1 小时/1 天窗口做趋势分析。

为何常见:直觉上“监控越频繁越灵敏”,没意识到统计窗口的粒度需要和样本量、业务抖动周期匹配。

面试题

Q1. 关于 P99 延迟,正确的理解是什么?

单选题 · easy

  • A.所有请求中最慢的一个的延迟
  • B.排序后位于 99% 位置的延迟值← 正确答案
  • C.全部请求延迟的平均值
  • D.最快 1% 请求的延迟均值

答案:B。P99 是把延迟升序排列后取 99% 位置的值,既不是最大值也不是平均值。最大值是离群噪音点,平均值会被长尾拉偏,只有 P99 稳定地代表尾部体验。

Q2. 推理服务总延迟通常由哪三部分构成?

单选题 · easy

  • A.预处理、推理、后处理← 正确答案
  • B.网络、存储、数据库
  • C.编译、加载、调度
  • D.排队、响应、重试

答案:A。一个推理请求经历预处理(数据清洗/tokenize)、推理(模型前向计算)、后处理(解码/排序/返回)三段。像备菜、炒菜、装盘一样,三段串联相加就是端到端延迟。

Q3. 动态批处理中 max_wait 参数的主要作用是什么?

单选题 · mid

  • A.限制单个 batch 的最大计算耗时
  • B.控制请求在队列中最多等待多久← 正确答案
  • C.限制 GPU 的最大利用率
  • D.控制推理请求的最大延迟

答案:B。max_wait 是兜底超时:即使请求没攒满 max_batch,第一个请求等够 max_wait 也必须发车,防止请求被饿死。它直接约束了排队等待时间,从而影响 P99 上界。

Q4. GPU 利用率从 60% 升到 95%,P99 延迟却明显上升,最可能的原因是什么?

单选题 · mid

  • A.GPU 算力突然下降
  • B.请求排队等待拉长延迟← 正确答案
  • C.模型精度参数被改动
  • D.显存容量自动缩小

答案:B。利用率升高往往是因为请求积压、batch 被撑大,GPU 确实在满负荷工作,但单个请求在队列里等待的时间变长,P99 随之恶化。这正是延迟与吞吐权衡的典型表现。

Q5. 最擅长反映系统长尾问题的指标是哪一个?

单选题 · mid

  • A.P50 中位数延迟
  • B.平均延迟水平
  • C.P99 尾延迟值← 正确答案
  • D.请求的最大延迟

答案:C。P50 和平均延迟只代表“一般情况”,最大值会被单次离群噪音污染。P99 既过滤了极端偶发,又忠实覆盖尾部 1% 的真实体验,是 SLO 监控的首选指标。

Q6. 延迟分解显示预处理 20ms、推理 150ms、后处理 30ms,想显著降低 P99 应优先优化哪部分?

单选题 · hard

  • A.预处理,因为它是第一个环节
  • B.推理,因为它占总延迟 75%← 正确答案
  • C.后处理,因为改动最简单
  • D.三者平均分配精力优化

答案:B。P99=200ms 中推理占 150ms、占比 75%,是最大瓶颈。按占比定位优化方向,推理的收益空间最大;预处理和后处理占比小,优化到极致也难显著改善 P99。

Q7. 动态批处理中单请求最长等待 40ms,当前 batch 计算耗时 12ms,该请求总延迟最坏约为多少?

单选题 · extreme

  • A.40ms
  • B.52ms← 正确答案
  • C.12ms
  • D.28ms

答案:B。总延迟 = 排队等待 + batch 计算 = 40 + 12 = 52ms。max_wait 控制的是排队上界,计算耗时还要另加,两者之和才是端到端延迟的最坏情况。

Q8. 下列哪些手段可以降低 P99 延迟?

多选题 · mid

  • A.减小批处理的最大等待时间← 正确答案
  • B.增加 batch 的最大尺寸
  • C.优化预处理中的 CPU 瓶颈← 正确答案
  • D.延长采样统计窗口

答案:AC。减小 max_wait 让请求更快发车,缩短排队时间;优化 CPU 瓶颈直接降低预处理耗时。增大 batch 会拉长等待反而推高 P99,延长采样窗口只是改变统计口径,不改变真实延迟。

Q9. 构建推理监控告警体系时,正确的做法有哪些?

多选题 · mid

  • A.以 P99 作为核心尾延迟指标← 正确答案
  • B.仅监控平均延迟这一指标
  • C.设置阈值并配套根因分析← 正确答案
  • D.忽略延迟只看 GPU 利用率

答案:AC。SLO 场景必须用 P99 这类尾延迟指标,告警触发后要有根因分析流程(延迟分解→利用率→batch 配置→QPS)。只盯平均值或只看利用率,都会错过真正的性能劣化。

Q10. P99 与最大延迟的关系是什么?

单选题 · easy

  • A.两者一定始终保持相等
  • B.P99 通常小于等于最大延迟← 正确答案
  • C.P99 通常大于最大延迟
  • D.两者没有任何必然关系

答案:B。P99 取自排序后 99% 位置,最大延迟排在序列末尾,所以 P99 必然 ≤ 最大延迟。极端情况下若所有请求延迟相同才可能相等,但通常 P99 会明显低于最大值。