六、性能指标与评估
📊 TTFT、TPOT、吞吐量分别代表什么?如何测量?¶
在LLM推理服务中,这三个指标是衡量用户体验和系统效率的核心。
⏱ TTFT (Time-To-First-Token) —— 首Token延迟¶
定义:从客户端发送请求到收到第一个生成的token(或第一个有意义的字符)所经历的时间。它反映了模型理解用户输入并开始响应的“反应速度”。
测量范围:严格来说,TTFT包含:
-
客户端到服务器的网络往返时间(RTT)
-
请求排队时间(如果在高负载时)
-
Prompt的Prefill时间(模型处理输入、生成KV Cache的时间)
-
生成第一个token的Decode时间
-
服务器到客户端的首个token传输时间
在服务端日志中,通常记录从接收到请求到输出第一个token的耗时,但为了端到端评估,应包含网络时间。
测量方法:客户端记录发送请求的时间戳T_send,当接收到第一个data:事件(SSE)或第一个token时,记录时间戳T_first,则TTFT = T_first - T_send。服务端也可记录,但可能不含网络传输和排队。
⏳ TPOT (Time Per Output Token) —— 每Token生成延迟¶
定义:在生成阶段,每输出一个token所需的时间间隔。通常不包括首个token,因为Prefill和Decode的特性不同。更精确地说,TPOT衡量的是Decode阶段每个step的耗时,它是流式输出“平滑度”的关键指标。
测量方法:对于每个生成的token,记录其输出的时间戳,计算相邻token的时间差。通常以平均值、中位数、P95/P99等统计量来汇报。如果生成过程没有流式,也可用总生成时间除以生成token数得到平均TPOT,但这掩盖了波动。
🚀 吞吐量 (Throughput)¶
定义:推理服务在单位时间内处理的请求数量或生成的token总数。有两个维度:
-
Requests per second (RPS):每秒完成的请求数。适用于请求长度相近的场景。
-
Tokens per second (TPS):每秒生成的token总数。这是更通用的指标,因为它不受请求长度影响。
测量方法:在一段固定时间内,统计服务端成功处理的请求总token数(prompt+completion),除以时间。通常使用压测工具(如JMeter、Locust、自研脚本)并发发送请求,逐步增加并发,记录系统达到饱和前的最大TPS。
📈 三者关系¶
-
对于单个请求,总延迟 = TTFT + (生成token数 - 1) × 平均TPOT(假设首个token后的每个token间隔相近)。
-
对于服务整体,高吞吐往往意味着较大的批处理,这可能会增加单个请求的TTFT(因为排队和批次等待),但可以降低平均TPOT(因为GPU算力更充分利用)。因此,在SLA约束下最大化吞吐是服务优化的目标。
💡 测量工具:常用 vLLM 的基准测试脚本、TensorRT-LLM 的 gptManagerBenchmark、或 Hugging Face TGI 的 benchmark。结合 Prometheus + Grafana 进行实时监控。
🧭 为什么 TPOT 的 P50 和 P99 都重要?¶
TPOT 的分布往往不是正态的,存在长尾延迟。P50(中位数)和P99(99百分位)分别揭示了系统性能的不同侧面。
-
P50 TPOT:代表“典型”用户的生成速度。如果中位数很低,大多数用户会感觉回复流畅。优化P50通常涉及提升GPU算力、优化矩阵乘法、使用更高效的解码策略等。
-
P99 TPOT:代表“最差情况下”的表现,直接关系到用户是否会遇到明显卡顿。P99高意味着少数请求的生成延迟很大,可能由于资源竞争、垃圾回收、显存碎片、或长序列导致的带宽瓶颈。这对于高端用户或付费用户的体验至关重要。
📉 长尾延迟的危害¶
在流式输出中,即使中位数TPOT只有50ms,但如果P99达到500ms,用户会每隔一小段时间就感受到明显的停顿。这比整体慢更让人恼火,因为它是间断的。因此,SLA通常必须同时限制P50和P99,例如“P50<100ms 且 P99<300ms”。
⚙️ 优化P99的策略¶
-
资源隔离:使用MIG或时分复用避免干扰。
-
调度优化:确保decode请求不被prefill长时间阻塞(分段prefill)。
-
显存管理:防止KV Cache碎片导致的分配停顿。
-
GC调优:避免CUDA或Python GC引发的突发暂停。
💡 实际案例:我们曾遇到TPOT P50约60ms,但P99超过2秒。排查发现是vLLM的block manager在显存紧张时触发了大量的碎片整理,导致偶尔的decode step长时间阻塞。通过降低gpu_memory_utilization并增加节点解决了问题。
🔍 如何评估量化或剪枝后模型的推理质量?¶
模型压缩后,必须从多个维度评估其对最终应用的影响,而不仅仅是困惑度(PPL)。
📊 评估体系¶
- 标准基准测试
- 困惑度 (Perplexity):在通用文本数据集(如WikiText-2、C4)上评估,是最基础的指标。但注意,PPL的微小下降(如1-2%)可能对下游任务影响不大。
- 任务特定基准:根据模型用途选择。如MMLU(知识)、HellaSwag(常识推理)、GSM8K(数学)、HumanEval(代码)、TruthfulQA(诚实性)等。观察压缩后得分的绝对变化。
-
长文本能力:对于长上下文模型,需用特定基准(如LongBench)测试。
-
生成质量评估
- 人工评估:盲评是金标准。让人类标注员对压缩前后的模型输出进行成对比较,从有用性、流畅性、事实正确性等维度打分。
- 自动评估:使用AlpacaEval、MT-Bench等基于LLM裁判的评估,或用GPT-4作为评判。但需注意裁判模型自身偏好。
-
多样性测试:检查生成的文本是否变得重复、模板化。可用distinct n-grams指标。
-
特定维度测试
- 事实正确性:针对知识密集型任务,检查幻觉率。
- 安全对齐:用红队提示集测试压缩是否削弱了安全拒绝机制。
- 鲁棒性:测试对同义改写、对抗性输入的敏感性。
🛠️ 实际流程¶
-
快速筛查:先看PPL和几个关键基准(如MMLU、HellaSwag)的得分。如果下降超过3%,通常需调优量化参数。
-
A/B测试:在线部署压缩模型,与原始模型进行小流量A/B对比,观察用户点击率、满意度等真实业务指标。
-
回归测试:建立固定的评估集,每次压缩后自动运行,确保不引入新问题。
💡 注意:量化可能对某些模型组件更敏感,例如注意力层比FFN层更易受影响。因此,混合精度量化(如LLM.int8())往往效果更佳。
📈 推理时的延迟抖动通常由什么引起?如何缓解?¶
推理延迟抖动(Jitter)是系统不稳定的表现,通常源于资源竞争或调度策略不佳。
🔍 主要原因¶
-
批处理中的“计算气泡”:静态批处理中,短请求等长请求;动态批处理中,如果调度不当,prefill大请求可能会阻塞decode。
-
显存管理开销:PagedAttention的物理页分配、回收、碎片整理可能在特定时刻造成突发延迟。
-
GPU资源竞争:多个进程共享GPU,或与系统其他任务(如CUDA上下文切换)冲突。
-
网络与IO:如果模型权重未完全驻留显存(CPU offload),磁盘/网络IO会导致严重抖动。
-
垃圾回收 (GC):Python GC或CUDA内存释放可能在某些时刻暂停所有kernel执行。
-
硬件与功耗管理:GPU频率动态调整、过热降频也会引起性能抖动。
🛠️ 缓解策略¶
-
调度优化:使用分段prefill,限制单次prefill的token数,避免decode饥饿。优先级确保decode不被阻塞。
-
显存池化与预分配:设置合理的
gpu_memory_utilization,预留缓冲区。使用vLLM的block manager v2版本。 -
资源隔离:为推理服务绑定CPU核心,避免其他进程干扰;使用Docker限制资源;对延时敏感服务,使用独立GPU或MIG实例。
-
GC调优:调整Python GC阈值,或周期性手动触发GC以避免突发;使用
torch.cuda.empty_cache()谨慎。 -
锁频:通过
nvidia-smi -lgc锁定GPU频率,消除动态调频带来的抖动。 -
模型常驻:确保模型完全在显存,关闭CPU offload。使用
--mlock或 vLLM的swap_space配置。 -
监控与定位:使用Nsight Systems抓取抖动时刻的Timeline,精确找到延迟峰值的kernel或操作。
💡 案例:某服务每10秒出现一次TPOT峰值,经Nsight定位是vLLM的block manager每10秒尝试整理碎片,通过减小max_num_seqs和增加gpu_memory_utilization预留空间后解决。
🌊 面向用户的产品中,流式输出的流畅度如何量化评估?¶
流式输出的流畅度直接影响用户对“AI智能”的感知。除了TPOT外,还需从客户端体验角度定义指标。
📊 流畅度指标¶
-
Token间间隔的变异系数 (CV)
标准差 / 平均值,反映间隔的均匀程度。CV越小,输出越流畅。即使平均TPOT低,若CV很大,用户也会感觉忽快忽慢。 -
停顿频率与时长 统计TPOT超过某个阈值(如300ms)的次数和持续时间。一次长时间的停顿比多个微小停顿更让人烦躁。
-
首次渲染延迟 (Time to First Paint, TTP) 从用户发送请求到屏幕上首次出现文本的时间(近似TTFT,但包含前端渲染)。这是用户感知“开始回应”的瞬间。
-
单位字符刷新率 客户端每秒实际显示的字符数(受网络和前端渲染影响)。高刷新率给用户带来“思维敏捷”的体验。
🎯 评估方法¶
-
客户端日志:前端在接收到每个token时记录时间戳,计算间隔、卡顿次数等。
-
模拟测试:使用Playwright等浏览器自动化工具,注入不同网络条件,测量上述指标。
-
用户感知调查:通过众包平台,让真实用户观看流式输出录屏,给出1-5分的流畅度评分,与客观指标进行相关性分析。
💡 产品优化:如果流式输出有明显停顿,可以考虑前端实现“打字机效果”的平滑动画,掩盖微小的网络抖动;服务端则努力优化P99 TPOT。
⌛ TTFT(首 token 延迟)具体从哪个时刻算到哪个时刻?包含网络传输时间吗?¶
TTFT的起止点定义对优化目标至关重要。
-
起点:客户端发起请求的时刻(例如HTTP POST的发送时间)。这最符合用户体验。
-
终点:客户端接收到第一个有意义token的时刻(SSE流的第一个
data:事件被成功解析的时刻)。
📡 是否包含网络时间?¶
-
端到端TTFT:包含网络往返时间、服务端排队、prefill、首个decode以及网络传输。这是产品优化最关注的。
-
服务端TTFT:仅从服务端收到完整请求开始,到生成第一个token并写入socket缓冲区为止。不包含网络传输,是服务端自身的性能指标。
在实际优化中,两者都要测量。服务端TTFT可用于定位内部瓶颈,端到端TTFT则反映用户真实感受。如果两者差异很大,说明网络延迟或客户端处理有明显问题。
⏱️ TPOT(每 token 生成延迟)通常用什么统计指标来汇报?平均值、中位数、P99?¶
TPOT通常汇报以下统计量:
-
平均值 (Mean):反映整体性能水平,但易受极端值影响。
-
中位数 (P50):反映典型用户体验,比平均值更稳健。
-
P95、P99:用于衡量长尾延迟,是SLA的关键。P99表示99%的token间隔都低于该值。
-
最大值 (Max):用于发现最极端的卡顿。
📊 汇报方式¶
通常以表格呈现,例如:
同时,也会绘制TPOT随时间或序列位置的分布图,观察生成过程中是否有变慢的趋势。
📉 为什么 TPOT 在生成长序列时可能会出现波动?与 KV Cache 大小有关吗?¶
是的,TPOT与KV Cache大小有密切关系。
🔍 原因¶
-
KV Cache 增长导致显存带宽压力增大 在Decode阶段,每次生成一个token需要读取整个KV Cache。随着序列变长,KV Cache体积变大,从显存读取的时间线性增加。由于Decode是带宽受限的,这直接导致TPOT逐步升高。
-
PagedAttention的碎片与重分配 当KV Cache增长到需要新物理页时,可能触发物理页分配,若显存紧张,可能引起碎片整理或换页,导致突发高延迟。
-
上下文相关计算 某些注意力模式在长序列下可能变得稀疏或需要更复杂的计算(虽然FlashAttention优化了这一点),但总体影响不如带宽大。
📈 波动表现¶
通常,随着生成token数增加,TPOT会缓慢上升。在序列长度为4k左右可能出现一个较明显的阶梯,原因是GPU L2缓存或TLB miss增加。
💡 缓解:使用KV Cache量化(INT8)减小体积;采用GQA/MQA降低KV头数;限制最大生成长度。
⚡ 推理吞吐量通常用 tokens/s 还是 requests/s 来衡量?两者如何换算?¶
-
tokens/s(TPS):每秒生成的token总数,包括prompt token(prefill)和completion token(decode)。这是最通用、最准确的指标,因为它不受请求长度影响,直接反映GPU的利用率。
-
requests/s(RPS):每秒完成的请求数。当请求的prompt和生成长度相近时,RPS更直观。但若长短请求混合,RPS可能掩盖真实性能(例如大量短请求会让RPS虚高,但TPS不高)。
🔄 换算¶
假设平均每个请求的prompt长度为P,平均生成长度为G,则:

前提是prefill和decode共享同一吞吐量计算。实际服务中,prefill和decode的TPS能力不同(prefill计算瓶颈,decode带宽瓶颈),所以更精确是分开统计,但通常汇报总TPS。
💡 压测实践:固定请求长度和生成长度,用逐步增加的并发数测试,记录TPS和延迟曲线,找到吞吐饱和点。
🔧 如何设计一个端到端的压测方案,模拟真实用户的多轮对话和随机输入长度?¶
真实用户场景复杂,压测需模拟多样性。
📋 方案设计¶
- 数据集准备
- 从生产日志中采集大量真实的用户prompt,保留其长度分布和语言特征。
- 构建多轮对话模板:第一轮短文本,第二轮中长文本,第三轮可能带图像(多模态)。
-
将prompt按长度分桶(如0-128, 128-512, 512-2048...),按比例混合,再现真实分布。
-
压测工具与场景
- 使用 Locust 或 自定义 asyncio 脚本。Locust 支持Python编写复杂用户行为。
- 创建“用户任务”,每个任务包含多轮对话,随机决定每轮的 prompt 和
max_tokens。 - 模拟用户思考时间(Think Time),在每轮之间插入随机延迟(如2-10秒),更真实。
-
设置并发用户数阶梯上升,如从10逐步增加到200,每个阶段持续10分钟,观察系统稳定性。
-
需要监控的指标
- 客户端:TTFT、TPOT分布、请求成功率、错误类型(超时、拒绝)。
-
服务端:GPU利用率、显存占用、KV Cache命中率、调度队列长度、Prefill/Decode耗时。
-
随机输入长度的处理
- 压测脚本在每次发送请求前,从数据池中随机抽取一个prompt。
- 如果涉及多模态,随机加入不同分辨率的图片。
- 观察服务在长短混合下的延迟抖动和吞吐变化。
📈 结果分析¶
-
绘制吞吐量-延迟曲线,找到系统容量上限。
-
分析不同长度分段下的TTFT和TPOT,发现调度瓶颈。
-
对比多轮对话首轮与后续轮次的TTFT(后续轮次可能因前缀缓存而更快)。
💡 真实案例:某对话产品压测发现,在100并发时TPOT P99正常,但150并发时突然飙升,定位是显存碎片导致调度停顿,通过调整gpu_memory_utilization和启用分段prefill解决。
🚨 压测时发现 TPOT 的 P99 很高但平均值正常,可能是什么原因?¶
这种现象明确指示了长尾延迟,是系统存在偶发性瓶颈的典型信号。
🔍 可能原因¶
-
显存分配风暴:当大量请求并发且生成长度不一,PagedAttention 需要频繁分配物理页,或在显存紧张时进行碎片整理,导致某些decode步骤被长时间阻塞。
-
Prefill 抢占:大Prompt的prefill占用了大量计算资源,导致同时进行的decode请求被短暂阻塞(即使使用了分段prefill,仍可能因调度粒度产生毛刺)。
-
Python GC / CUDA 同步:PyTorch 或 vLLM 内部偶尔触发 CUDA 同步点,等待所有 GPU 操作完成,瞬间卡住所有请求。
-
操作系统调度:CPU 密集型任务(如数据预处理)与 GPU 内核竞争 CPU 时间,导致 GPU 内核提交延迟。
-
网络抖动:虽然不直接影响TPOT,但如果测量在客户端进行,网络延迟也会贡献P99。
🛠️ 排查与解决¶
-
Profiling:使用 Nsight Systems 抓取发生P99高峰时段的Timeline,找出是哪个 kernel 或 CUDA API 调用特别长。
-
服务日志:检查vLLM的调度日志,看是否频繁出现 “preempt” 或 “block table overflow”。
-
调整配置:降低
gpu_memory_utilization,留出更多空闲显存给物理页分配缓冲;减小max_num_batched_tokens限制prefill的规模。 -
拆分服务:将大prompt的请求路由到单独的计算池,避免干扰实时decode。
💡 案例:我们曾发现vLLM的block manager在物理页用完时,会触发一次全局的碎片整理,耗时数百毫秒,导致P99飙升。通过降低并发数和增加显存池大小解决了问题。
🔍 推理延迟的抖动通常由什么引起?如何通过调度策略或资源预留来缓解?¶
这个问题与第11题部分重叠,但更侧重于通用抖动而非仅P99。
抖动来源:
-
硬件:GPU降频、温度墙。
-
软件:GC、内存碎片、调度不公平。
-
负载:请求长度变化、并发波峰。
📏 调度策略缓解¶
-
分段 Prefill(Chunked Prefill):将大prefill拆分成多个小块,与decode交替执行,避免decode被长时间阻塞。
-
公平调度:使用类似于TGI的“水印”机制,限制单个请求的资源占用。优先保证decode请求的执行。
-
资源预留:为decode请求预留部分GPU算力(例如使用MPS限制prefill的占用上限),但这在共享GPU上较难实现。
📦 资源预留¶
-
独立资源池:将推理服务拆分为 prefill 池和 decode 池(Sarathi-Serve论文思路),prefill节点处理大计算,decode节点负责低延迟生成,通过高速网络传递KV Cache。这从根本上解决了互相干扰。
-
KV Cache 量化:减少显存压力,降低因换页导致的抖动。
-
超额预留显存:
gpu_memory_utilization设得比理论最大值低5-10%,用于吸收突发分配。
⚖️ 在对比两个推理框架的性能时,需要控制哪些变量才能做公平对比?¶
公平对比是评估框架优劣的基础,否则结论无意义。
📊 必须控制的变量¶
-
硬件环境:相同的GPU型号、驱动版本、CUDA版本、内存配置、网络拓扑。
-
模型与精度:完全相同的模型架构、权重(相同量化方法、相同量化参数)。使用相同的tokenizer。
-
输入数据:相同的测试数据集,相同的prompt分布,包括长度、内容。
-
请求负载:相同的并发数、请求速率、生成参数(如
max_tokens、temperature、top_p)。使用相同的批处理大小(如果框架允许设置)。 -
性能指标:预先统一定义TTFT、TPOT的测量起止点(是否含网络时间),以及吞吐量的计算方式(是否包含prompt token)。
-
框架配置:尽量对齐框架功能,如是否启用前缀缓存、KV Cache量化、PagedAttention等。如果功能不对齐,应在测试报告中声明差异。
-
预热:在正式测试前,均进行充分预热,排除模型加载和编译的影响。
-
多轮测试:每个配置重复多次,报告统计量,避免偶然因素。
📈 测试流程建议¶
-
在相同Docker容器中分别部署两个框架,保证软件环境一致。
-
使用同一压测脚本,避免客户端差异。
-
从低并发到高并发逐步测试,记录完整的延迟-吞吐曲线。
-
分析不同长度分段下的表现,看框架在特定场景是否有优势。
💡 常见陷阱:vLLM默认开启前缀缓存,如果对比的框架没有此功能,应在vLLM中关闭,或者注明。同样,TensorRT-LLM编译engine时的max_batch_size和max_seq_len设置会影响性能,应与对比框架的实际负载匹配。
量化后模型的质量如何全面评估?除了困惑度,还需要看哪些任务指标?¶
量化压缩模型体积后,必须确保其在真实业务中的表现不受明显影响。仅仅看困惑度(PPL)是远远不够的,因为它只能反映模型对下一个 token 预测的整体不确定性,而无法揭示量化对特定能力、安全性或生成多样性的微妙损害。
📊 需要评估的维度与指标
🔍 实践流程:
-
快速筛查:跑一遍 Open LLM Leaderboard 常用基准(HellaSwag, ARC, GSM8K, MMLU)和 Perplexity。若某项明显下降(>3%),则需要排查量化配置。
-
任务定制测试:根据业务场景构建专属测试集。例如客服模型要测情绪识别、槽位提取;代码模型要测特定 API 调用正确率。
-
人工评估:随机抽取 200 组 prompt,让量化模型和原模型分别生成,由人工进行成对比较(Win/Tie/Lose),检验量化后的生成质量是否显著下降。
-
A/B 上线:在生产环境部署量化模型,用小比例流量对比真实用户指标(满意度、完成率、点踩率),这是终极检验。
💡 案例:我们曾将对话模型从 FP16 量化到 INT4,PPL 仅上升 0.5,但人工评估发现生成的回复变得啰嗦且偶尔重复短语,这是因为量化使某些注意力头的分布变尖锐。最终通过 AWQ 替代 GPTQ 解决了该问题。
如何评估投机采样带来的实际加速比?在什么条件下加速效果最好?¶
投机采样并非总是有效,其加速效果严重依赖于草稿模型质量、主模型计算特征以及生成任务的属性。
📈 加速比的定义与测量
实际加速比 = 使用投机采样的平均每 token 延迟(或端到端延迟) / 不使用时的延迟。测量时需固定硬件、模型、输入 prompt 分布、生成的 max_tokens。
✅ 加速效果最好的条件:
-
草稿模型接受率高:当草稿模型与主模型输出分布高度相关(如同家族小模型),接受率可达 85%~95%。此时加速比接近每轮的猜测数。
-
主模型较大、计算密集:主模型越大,一次并行验证多个 token 相比于逐 token 生成的收益越大。例如 70B 模型加速比通常高于 7B。
-
生成任务确定性强:如代码补全、翻译、事实陈述等,草稿模型容易准确预测;而创意写作、开放式对话接受率较低。
-
batch size 较小:投机采样对单请求延迟改善最明显。大批量时 GPU 已经被计算填满,投机带来的加速有限,甚至因额外通信变慢。
-
草稿模型极轻量:如 Medusa 头、EAGLE 预测器,开销极小,几乎不影响主模型推理速度。
❌ 效果不佳的场景:
-
主模型较小(如 1B),一次前向本身很快,投机开销占比大。
-
高并发、大 batch 服务:此时吞吐主要由批处理效率决定,投机采样对吞吐提升有限。
-
草稿模型接受率低于 50%,猜测的 token 大部分被拒绝,浪费计算。
📊 评估指标:除了墙钟延迟和吞吐,还要关注每 token 的平均猜测次数、接受率分布(不同序列位置的接受率可能变化)。
💡 实践:我们曾用 TinyLlama-1.1B 作草稿为 LLaMA-7B 加速,代码生成场景加速 2.3x,但在多轮闲聊中仅 1.4x,因为用户输入不可预测。
评估生成质量时,使用 GPT-4 作为裁判比人工评估有什么优势和风险?¶
使用 GPT-4 等大模型作为自动化裁判(LLM-as-a-Judge)已成为主流,但它是一把双刃剑。
⚖️ 优势
-
成本极低:单次评判成本约 \(0.01~0.05,远低于人工标注(\)1~2),且可无限并发。
-
速度惊人:数分钟内完成数千条评判,加速迭代。
-
高一致性:同一 prompt 下的评判结果高度一致,不会因疲劳或主观偏好波动。
-
可覆盖长尾:对专业领域(如代码、高等数学),GPT-4 的评判能力可能超越普通标注员。
-
避免伦理问题:无需人工接触有害、暴力内容。
⚠️ 风险与偏差
-
位置偏见:偏好第一个出现的回答(Position Bias)。必须采用双向评判(交换顺序评判两次)来缓解。
-
长度偏见:倾向于给更长的回答更高分,即使内容冗余。需在评判 prompt 中明确禁止。
-
“回声室”效应:GPT-4 自身经过 RLHF,带有特定价值观和风格偏好,用它评判会将其偏见“遗传”给被评模型。
-
能力天花板:评判模型无法理解超越其自身能力的创新表达或复杂逻辑,可能误判佳作。
-
事实性核查无能:GPT-4 仍会犯事实错误,用它评判事实准确性并不可靠,需要外部知识源辅助。
🛡️ 缓解措施:
-
多裁判集成(GPT-4 + Claude + Gemini),取多数票。
-
关键或高风险样本人工复核。
-
定期与人工黄金标准对比,监控评判偏差。
💡 建议:GPT-4 裁判适用于大规模、快速、初步的筛选和对比;最终上线决策仍需辅助人工评估。
流式输出的“流畅度”如何量化?用户感知的卡顿和 token 生成间隔的关系是怎样的?¶
流式输出中,用户对“卡顿”的感知并非仅由平均生成速度决定,而是更受延迟的波动性和极端停顿影响。
📏 量化指标
-
Token 间隔的标准差/变异系数 (CV):间隔越不均匀,用户越感卡顿。
-
停顿频率与时长:设定阈值(如 200ms),统计超出阈值的停顿次数和最长停顿。一次超过 500ms 的停顿比 10 次 50ms 的更令人烦躁。
-
Token 生成间隔的分布(P50, P95, P99):P99 过高表示偶尔的大卡顿。
-
视觉流畅度评分:通过前端录屏,让人工或算法评估“卡顿感”。
🧠 用户感知机制:
人眼对匀速运动的物体接受度高。当 token 以均匀间隔出现时,即使整体速度较慢(如每 80ms 一个 token),用户也会觉得“流畅地在写”。但如果间隔忽快忽慢,即使平均速度更快,大脑也会将突然的停顿解读为“系统卡住了”,从而焦虑。
💡 优化方向:
-
服务端努力将 TPOT 的 P99 压制到用户感知阈值以下(通常 <200ms)。
-
前端可引入“打字机平滑动画”,对微小抖动进行缓冲,但人为增加延迟可能让灵敏用户反感。
推理服务的 SLA(服务等级协议)通常包含哪些指标?如何监控和告警?¶
生产环境推理服务必须有明确的 SLA,否则故障责任无法界定。
📋 核心 SLA 指标
-
可用性:服务正常响应时间占比,通常要求 99.9% 以上。
-
端到端延迟:P50/P99 TTFT、P50/P99 TPOT。例如“P99 TTFT < 1s, P99 TPOT < 150ms”。
-
吞吐量下限:保证的最低 tokens/s 或 requests/s。
-
错误率:非 200 状态码或超时比例,通常要求 <1%。
-
并发上限:承诺能稳定支持的最大并发请求数。
🚨 监控与告警
-
Prometheus + Grafana:服务端暴露指标(如 vLLM 的
/metrics),使用 Prometheus 抓取,Grafana 展示。 -
关键告警规则:
- TTFT P99 > 阈值持续 5 分钟 → 告警。
- 错误率 > 1% → 紧急告警。
- GPU 利用率 < 50% 但队列长度 > 10 → 可能死锁或卡住。
-
显存占用 > 95% 持续 10 分钟 → 可能存在内存泄漏。
-
客户端探针:定期从外部发送测试请求,测量端到端延迟,避免监控盲区。
💡 实践:我们定义了三级告警:Warning(P99 TTFT 超过 2s)、Critical(错误率 > 2%)、Emergency(可用性 < 99%),分别通知开发、SRE 和值班经理。
如何用可视化工具(如 Grafana)搭建一个推理服务的监控面板?核心面板有哪些?¶
一个好的 Grafana 面板能让团队在几秒内判断系统健康状况。
🖥️ 面板布局建议(全屏展示,一行一主题)
第一行:服务质量
-
实时 TTFT (P50, P99) 折线图
-
实时 TPOT (P50, P99) 折线图
-
请求成功率(%)和错误类型分布
第二行:吞吐与负载
-
每秒生成 token 数 (tokens/s) 和 请求数 (req/s)
-
活跃并发请求数
-
请求排队长度
第三行:资源利用率
-
GPU 利用率(%)和 显存占用 (GB)
-
CPU 利用率、内存占用
-
网络吞吐 (Gbps)
第四行:推理引擎内部
-
KV Cache 命中率
-
Prefill/Decode 延迟分布直方图
-
活跃序列数、preempted 请求数
📊 数据源:vLLM/TGI 等框架内置 Prometheus 指标端点,也可自定义日志解析 exporter。
🔧 技巧:使用 $instance 变量区分不同 GPU 或 Pod,设置 Time range 快速回看故障时段。
GPU 利用率和显存占用都很高,是否代表推理服务处于最佳状态?为什么不一定?¶
❌ 不一定。 GPU 高利用率可能隐藏着糟糕的调度和资源浪费。
🔍 场景分析
-
无效计算:模型正在为即将被丢弃的短请求重复计算大量 padding token(静态批处理),利用率高但有效吞吐低。
-
等待而非计算:利用率 100% 可能是因为大量时间花费在显存读写(带宽瓶颈),而非计算核心真正忙碌。此时如果增大 batch size,吞吐可能不会提升。
-
计算碎片:Prefill 和 Decode 混杂调度时,可能因为长 Prefill 阻塞所有 decode,decode 算力空转但利用率显示被 Prefill 占满。
-
资源竞争:多租户下,一个租户占用所有显存,其他租户排队,整体吞吐低下。
📈 真正的最佳状态:
-
GPU 利用率 > 90%,同时 TPOT P99 依然较低且稳定。
-
显存占用高,但 KV Cache 命中率也高,没有频繁的 OOM 或碎片整理。
-
吞吐量达到平台的理论上限附近,且延迟满足 SLA。
💡 经验:如果 GPU 利用率 99% 但 TPOT 很高,通常是带宽瓶颈;如果利用率波动大,可能是调度问题。需要结合延迟和吞吐综合判断。
长文本推理的延迟和短文本相比,瓶颈主要在 prefill 还是 decode?为什么?¶
📊 长文本推理瓶颈转变:
-
短文本(<1024 tokens):瓶颈在 Prefill。因为模型需要一次性处理整个 prompt,大量矩阵乘法并行计算,此时 GPU 计算核心是瓶颈。
-
长文本(>4096 tokens):瓶颈转移到 Decode。因为 KV Cache 变得极其巨大(数十 GB),每次 decode 都要从显存读出整个 KV Cache 和模型权重,显存带宽成为绝对瓶颈。同时,Prefill 阶段因为平方级的注意力计算也会很慢,但 FlashAttention 等优化使其不再主导。
🔬 具体分析:
-
Prefill 瓶颈:注意力矩阵为
[1, 32k, 32k],计算量惊人,但现代 GPU 的 Tensor Core 还能勉强应对。 -
Decode 瓶颈:每生成一个 token,需要读取的 KV Cache 大小 =
2 × L × S × d × sizeof(dtype)。当 S=32k 时,光读取 KV Cache 就可能需要几毫秒,而实际计算只需零点几毫秒。所以 Decode 是带宽受限的。
💡 结论:长文本优化必须优先考虑压缩 KV Cache(GQA、量化、稀疏化)和提升带宽利用率(FlashDecoding、PagedAttention)。
在 A/B 测试新推理框架或新量化方案时,需要收集哪些数据来判断是否达标?¶
🎯 必收集数据(分硬件层、服务层、业务层)
硬件与资源
-
GPU 利用率、显存占用、功耗
-
CPU/内存使用
服务性能
-
TTFT (P50/P99)、TPOT (P50/P99) 分布
-
吞吐量 (tokens/s)、并发数
-
错误率、超时率
-
Prefill/Decode 延迟占比
模型质量
-
在固定测试集上的自动指标(PPL、MMLU 等)
-
人类或 GPT-4 评估的胜率(Win/Tie/Lose)
-
生成长度分布、多样性(distinct n-grams)
业务指标
-
用户点踩率、满意度评分
-
任务完成率(如客服问题解决率)
-
平均对话轮次
📊 对比方法:在同一硬件、相同流量下,双模型并行服务,使用相同分流策略,运行至少数小时,确保统计显著。
对于端侧部署的模型,除了推理速度和精度,还需要评估哪些指标?¶
📱 端侧特有指标
-
功耗 (Power):每秒推理的能耗 (mJ/token) 或整体功耗 (W),影响设备续航和发热。
-
发热 (Thermal):持续推理后芯片温度,过高会导致降频,性能大幅衰减。
-
内存占用 (RAM):运行时占用的内存,模型太大可能被系统杀死。
-
存储空间:模型安装包大小,影响下载和更新体验。
-
冷启动时间:首次加载模型到可以推理的耗时。
-
稳定性:长时间运行是否出现内存泄漏或崩溃。
-
适配性:在不同芯片(骁龙、A17、麒麟)上的兼容性和性能差异。
💡 测试方法:使用厂商提供的 Profiler(如 Snapdragon Profiler、Xcode Instruments)测量功耗和发热,配合自动化脚本持续压测。
如何估算一个推理服务在给定硬件配置下的最大并发数?需要哪些输入参数?¶
🧮 估算公式(以自回归生成为例):
最大并发数 ≈ 可用于 KV Cache 的显存 / 单请求的平均 KV Cache 大小
📥 输入参数:
-
硬件:GPU 总显存(GB),预留显存比例(用于模型权重和临时缓冲区,通常 10-20%)。
-
模型:参数量、精度(FP16/INT4)、层数 L、隐藏维度 d、头数及 GQA 头数。
-
推理配置:最大生成长度
max_tokens,平均 prompt 长度。 -
框架内存效率:PagedAttention 的碎片率(通常 5-10%)。
🔢 估算步骤:
-
权重显存 = 参数量 × 精度字节。
-
可用显存 = 总显存 - 权重显存 - 系统预留。
-
单 token KV 大小 =
2 × L × (d * (head_ratio)) × sizeof(dtype)。 -
单请求平均 KV 大小 =
(prompt_len + 预计生成_len/2) × 单 token KV 大小(考虑生成过程中缓存逐步增加,取平均)。 -
最大并发数 = 可用显存 / (单请求平均 KV 大小 × (1 + 碎片率))。
💡 注意:这只是理论上限。实际并发还受算力、调度效率影响。在达到显存瓶颈前,可能先遇到计算瓶颈(如 Prefill 吞吐限制)。因此需要结合压测确定实际饱和点。
你曾经如何定位和解决推理服务上线后性能衰减的问题?复盘一下你的排查思路。¶
🕵️ 案例复盘:某在线客服模型上线一周后,TPOT 平均延迟从 40ms 缓慢增长到 80ms,且 P99 飙升至 500ms。
🔍 排查步骤:
-
确认变化:对比上线初和当前的监控面板,确认延迟劣化非流量增长所致(并发数未明显增加)。
-
检查资源:GPU 利用率仍约 85%,显存占用从 70% 升至 95%,初步怀疑显存碎片或内存泄漏。
-
框架日志:vLLM 日志中出现“block table full”和“preempting”警告,表明 KV Cache 物理页不足,触发请求抢占和重计算。
-
定位根因:分析请求分布,发现近期用户大量上传长文档进行摘要,平均 prompt 长度从 200 增至 800 tokens。由于 PagedAttention 按需分配页,长 prompt 导致页表迅速膨胀,碎片增多,物理页池提前耗尽。
-
解决方案:
- 紧急:降低
gpu_memory_utilization至 0.85,留出更多空闲页;限制 max_model_len 为 2048,阻断超长请求。 -
长期:启用分段 prefill,减少长 prompt 对显存的瞬时冲击;增加
--swap-space利用 CPU 内存作换页缓冲;扩容节点提升整体容量。 -
验证与回归:压测模拟长文本负载,确认 TPOT P99 回落至 120ms,且日志无 preempt 错误。
💡 经验:性能衰减往往源于负载特征变化,而显存是推理系统最脆弱的一环。健壮的监控和容量规划需定期回顾。