☰
LLM Serving框架与系统设计实证对比:真实负载下的性能真相
2026/10/26 12:56:31 网站建设 项目流程

1. 为什么需要一篇跑在真实环境里的Serving对比研究

LLM Serving,也就是大语言模型的服务化部署,最近两年几乎是每个做AI应用团队都绕不开的话题。模型训出来了只是第一步,真正让它变成产品里一个稳定、低延迟、高吞吐的接口,中间隔着的全是工程问题。框架选哪个、显存怎么省、并发一高为什么延迟就炸、同样的模型在不同框架下为什么吞吐差好几倍——这些问题在技术社区里每天都有人在问,但绝大多数回答都是零散的、带立场的,甚至只是某个框架在特定条件下的自报数据。

这段时间我读了一篇实证研究风格的论文,题目里有三个关键词:Frameworks、Methods、System Designs。它做的事情很朴素,却也很难得——把目前主流的LLM Serving框架、优化方法、系统设计放在同一套评估体系下,用真实工作负载跑了一遍,然后告诉你哪些差距是真实存在的,哪些被夸大了,哪些优化手段在什么场景下才有意义。

这篇论文的价值不在于给出一个“选XX框架就对了”的结论,而在于提供了一套可以复用的评估方法,以及一组在生产环境中才看得出来的规律性结论。以前我们选框架基本靠看GitHub star数、读各家benchmark博客、或者直接跑一个简单并发测试就拍板,但这篇论文让我意识到,Serving领域的性能差异高度依赖负载特征、上下文长度分布、输入输出比、甚至请求到达的节奏。同一个模型,同一个GPU,在对话场景和离线批量处理场景下,最优框架可能完全不同。

如果你是做AI应用后端、负责推理服务部署的工程师,或者正准备把模型推上线、正在纠结框架选型,这篇论文的读法跟普通技术博客不一样。它不是让你照抄某个配置,而是让你看清框架、方法、系统设计这三个层面分别解决什么问题,以及它们之间的耦合关系。读完以后最大的收获,就是不会再被单点benchmark数据牵着鼻子走。

论文里把Serving系统拆成了三层来看:框架层解决的是“请求来了怎么调度模型执行”,方法层解决的是“在执行过程中怎么压榨硬件利用率”,系统设计层解决的是“在多请求、多实例、不稳定的生产流量下怎么保住服务质量”。这三层单独看都不算新东西,但放在一起做实证对比的,确实不多。我读的时候反复在想一个问题:为什么我们自己部署时踩过的那些坑,论文里的数据几乎都复现了?答案其实不复杂——因为这些坑本来就是系统性问题,不是某一个框架特有的bug。

2. 测评对象:框架、方法和系统设计三个维度的边界

论文在展开实验之前,先花了不少篇幅把Serving领域的技术地图梳理了一遍。这部分我认为是全篇的骨架,因为很多人在选型时根本分不清自己遇到的问题到底出在哪个层面。是框架没选对,还是某个优化方法没开,还是整个系统设计就不适合这种负载?这三件事经常被混为一谈,归因自然就错了。

2.1 框架层:解决“请求如何排队、批处理、调度执行”

框架层面,论文把目前社区活跃的几个主流方案都纳入了测评范围。这一类开源项目虽然入口形式不同(有的带推理引擎,有的带完整API服务),但核心都在解决同一个问题:当多个请求同时到达时,怎么把模型参数加载进显存,怎么组织batch,怎么在生成过程中管理中间状态。

这层的关键差异主要来自两点:一是批处理策略,能不能动态地把新请求塞进正在执行的batch里,而不是等当前batch全部跑完;二是显存管理方式,KV Cache是固定预留还是按需分配,直接决定了同样一块卡能同时跑多少请求。早期框架多用静态批处理,请求按到达时间凑够一批才执行,batch里一旦有请求提前生成完,整个batch还得等最慢的那个走完才能释放资源。后来引入Continuous Batching(连续批处理)之后,吞吐有了质的变化,但实现细节在框架之间差异挺大,论文的实测数据显示这种差异在高并发下会被明显放大。

2.2 方法层:优化手段不是越多越好,配套才重要

方法层涵盖的优化手段我在读的时候数了一下,主要包括KV Cache的管理策略(量化、换入换出、回收复用)、投机解码、Prefix Caching、Prefill和Decode的解耦、显存池化等。论文对每个方法都做了单独的A/B式测评,但更关键的是它观察到了组合效应。

组合效应这个词听起来抽象,实际例子很容易理解。投机解码在小batch时几乎没有收益,因为此时GPU算力本来就有富余,多跑一个草稿模型反而是额外开销;但在大batch下,它能让Decode阶段更早释放显存和算力,整体吞吐反而能再上一个台阶。再比如Prefill和Decode解耦,对混有大量短请求和长请求的场景帮助极大,因为长请求的Prefill会抢占短请求的Decode算力,不去隔离就会互相拖累。这些方法单独看都有道理,但放在一起的时候,有的共振放大,有的互相抵消,论文实测里确实出现了“全开选项”不是最优结果的情况。

2.3 系统设计层:调度、排队、过载、多实例协作

系统设计层是论文里我个人觉得最有信息量的部分,因为大多数公开benchmark只测单实例、理想并发,根本不碰这个问题。生产环境里的Serving系统通常是一个集群:前面有负载均衡,中间有队列,后面有多个推理实例。请求的到达不是均匀的,时而波峰时而波谷,而且长请求和短请求混在一起。

论文在这一层重点研究了几个设计维度:排队策略(是先到先服务还是按预估长度调度)、过载时的行为(是丢弃新请求还是压榨已有请求)、多实例间的负载分配方式、以及长请求被抢占之后的恢复机制。这些设计平时不太会出现在框架的功能清单里,但在真实流量下,它们对延迟分布的影响往往比推理引擎本身还大。读完这部分我有一个很强烈的感受:做Serving部署,不能只盯着GPU利用率那一行指标。

3. 评估方法论:怎样设计一场“公平”的Serving实验

论文标题里的“Empirical Study”意味着它主要靠数据说话,所以评估方法的设计直接决定结论可信度。我看过太多对比测试,结论站不住脚往往不是实验不用心,而是变量没控制住,或者评估指标过于单一。这篇论文在方法论上有几个处理方式很值得搬回自己的测试流程里。

3.1 公平性设计:控制变量与最优配置的平衡

首先要说的是框架对比里最常见的坑:各框架默认配置不同,如果都按默认参数跑,差的框架可能只是没调好,而不是真的差;反过来,如果每个框架都使劲调优到极致,又会耗掉大量时间,而且未必贴近实际使用习惯。论文采取的是“贴近实际使用”的路线——每个框架都按官方推荐的生产配置来,但统一了模型权重、推理精度、并发模型和硬件环境。

硬件这块,所有实验都跑在同一批加速卡上,模型也都是同一份权重文件,避免“不同框架分不同机器跑”这种明显不合理的对比方式。模型的输入输出长度也做了统一抽样,而不是各用各的测试集。有一个细节我觉得特别妙:它把负载里的上下文长度分布明确列了出来——短请求、中请求、长请求分别占多少比例,因为上下文长度直接决定KV Cache占用和Prefill计算量,这个分布一变,框架间的排名都可能变。

3.2 工作负载设计:合成负载与真实流量的差距

论文同时用了合成负载和真实流量回放两种方式。合成负载的好处是可控、可复现,可以单独调节并发数、请求到达间隔、输入输出比例,用来观察某个参数对系统行为的影响。真实流量回放则用来验证一个容易被忽略的问题:在真实Web服务里,请求到达往往呈现突发性,几毫秒内涌进一大波请求,然后安静一小会儿。

突发流量对Serving系统特别不友好,因为调度器和批处理器在瞬间过载时,行为会变得完全不同。有的系统会直接拒掉新请求,有的会把现有batch拆散重排,有的则会排队硬扛。论文实测发现,在平滑负载下表现最好的框架,换到突发负载后可能连SLO都保不住。这一点对实际部署的启示非常直接:如果你只做匀速压测就上线,高峰期一来大概率会现原形。

3.3 指标体系:吞吐之外还要看延迟分布和SLO达成率

指标设计上,论文用的是一套组合拳:吞吐量、首token延迟(TTFT)、每个token的生成间隔(TPOT或ITL)、端到端延迟的P50/P95/P99、以及SLO达成率。这套指标现在看是行业共识了,但论文的处理方式比普通博客细致得多。

比如延迟,它不只是给一个平均值,而是给出了完整的分布和长尾情况。有些框架P50很低,P99却高得离谱,这意味着对个别用户来说体验极差;有些框架平均吞吐没那么亮眼,但延迟整体均匀,反而更适合对稳定性要求高的场景。论文还引入了SLO达成率这个维度——不是看平均延迟,而是看有多大比例的请求在规定时间内完成了。这个指标比平均延迟更能反映真实用户体验,因为它直接对应用户的不满意率。

表:论文指标体系与典型关注点

指标维度关注的问题对实际部署的意义
吞吐量(请求/秒、token/秒)硬件利用率的极致表现决定单实例成本,离线批量场景优先看
TTFT首token延迟用户等待第一字的感受对话、交互式应用的关键指标
ITL/TPOT每个token间隔生成过程是否流畅流式输出场景体验的核心
延迟分布P50/P95/P99是否存在长尾劣化判断系统稳定性,防止个别请求被饿死
SLO达成率多少请求在规定时间内完成直接决定服务质量承诺能否兑现

4. 核心发现:框架差异、方法组合与系统瓶颈的实测结论

这篇论文最硬核的地方在实验结论部分。我不打算复述每一个图表的细节,而是把几个对部署决策最有影响、也最容易被忽视的规律性发现拎出来讲。如果你正在做框架选型或者性能调优,这几条大概率能帮你少走一些弯路。

4.1 没有“绝对最快”的框架,只有“适配某种负载”的框架

论文的核心结论之一是:不存在在所有指标上全面领先的框架。在低并发、短上下文的场景下,某些轻量级框架的表现非常亮眼,延迟低、资源占用小;但一旦并发上来、上下文变长,显存管理能力强的框架优势就会凸显。更微妙的是,吞吐量和延迟并不是同向变化的——有的框架能把吞吐压得很高,但代价是P99延迟明显变差,因为过多的请求被塞进同一个batch,个别慢请求拖累了整体。

这意味着什么?选框架之前,必须想清楚自己的主要负载类型。如果做的是内部工具、并发不高,选轻量方案可能更划算,没必要为了极端吞吐背上复杂的运维负担;如果做的是面向大量用户的在线服务,就得重点看高并发和显存管理能力,同时做好延迟长尾的监控。论文里那个让我印象深刻的例子是:同一个模型,在一个以短请求为主的负载下,框架A的吞吐比框架B高大概一成;换成长请求占主的负载后,框架B反超,而且反超幅度接近五成。负载一变,排名翻盘,这就在提醒我们不要照搬别人的benchmark结论。

4.2 KV Cache的管理方式决定了并发上限,而不只是省显存

关于显存,论文给出了一个很清晰的结论:KV Cache管理策略对并发能力的影响,主要集中在长上下文场景。短上下文请求的KV Cache占用不大,任何框架用暴力预留也不会太吃亏。可当上下文长度拉长到数万token甚至更多时,按最大长度预留显存的做法会让并发数被压缩到惨不忍睹——每多一个请求,就要预留一份可能根本用不满的缓存空间。

论文里对比了不同KV Cache策略在长上下文下的表现差异,结论让我想起自己线上服务踩过的坑:我们之前某个服务把max length设得很激进,结果显存扛不住只能降低并发,用户高峰期排队严重。后来换成按需分配策略,同一批GPU能承载的并发直接翻倍。围绕KV Cache,论文还提到了量化缓存和缓存换出这几类做法,各有适用前提:量化会在极端场景下引入质量损失,缓存换出则依赖CPU内存带宽。它们的共同逻辑是,把显存当成一个动态资源池来管理,而不是静态按最大需求预留。对于做在线服务的团队,这部分值得反复读。

4.3 优化方法的组合收益不是线性的,甚至存在负优化

论文对每个优化方法都做了单独测评和组合测评,结果显示:组合收益不是累加的。最经典的负优化场景是投机解码和CPU offload同时开启——投机解码希望通过小模型草稿减少大模型的Decode步数,但部分KV Cache换出到CPU后,草稿验证需要频繁访问CPU内存,走一次的速度反而比纯粹在GPU上多跑几步还慢。类似的情况也出现在Prefix Caching上:对单一场景、请求之间几乎没有重复前缀的负载,这个优化不仅不收益,还会因为额外的缓存查找逻辑增加开销。

方法层面的选择,必须结合自己的请求特点来权衡。如果你的场景是Agent式的多轮对话,每条请求都带着很长的系统提示词和早轮历史,重复前缀很多,Prefix Caching的收益会非常可观;如果你的场景是每次独立的短查询,前缀缓存基本没用。论文给出了一套判断思路:先算算负载里有多少重复前缀、有多少解码步数可以被草稿模型覆盖、显存压力主要集中在哪一层,再决定开哪些优化。盲目把框架里的高级选项全部打开,结果大概率不会像功能列表那么好看。

4.4 高并发下最大的瓶颈往往不在GPU,而在调度和排队

这是论文里我认为最容易被低估的结论。实验数据显示,当并发超过一定阈值后,GPU利用率可能还远没到100%,系统的端到端吞吐已经不再提升,甚至开始下降。瓶颈转移到了CPU侧的调度逻辑、batch组装、Python层的参数传递和内存拷贝开销上。框架的Serving部分如果处理得不够高效,请求排队和组批的逻辑会成为新的瓶颈,GPU反而在那里空转等待。

排队策略对延迟的影响更直观。论文对比了几种常见的排队策略:先到先服务、按预估长度优先、混合作业调度。在长短请求混合的场景下,先到先服务会让短请求被前面的长请求堵住,P99延迟非常难看。按长度优先则能明显改善响应速度,但可能导致长请求饥饿。更合理的做法是在两者之间取平衡,比如给长请求加上老化因子,随着等待时间增长提升优先级。这些调度层面的细节,论文里没有给出标准答案,因为最优解严重依赖业务负载特征,但至少它用数据证明了:Serving系统,尤其是高并发场景,工程优化的重心已经有一部分从GPU移动到CPU侧,甚至是策略侧了。

5. 从论文结论到部署决策:选型、压测和容量规划

读论文终究是为了指导实践,所以我想把这套实证方法落到可操作的层面。论文用大量实验说明了一个关键道理:Serving系统的性能是负载特征和系统配置共同决定的,所以正确做法不是找一份现成benchmark来“证明”某个框架好,而是自己把负载摸清楚,再在同等条件下做对比。

5.1 选型前的第一件事:给你的请求画像

我建议所有准备做Serving选型的人,第一步都是先统计自己业务的请求特征。至少要弄明白这几个分布:请求到达的时间间隔分布(是否存在明显突发)、输入token长度分布、期望输出token长度分布、以及请求之间的前缀重复率。这些数据可以从测试环境流量或者历史API日志里统计出来,用一张表就能看明白。

表:请求画像与对应的关键选型维度

画像维度统计方式对选型的影响
请求到达节奏每秒请求数曲线、峰值/均值比判断是否必须有过载保护、排队设计
输入长度分布P50/P95/P99直接影响Prefill开销和显存规划
输出长度分布平均输出token数、长输出占比判断Decode阶段压力和投机解码是否有效
前缀重复率请求之间共享的token比例判断Prefix Caching要不要开
并发峰值高峰期并发请求数决定单实例还是多实例、是否要调度扩展

做完画像以后再去评估框架,你就知道自己最该关注哪些能力了。比如你的输出普遍偏长,那就得重点测高并发下的Decode吞吐和显存管理,而不是只看首token快不快。这个问题看似简单,但我在接触过的不少团队里都见过相反的做法——大家一上来就比“谁的吞吐高”,却没人说得清自己业务的负载长什么样。

5.2 压测必须模拟真实流量模式,别做匀速炮击

论文里反复出现的“突发负载”提醒我,压测方案需要设计得跟真实流量一样“难缠”。现在很多压测工具默认是匀速发压,但这并不能测试出系统在突发下的真实表现。建议在同一轮压测里加入几个不同阶段:先是稳定的中低并发,维持几分钟看看基线;接着突然把并发拉高到峰值的两倍,保持几十秒,观察系统是否出现排队、拒绝、内存溢出;再快速回落到低并发,看看恢复速度和是否有内存没有释放。

这种压测方式一次就能暴露很多问题:排队策略是不是会造成短请求饿死、过载保护是不是宁可拒绝也不拖垮整体、长请求占用资源时新请求还能不能被及时处理。我在自己负责的服务上复现过论文里的“突发后性能劣化”现象——压测时匀速每秒钟100个请求漂亮得很,一旦模拟突发流量,P99直接翻了几倍,而且恢复期比想象中长,原因是缓存批处理里积压了大量半完成的请求,短时间清不干净。这些问题如果不在上线前暴露,就会在用户高峰期成为事故。

5.3 拆解容量规划的三个步骤

容量规划这件事,论文没有直接给公式,但它的实验逻辑可以推导出一套很实际的估算方法。第一步,确定单实例在目标负载下的吞吐上限和显存承载能力,注意是“在目标负载下”,不是在generic的标准benchmark下。第二步,用SLO达成率反推所需的实例数量——假设一个实例在并发50时,P95延迟还能满足要求,并发80时就开始衰减,那么高峰期并发200就需要至少4个实例,并预留一定的过载缓冲。第三步,把实例数量对应到GPU资源、显存和CPU核数上,形成每一路服务的容量基线。

这套方法的好处是每个数字都跟自己的业务挂钩,而不是拍脑袋定的“先上多少个实例再说”。当然,容量规划不是一次性工作。模型参数调整、上线新功能、用户规模变化,都会改变负载画像,所以建议每隔一段时间根据线上监控数据回刷一遍这个估算过程。论文里有一句话我印象很深:系统设计是一个持续逼近真实负载的过程,而不是上线那一瞬间做完的动作。这句话用来概括LLM Serving的运维节奏非常合适。

5.4 实际部署中的几个隐性成本

除了框架本身的性能,部署运维成本也应该纳入选型考量,这一部分论文虽然着墨不多,但结合实际体验我想补充几点。第一,多实例部署时的负载均衡方案是不是现成的,还是需要自己写路由层;第二,监控体系是否完整,能不能观测到排队长度、KV Cache命中率、显存碎片这些核心指标,而不是只有GPU利用率这种粗糙的全局数据;第三,配置热更新和模型热加载的能力,如果每次调整参数都要重启服务,迭代速度会拖慢很多。这些隐性成本不容易量化,但在长期运维里会实打实地影响团队效率。

论文的实验数据覆盖的是性能维度,而性能只是选型的一个维度。一个吞吐稍微低一些、但运维界面更友好的框架,对一个只有两三个人的小团队来说,很可能比极限性能更优秀的大框架更合适。所以读这篇论文的时候,不必把它的性能排名当成交付标准,而应该把它当做一个用来校准自己测试方法的参照系。

6. 从“选框架”到“设计服务系统”:这篇论文带来的思维转变

读完整篇论文,我最想强调的不是某个框架赢了、某个方法失效了,而是整个问题域的重心转移。过去我们对LLM Serving的理解,很大程度停留在“选个好框架”上,仿佛框架选定了一切就尘埃落定。但论文用大量实验表明,框架只是起点,真正决定线上体验的是你如何理解自己的负载、如何配置系统的调度与排队策略、如何组合优化方法、以及如何持续监测和调优。

这种思维转变对工程实践的指导意义在于:任何时候性能出了问题,先别急着怪框架或者换框架。优先检查的是负载画像是否清楚、压测方式是否贴近真实流量、系统配置有没有跟业务特征匹配。很多时候性能数据不好看,只是因为在错误的前提条件下做了对比。论文里提到的KV Cache策略差异、调度策略差异、优化组合的边际收益递减,这些东西不是某一个框架独有的,而是所有Serving系统都面临的共同问题,区别只在于不同框架提供了什么样的旋钮和接口。

我自己最大的实际收获,是重新设计了团队的压测流程。以前我们跑完一轮并发测试,只看“平均延迟”和“QPS”两个数字就写报告,现在会完整记录请求到达曲线、上下文长度分布、P95/P99延迟、SLO达成率,并且把突发测试作为必选项。这个改变让我发现了好几个隐藏问题:某个框架的默认队列长度设置太小,高峰时会直接丢弃请求;另一个框架在长请求比例高的时候,短请求的P99会恶化到不可接受的程度。这些问题以前要么被平均数据掩盖,要么直到线上事故才暴露。按照论文里的思路排查,归因和修复都快了很多。

如果你正在做类似的Serving部署,我建议你也把这篇论文当成一份方法论文献来读,不必太纠结它的具体实验数值(因为它们对应的是论文自己的负载和硬件环境),重点是把它的评估思路迁移到自己的场景里。先给自己的请求画像,再设计贴近真实流量的压测脚本,再对照框架在不同指标上的取舍做出选择,最后在线上持续用SLO数据校准容量规划。这套流程走下来,你会发现Serving选型这件事从玄学变成了工程。

最后再补充一个实用小技巧:论文里的负载模拟思路可以自己实现——把你的线上API日志按照时间顺序回放,作为压测工具的输入流量。这样既有真实的到达时间和长度分布,又能通过倍速播放模拟峰值压力。我自己用了这个办法之后,测试结果跟线上行为接近了很多,比任何合成流量模型都靠谱。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询