ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

用JMeter压测大模型服务:流式接口QPS统计与容量评估实战

用JMeter压测大模型服务:流式接口QPS统计与容量评估实战 近两年大模型相关的性能测试需求越来越多但很多人一上来就习惯性掏出JMeter按接口压测的老套路走结果压出来的QPS要么虚高要么低得离谱根本没法作为容量评估的依据。这个问题的核心不在于JMeter本身而在于大模型服务的计费方式、响应模式和资源瓶颈跟传统HTTP接口完全不同你不能拿测订单接口的思路去测一个流式输出的文本生成服务。这篇文章就专门聊清楚一件事怎么用JMeter把大模型服务的QPS测准。我会从指标定义、脚本设计、Beanshell统计、压测策略到常见坑位把整套流程完整走一遍。适合正在做大模型服务上线前压测、容量评估、或者想搞清楚“我这个模型部署到底能扛住多少并发”的测试和运维同学。就算你之前完全没碰过JMeter按着这篇的步骤走也能把QPS跑出来。1. 为什么选JMeter测大模型QPS以及和传统压测的本质区别先解决一个基本问题大模型服务到底能不能用JMeter测答案是可以而且JMeter在“快速搭建压测脚本多线程模拟并发聚合报告出指标”这条链路上依然是最顺手的工具。但你必须先接受一个事实——大模型接口的压测跟普通REST接口压测有本质区别用错方法数据就是废的。1.1 大模型QPS的传统接口压测误区很多同学第一次测大模型都是这么干的开一个线程组50个线程跑10分钟每个线程循环调用一次Chat接口最后打开聚合报告看“Throughput”那个数字嘿显示200/s开心得不行觉得模型服务很能打。但实际上这个数字可能只有真实业务吞吐的一半甚至更低也可能反过来虚高。问题出在哪传统接口压测里一个请求的响应时间通常在几十到几百毫秒响应体是固定JSONJMeter拿到完整响应就算结束。但大模型接口走的是流式输出一个请求从发出去到最后一个token返回可能持续10秒甚至更久。线程数是固定的每个线程一次只能跑一个请求那QPS的上限就被“响应时间”死死锁住了QPS约等于线程数除以平均响应时间。一个50线程的压测如果平均响应15秒那聚合报告里显示的吞吐最多也就是3.3/s左右——这不是服务的真实能力这是JMeter线程池的物理瓶颈。反过来如果你用的是异步接口或流式接口但没有正确读取响应流JMeter可能只计算“连接建立请求发出”的时间就标记为完成那响应时间会短得离谱QPS虚高但这个数字跟实际用户体感完全对不上。所以第一件事就是JMeter压大模型不要直接盯着聚合报告里的Throughput交差你要先搞清楚你测的是哪种模式、JMeter的数是怎么算出来的。1.2 CLI模式响应时间、首token延迟与吞吐量之间的关系大模型服务的响应过程可以拆成三段请求到达服务端并完成预处理Prompt处理、模型开始生成并吐出第一个token、持续生成直到结束或达到max_tokens上限。三段时间加起来才是完整响应时间但真正影响用户体验和业务容量的是两个独立指标首token延迟TTFTTime To First Token从请求发出到收到第一个token的时间用户感知到的“转圈时间”就是它。生成吞吐Tokens per Second模型每秒生成的token数决定了流式输出的“打字速度”。QPSQuery per Second单位时间能完成多少完整请求。这三者之间存在相互制约的关系。并发升高时显存和计算资源被抢占TTFT会变长单请求的生成速度可能变慢但服务整体QPS可能在上升——直到资源耗尽开始排队、超时甚至报错。所以压测大模型的核心目的不是简单找一个“最大QPS”而是找到“在可接受的TTFT和错误率前提下服务能稳定支撑的最高并发与QPS曲线”。这也是为什么我会把JMeter脚本里的统计逻辑专门改成“按请求维度统计QPS同时附带监控TTFT分布”——聚合报告默认只能给你一个平均响应时间根本看不出首token延迟的变化趋势而首token延迟恰恰是大模型服务容量规划里最敏感的信号。2. 压测前的准备工作环境、指标和部署形态确认在动手打开JMeter之前有四个前置条件必须确认清楚。我见过太多人脚本写得漂漂亮亮结果压出来的数据没法用就是因为前置条件没定死。2.1 确认大模型的部署形态和接口类型大模型服务的部署形态基本决定了你压测脚本的写法。如果是OpenAI兼容的HTTP接口那JMeter直接按HTTP请求处理就行这是大多数中小团队最快落地的方案如果走的是Triton Inference Server的gRPC接口那JMeter原生不支持gRPC要么用自定义JSR223采样器要么先用ghz之类的专用工具做初步压测再把核心结论搬到JMeter里做HTTP层的并发回归。接口类型上你还需要确认一件事模型是走流式输出streamtrue还是非流式输出streamfalse。流式输出适合偏对话类的应用场景用户要看到打字机效果非流式适合内容生成类的后台任务比如批量写摘要、离线推理。两种模式下JMeter的处理器编写完全不同——流式接口返回的是data:前缀的SSE事件流JMeter默认不会自动解析需要写专门的读取逻辑非流式接口相对简单一个HTTP采样器加JSON提取器就够了。提示没有特殊说明时我下面的所有脚本示例都假设你的模型服务提供了一个OpenAI兼容的/v1/chat/completions流式接口。如果你用的是国内开源自建服务比如基于vLLM、TGI部署的开源模型通常也兼容这个接口格式云厂商的API可能略有差异但请求体结构大同小异。2.2 明确测哪些指标QPS的统计口径与目标值锚定在开始压测前先跟团队的SRE或算法同学对齐口径你们要的QPS是“服务端成功处理的请求数/秒”还是“JMeter客户端看到的完成请求数/秒”正常情况下这两个数字应该接近但如果你加了超时时间、客户端断连重试就会产生偏差。我的建议是统一用“JMeter侧统计的成功请求数 / 总压测时长”作为QPS同时记录这个QPS对应的P99响应时间、错误率、平均生成token数这样才有横纵对比的意义。目标值怎么锚定如果你们的场景是智能客服参考用户流量峰值比如200 QPS那压测目标就是验证200 QPS下P99 TTFT不超过2秒、无5xx错误如果是离线批量你可能更关注吞吐型QPS允许单请求耗时更长但错误率必须为零。确定目标之后压测才有验收标准否则跑出来的数字只是数字没有决策价值。2.3 JMeter环境准备安装、插件和运行模式选择JMeter的安装本身不复杂下载二进制包解压即用但有几个细节值得说JDK版本JMeter 5.6.3以上建议JDK 11或17老版本JDK8跑高并发时容易OutOfMemory。压测开始前改一下jmeter.bat或jmeter.sh里的HEAP参数示例是-Xms2g -Xmx4g大规模压测建议给到8g以上。别小看这个默认堆内存512MB跑500线程压大模型JMeter自己先崩给你看。插件如果你要看实时TPS曲线装jmeter-plugins-perfmon和Custom Thread Groups如果只看聚合报告裸JMeter就够。运行模式压测时一定要用命令行模式跑不要开着GUI跑高并发。GUI模式光是绘制曲线就能吃掉大量CPU会让压测结果失真。命令行跑完之后再用GUI打开.jtl结果文件看报告这是标准操作。# 命令行执行压测 ./jmeter -n -t test_llm_qps.jmx -l result.jtl -e -o report_dir这个命令的意思是-n非GUI模式-t指定脚本-l输出原始结果文件-e生成HTML报告-o指定报告输出目录。压测完成后直接打开report_dir/index.html就能看到聚合报告和图表比GUI模式手动导数据方便太多。2.4 确认模型服务的并发承载边界与GPU监控配合压测大模型不比压测普通接口因为模型推理是典型的计算密集显存敏感型任务。你在压测过程中必须同时盯着GPU利用率、显存占用、服务端的排队长度。怎么盯最简单的方式是用nvidia-smi定时轮询写入日志或者部署一个PrometheusGrafana的监控面板。压测结束后把JMeter的QPS数据跟GPU利用率曲线对齐看——如果QPS还在涨但GPU利用率已经99%、显存接近满载那瓶颈就在计算卡上如果QPS上不去但GPU利用率不到50%那大概率是框架层比如vLLM的调度或网络层卡住了压测数据会让你误判模型能力。我是强烈建议在压测之前先粗跑一轮“冒烟测试”用10个线程跑1分钟确认接口通、响应流能正确解析、JMeter能正常统计——这一步花10分钟能省后面2小时排坑。3. 实操在JMeter中构建大模型QPS压测脚本脚本构建是整个流程的核心。我直接给出一个可复用的方案然后逐段解释为什么这么写。3.1 测试计划结构与核心采样器配置JMeter的测试计划结构我习惯分成这么几层线程组Thread Group控制并发数和持续时间这里不用循环次数用调度器时长控制。HTTP请求默认值HTTP Request Defaults统一填协议、host、端口后面所有HTTP采样器都自动继承。HTTP头管理器HTTP Header Manager统一管理Authorization、Content-Type请求头。用户定义的变量User Defined Variables放模型名称、prompt内容、max_tokens等参数方便压测时随时调整。循环控制器Loop Controller控制每个线程循环发送请求次数压长稳时也可以用固定吞吐量定时器控制速率。HTTP采样器实际的请求内容。Beanshell/ JSR223断言Assertion解析流式响应、统计token数和请求耗时。汇总报告Summary Report或聚合报告Aggregate Report最终的结果视图。HTTP请求默认值里我通常这么配协议填https或http服务器名称填你的模型服务域名或内网IP端口按实际服务端配置填路径留空因为不同采样器路径不同。这个默认值最大的价值是后面如果切换到另一套环境只改一个地方所有采样器全部生效。核心的HTTP采样器配置如下请求方法: POST 路径: /v1/chat/completions 消息体: { model: qwen2.5-7b-instruct, messages: [ {role: user, content: 用一句话解释量子纠缠} ], max_tokens: 128, stream: true, temperature: 0.7 }这里有个细节压测时把max_tokens设成一个固定值比如128或者256。为什么因为不同请求生成token数不同QPS统计就会失真——一个只生成了10个token的短请求和一个生成了512个token的长请求对系统资源的占用完全不同如果混在一起看QPS时间长了根本没法对齐性能。固定max_tokens就是给每个请求设定同样的“工作负载”压测数据才有可比性。我通常是跑两组一组max_tokens128模拟短对话一组max_tokens512模拟长文本生成场景。3.2 流式响应与SSE事件流的处理这是大模型压测跟普通接口压测最大的分水岭。流式接口返回的是text/event-stream格式每行长这样data: {id:chatcmpl-123,object:chat.completion.chunk,choices:[{delta:{content:量子},index:0}]} data: {id:chatcmpl-123,object:chat.completion.chunk,choices:[{delta:{content:纠缠},index:0}]} data: [DONE]JMeter默认的HTTP采样器拿到完整响应后才会标记结束但流式响应是边生成边发送的JMeter实际是等连接关闭或读取到EOF才认为响应结束——这会导致一个问题如果你请求头里没设置正确的Accept头或者服务端配置了keep-alive导致连接不主动关闭JMeter可能会一直挂在那等导致响应时间被无限拉长。正确做法是在HTTP头管理器里加上Accept: text/event-stream然后服务端会在生成完最后一个token后发送data: [DONE]但连接通常还会保持一段时间。为了避免JMeter傻等建议在脚本层面做两层保险设置合理的响应超时时间比如120秒同时在Beanshell断言里判断如果收到了[DONE]标记就不再等后续数据。这块我会在下一节详细写代码。另一个坑是JMeter的“响应体”对SSE流式数据默认会做编码转换有时候会把\n吃掉导致data:行粘连在一起。处理办法是断言里用prev.getResponseDataAsString()拿原始响应自己在代码里按行拆分解析别依赖JMeter的响应文本解析功能。3.3 Beanshell断言如何统计QPS并识别请求真实完成现在到了全文最关键的环节——写断言统计QPS。打开采样器的“断言”设置添加一个BeanShell断言BeanShell Assertion把下面这段代码粘进去。import org.apache.jmeter.samplers.SampleResult; // 获取原始响应 String response prev.getResponseDataAsString(); // 简单校验是否正常结束 if (response.contains([DONE])) { // 用日志输出做记录压测时通过 jmeter.log 观察 // log.info(Request successful, response length: response.length()); } else { Fail(Response missing [DONE], request may be incomplete); } // 统计流式返回的行数粗算token数 int lines 0; String[] splitLines response.split(\n); for (String line : splitLines) { if (line.startsWith(data:)) { lines; } } // 放入变量方便在用后置处理或结果统计时读取 vars.put(stream_line_count, String.valueOf(lines));这段代码做了两件事第一确认响应完整包含结束标记[DONE]第二粗略统计返回的data:行数作为生成token数的近似值。为什么是近似值因为OpenAI兼容格式中某些chunk可能包含多行或空行laser行号和真实token数不完全一致但对QPS统计影响不大——我们要的是“完成请求数”不是精确token数精确统计可以留到服务端日志做。真正的QPS统计工作我建议不要放在断言里而是用JMeter的汇总报告或命令行生成的HTML报告来看。为什么因为Beanshell断言的执行频率太高每个请求都跑一段Java脚本本身就会消耗JMeter所在机器的CPU高并发下反而拖慢压测吞吐。我在实际操作中见过1000并发下Beanshell断言占掉客户端40% CPU的情况。所以好的折中方案是断言只做“是否含[DONE]”的简单校验不做复杂的正则或计数token统计交给服务端日志QPS统计交给JMeter聚合报告。如果你确实需要在压测过程中实时统计精确响应时间分布我建议用JSR223采样器Groovy替代BeanshellGroovy的执行效率比BeanShell高一个量级写起来也舒服。举个例子可以用JSR223 PostProcessor把每个请求的耗时写入一个CSV文件压完再用Python做后处理能画出更细的P95/P99 TTFT曲线。3.4 线程组参数设计并发数、持续时间与压测策略线程组是压测的“油门”。对大模型服务我的建议是区分两个阶段阶梯加压阶段和持续稳定阶段。第一步用Ultimate Thread Group插件或者JMeter自带的Stepping Thread Group如果装了插件来设计阶梯加压比如初始50线程跑1分钟每30秒加50线程直到加到500线程。这样能观察QPS随并发增长的曲线找到拐点——也就是“并发加到某个值时QPS不再线性增长甚至开始下降”的位置这个拐点对应的并发数就是服务的软极限。如果不做阶梯加压直接上500线程压10分钟你可能直接把服务压挂数据也没法用。第二步找到拐点后在拐点前一个安全位置设置稳定的并发数持续压5-10分钟验证长稳状态下QPS是否稳定、错误率是否为零、显存是否持续正常。长稳测试非常重要因为大模型服务在长时间运行后会遇到增量KV Cache的显存增长、碎片化、推理引擎的排队堆积这些短压根本暴露不出来。线程组设计好之后记得把“调度器”打开Duration填600秒相当于固定压10分钟。不要用循环次数因为不同响应时间下循环次数会导致线程组提前跑完或跑太久时间不可控。调度器模式下JMeter会用请求发送间隔尽量填满这600秒统计出来的QPS才准确。4. 使用聚合报告正确解读QPS和其他关键指标跑完压测后命令行生成的HTML报告已经包含大量信息。但很多新手的误区是只看“Throughput”一个数字然后就说“QPS是XX”。这个数字确实代表了JMeter侧统计到的每秒请求数但你没看它的并发数、错误率、响应时间分布等于只看了冰山一角。4.1 聚合报告关键指标解读Throughput的统计口径和局限性聚合报告Aggregate Report和HTML报告里的指标主要有指标含义注意点Samples总请求数压测时长内发出的所有请求包括失败请求Average平均响应时间所有请求从发出到收到完整响应的平均耗时Median响应时间中位数50%请求耗时低于该值比平均值更抗极端值干扰90% Line / 95% Line / 99% Line百分位响应时间重点关注P99大模型服务长尾明显平均值严重失真Throughput每秒完成请求数这就是JMeter口径下的QPSError %错误率大模型压测建议以0%为目标或明确容忍阈值注意到没有聚合报告里没有直接区分TTFT和生成吞吐。如果你只测流式接口的完整响应时间Average可能是10秒但其中3秒是首token等待7秒是生成延迟——这两个数字混在一起你无法定位瓶颈。这也是我在第2节强调要单独统计TTFT的原因。实操中我的做法是压测结束后除了看聚合报告还会拉服务端access log统计每次请求的time_to_first_token字段。大多数大模型推理框架如vLLM、TGI都会在日志里输出这两个指标。把JMeter的QPS和框架的TTFT画在一张图上才能回答“这个QPS下用户体验到底怎么样”的问题。4.2 响应时间分布与QPS曲线的联动分析QPS只是“果”不是“因”。我建议压测结束后的第一步先看HTML报告里的响应时间分布图和每秒事务数曲线。如果响应时间随着并发上升而线性增长但QPS还在涨说明服务还有余量如果QPS开始持平甚至下降但响应时间暴涨说明服务进入过载区——这个拐点就是你要找的容量上限。举一个我实测过的例子某开源7B模型基于vLLM部署在单张A800上max_tokens256时50并发QPS约12P99响应时间约15秒加到100并发时QPS涨到18但P99飙升到28秒再加到200并发QPS反而掉到16而且开始出现502。这个例子说明该配置下“舒适容量”在50并发附近“极限容量”在100并发过了反而更差。如果你不结合QPS曲线和响应时间看单看“200并发下QPS还有15看起来不错”其实用户侧的延迟已经不可接受了。所以每次压测完之后我习惯做一张小结论表并发数QPSP99响应时间错误率服务端状态501215s0%GPU利用率75%1001828s0%GPU利用率92%2001645s2%开始排队出现超时这张表直接进文档比单纯留一个聚合报告截图有用得多。5. 大模型压测常见坑位与排查技巧实录最后这部分是我自己踩坑踩出来的经验汇总。写出来帮你排掉那些十次压测里至少碰到八次的怪问题。5.1 连接超时、读取超时与Keep-Alive的冲突大模型流式生成时间长普通的HTTP客户端超时设置根本不够用。如果你的HTTP采样器“Timeout”里Connection和Response都是默认的60秒那生成长文本的请求大概率会被JMeter判为超时失败但服务端其实还在正常生成。这会导致错误率虚高QPS被低估。解决方式在HTTP采样器的高级设置里把Connect Timeout设5秒、Response Timeout设120秒甚至300秒具体值根据你的max_tokens和模型速度算比如模型每秒生成30个tokenmax_tokens512理论最长耗时约17秒加上首token延迟和排队时间预留60-120秒比较稳妥。如果max_tokens还会加大就把超时设到300秒宁可让脚本多等也不能误杀正常请求。还有另一个跟超时相关的坑JMeter默认每个线程会复用连接Keep-Alive这对普通API压测是好事但大模型服务端如果限制了单连接并发数或单连接最长存活时间复用连接反而会导致后续请求排队等待。我曾在压测某家大模型API时发现并发上不去查了半天发现是服务端对单连接同时只允许两个在途请求JMeter一个线程一个连接50个线程同时发连接数就50个被服务端限流了。后来我在采样器高级设置里勾选了Disable Keep-Alive强制每次新建连接QPS立刻恢复正常。注意不要贸然关闭Keep-Alive。只有确认服务端对连接复用有限制时才这么做否则新建连接的开销在高并发下反而会吃掉大量吞吐。5.2 大模型服务端显存OOM与请求排队导致的雪崩效应压测并发拉高之后最危险的不是QPS下降而是服务端显存OOM后进入“一个请求挂了拖垮所有请求”的雪崩状态。尤其是用vLLM或Triton部署时如果并发请求需要的KV Cache超过显存容量服务端会拒绝新请求或强制回收已有请求的缓存表现就是错误率骤升、响应时间雪崩式增长。怎么排查压测过程中一旦发现错误率从0突然跳到10%以上先看服务端日志里有没有Killed或CUDA out of memory同时看nvidia-smi显存使用率是否已经接近100%。如果确认是显存瓶颈就不要盲目压更高并发——要么调低max_tokens减少KV Cache占用要么扩容GPU要么换用支持PagedAttention的推理框架vLLM在显存管理上做得比其他框架好很多。我在实操中遇到过一种更隐蔽的情况服务端看起来没有报错但响应时间在并发升高时出现规律性的“周期峰值”比如每30秒来一次尖峰。一查发现是推理框架的定时清理任务在跑每次清理时请求全部排队尖峰过后恢复。这种情况不算故障但会影响QPS稳定性压测时长最好覆盖几个清理周期比如跑10分钟以上把这种波动平均进结果否则你压了3分钟取的数据可能刚好在尖峰区或低谷区数据失真。5.3 压测机自身成为瓶颈客户端线程数、Beanshell性能与网络带宽第三次强调这个问题因为它是大模型压测最容易忽略的“隐性杀手”。流式响应的数据量比普通接口大得多——一个max_tokens512的响应按每个token约4字节算实际带上JSON格式更大也有2KB左右100并发下每秒吞吐可能到几MB但和普通高并发接口相比不算大——真正吃客户端资源的是连接数和响应解析。500线程并发就意味着500个并发连接每个连接上挂着一个等待流式数据的线程。JMeter默认的HTTP实现Java在高连接数下会创建大量线程和缓冲区压测机自身的内存和CPU会先打满。解决方式是首选HttpClient4实现在JMeter启动参数或脚本中设置它能复用连接池资源占用小很多同时把Beanshell断言里的复杂逻辑全部去掉或换成Groovy避免每收到一个chunk就执行一遍解析脚本。还有一个小技巧压测过程中用top命令盯一下JMeter进程的CPU和内存。如果JMeter进程CPU超过80%你应该先给压测机降负而不是继续加压——否则压出来的QPS反映的是客户端上限而不是服务端能力。# 压测进行中另开终端监控JMeter资源占用 top -p $(pgrep -f ApacheJMeter.jar) -d 15.4 JTL文件中响应字节数与实际Token数的校准方法最后分享一个我常用的校准技巧。大模型流式响应中data:行里除了content还包含了id、created、model、choices等大量元数据所以响应字节数跟实际token数并不线性对应。如果你想从JMeter的响应字节数估算token数可以压测前先手动发一个max_tokens100和max_tokens500的请求记录响应字节数差值就能算出一组“字节数≈单个token的平均字节数”再把jmeter日志里的平均响应字节数换算成token数。这个方法不如服务端日志精确但作为快速评估够用。更重要的是这个校准能帮你识别脚本问题如果某个请求的响应字节数异常大或异常小多半是请求参数重复或截断了及时排查比压完再看数据要好。6. 从QPS到容量评估进一步扩展的压测思路如果你已经掌握了上面这些基础操作接下来可以往更复杂的方向扩展。毕竟大模型压测不是一次性的任务而是容量运营的一部分。6.1 场景化压测对话多轮、长文档摘要与流式打字机的差异化线上真实场景很少是“单轮固定输入输出”这么简单的。智能客服的流量特征是短prompt多轮对话短输出文档助手是长prompt短输出代码生成是中等prompt长输出。三种场景对服务的资源消耗模式完全不同——长prompt主要吃prefill阶段的计算能力长输出主要吃decode阶段和显存带宽。所以压测脚本最好做成参数化的把prompt内容、max_tokens、stream开关都放在用户定义变量里压测时用CSV文件或JMeter参数化功能批量替换。我习惯准备三组CSV数据一组客服短文本prompt约50字max_tokens128、一组文档摘要prompt约500字max_tokens256、一组代码生成prompt约200字max_tokens1024。跑压测时直接切CSV文件不用改脚本。6.2 QPS与成本模型单卡吞吐、单位Token成本与扩缩容决策最后一步把QPS数据换算成业务语言。假设你测出来单卡部署的7B模型稳定QPS是12每天可以处理约103万次请求12 QPS × 86400秒 × 70%有效时间留出余量。如果业务预估峰值需要50 QPS那就是需要5张卡左右才能支撑这还没考虑冗余。如果同时每个请求平均输出200 token那每秒要生成的token数是12×2002400 tokens单卡吞吐约2400 tokens/s——这个数字是模型和显存规格共同决定的如果压测时GPU利用率已经95%以上想再提升QPS就不是加并发能解决的了得从模型量化、批处理大小、推理框架调优下手了。我在实际做容量评估时都会把QPS消费成两个维度一个是每卡QPS单卡能撑多少并发请求一个是每QPS成本硬件成本、电费折算成单请求成本。有了这两个数扩缩容决策就完全数据化了不用拍脑袋。7. 结尾的实战经验补充说实话大模型QPS压测这项工作脚本搭建只占三分之一的精力剩下三分之二都在“校准口径”和“排坑”上。我个人走了很多弯路之后的一个体会是压测之前一定先花半天时间把接口文档读透、把流式响应的格式吃干净再动手写脚本。很多看起来神奇的“测试数据异常”最后都能在接口返回格式或客户端设置上找到答案。另外一个想重点提醒的是压测期间一定要实时盯着GPU显存的服务端日志别只盯着JMeter的曲线。JMeter曲线再漂亮服务端已经OOM重启了那你压出来的就是一段残废数据。我每次压测都会开一个终端持续跑watch -n 1 nvidia-smi顺便挂一个tail -f看服务端日志这两条命令比任何性能分析工具都直接。最后如果你从头到尾把脚本跑通了你会发现用JMeter测大模型QPS这件事跟测普通接口的核心区别就两条流的处理方式和超时设置。把这两点拿捏住剩下的事情其实都是通用压测方法论。这套方法目前在我这边已经从单模型验证扩展到了多模型对比压测——同样的脚本换不同的model名称和部署后端就能横向比较不同模型服务的性能差异。对团队选型来说这种横向对比数据比厂商海报上的宣传数字可靠一万倍。希望这篇内容能帮你少踩一些我踩过的坑。如果你在实操中遇到聚合报告数据跟预期差异很大的情况不用急着怀疑脚本先检查一下是不是服务端的并发限制或显存问题八成都是这两个地方出的岔子。
返回列表