1. 为什么AI模型推理会卡在“多线程”上
先说个真实场景。上个月我们团队要上一个大模型推理服务,模型已经用TensorRT优化过,单张卡上看延迟能压到几十毫秒。可一旦开放给内部十几个业务方同时调用,整个服务就像被堵住一样,请求排队,RT从几十毫秒直接飙到好几秒,最后还时不时超时报错。当时大家都懵了:模型明明很快,GPU利用率也不高,怎么整体表现这么拉胯?
这个问题的根子,恰恰就出在“多线程”上。AI推理服务从来不是一个单独在干活的东西。你从客户端发请求,服务端要接收HTTP连接、解析请求体、做数据预处理,然后把请求送到推理引擎去跑模型,跑完后还得做后处理、序列化返回。这一整条链路里,涉及服务端框架的线程池、推理框架内部的调度线程、GPU执行任务的队列,甚至还有客户端发起请求时自己的线程模型。任何一个环节的并发处理能力跟不上,都会把整条链路拖垮。
很多人以为性能测试就是拿工具怼流量,跑到服务报错就完事。但实际上,AI推理场景下的多线程性能测试,真正要回答的是三个问题:
- 这个服务当前架构能撑住多大的并发压力,也就是最大吞吐量在哪?
- 并发上来之后,延迟的稳定性如何?P99会不会冲上天?
- GPU、CPU、内存、线程池这些资源,谁先到瓶颈?是不是存在大量线程在那里空转或者互相等待?
只有把这些问题的答案量化出来,你才知道接下来该优化谁。是加GPU卡,是调线程池大小,还是改推理框架的batch策略。否则就是盲人摸象,今天调个这,明天调个那,钱花了效果没出来。
这篇文章就围绕这个主题展开。我会完整拆解一次AI推理服务多线程性能测试的思路和实操过程,包括用什么工具压、请求场景怎么设计、出来的数据怎么看、线程池和推理参数怎么调,以及我在实际测试中踩过的一堆坑。无论你是算法工程师想把模型部署做得更稳,还是后端开发在排查接口突然变慢的问题,或者测试同学正在研究怎么对AI服务做压测,这篇内容应该都能给你一些直接能用的参考。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 测试思路与场景设计:先想清楚再动手
2.1 用什么工具去压AI推理服务
目前用来压AI服务的主流工具其实就那几类:JMeter、wrk、k6,以及自己写Python并发脚本。我在这次项目中主力用的是JMeter,原因很简单:团队里其他同学也要参与测试,JMeter有图形界面,配置线程数、请求体、断言、聚合报告都比较直观,而且新增一个HTTP采样器就能直接打到推理服务的REST接口上,不需要额外写代码。
如果你对JMeter足够熟,整个过程其实就三件事:配置线程组、配置HTTP请求采样器、配置监听器。线程组里设置并发线程数和循环次数,HTTP请求里填服务地址、请求方法、Body数据,聚合报告里看吞吐量、平均响应时间、错误率。
如果追求更高的压测上限,或者需要复杂的请求间依赖关系,我建议用Python写脚本配合concurrent.futures里的ThreadPoolExecutor来压。这种方式灵活度更高,可以自定义请求内容生成策略,也方便把结果直接写成CSV做后续分析。我后面对比过,单个JMeter压测机在高并发下可能会因为自身线程调度开销影响结果,但Python脚本手动控制线程模型会更直接一些。对一般场景来说,JMeter已经够用了。
工具层面上还有一个值得提的点:压测机本身一定要和服务端分开部署。我自己就犯过这个错,一开始直接在开发机上跑JMeter,CPU被压测客户端吃满,结果服务端还没到瓶颈,压测机自己先卡死了,测出来的数据毫无参考价值。
2.2 JMeter测AI服务的配置步骤与参数说明
以下是我实际用JMeter压测一个基于HTTP的AI推理服务的配置方式,可以直接参考。
先说线程组。我通常会建三个不同场景的线程组:
- 冒烟测试线程组:1个线程,循环1次。目的是确认请求通路正常,模型不会因为输入格式问题报错。
- 阶梯加压线程组:线程数从5、10、20、50逐步递增,每个梯度持续30秒。目的是摸清服务在不同并发下的表现变化,找到延迟拐点。
- 峰值稳定性线程组:固定一个目标并发数,持续压5到10分钟。目的是看服务在持续压力下有没有内存泄露、连接池耗尽、响应时间逐渐恶化的问题。
线程组的核心参数是线程数、Ramp-Up Period和循环次数。比如你想模拟30个并发用户同时打请求,就设置线程数为30,Ramp-Up设为10秒,意思是30个线程在10秒内陆续启动完毕,而不是一开机就瞬间并发30个请求,后者会造成初始冲击,把服务瞬时报垮,结果并不真实。
