
“模型跑通了最多能扛多少并发”这是我被问到最多的一句话。训练团队把精度调到满意后转交给我从这天开始我的工作就从“看loss曲线”切换成“看显卡曲线”。做AI模型并发推理架构设计本质上是在算力、显存、时延和成本四堵墙之间找一条缝穿过去。这篇文章把我最近半年做推理服务化、并发优化的完整思路写出来包括资源怎么算、架构怎么搭、压测怎么排雷以及边缘设备上另外一套玩法。适合正在做模型部署、推理服务优化、GPU资源管理的工程师参考。1. 推高并发前先分清“并发数”和“QPS”很多刚接触推理服务的同学上来就问“能支持多少并发”但并发数只是一个表象指标它和QPS、时延是三个彼此咬合的概念。如果没把这三者的关系理清楚后面做架构设计和资源测算都会偏差。1.1 三个数字的真实含义并发数是某个瞬间系统里“在途”的请求总数包含正在等待调度的、正在GPU上计算的、正在把结果写回客户端的。QPS是系统每秒能完成的请求数量衡量的是吞吐能力。**时延RT**是从客户端发出请求到收到完整响应的总时间。用生活场景来类比银行网点里并发数等于排队的人加上正在柜台办业务的人QPS是每秒能送走多少个客户时延则是每个客户从取号到办完离开的耗时。三者之间有一个非常实用的关系式并发数 ≈ QPS × 平均时延秒举个例子目标QPS是50平均每次请求耗时200ms那么在途请求大约是 50 × 0.2 10 个。如果业务方张嘴就要“支持1000并发”你第一件事不是算显存而是反问两个问题单次请求目标RT是多少期望的QPS又是多少这两个数字没有对齐之前1000并发这个数字本身就没有工程意义。1.2 并发能力受三堵墙制约推理服务并发上不去从来不是单一原因我习惯把瓶颈分成三类排查时挨个对照。算力型瓶颈GPU上的SM单元流式多处理器或者张量核心已经满载此时就算把并发从10加到20QPS几乎不再上涨反而因为排队把RT拖长。这种瓶颈看nvidia-smi里的GPU-Util就能发现它长期卡在95%以上。显存型瓶颈这是大模型场景最典型的拦路虎。模型权重、KV cache、激活值、推理引擎的临时缓冲区全部挤在显存里。当batch size增大到某个临界点显存直接溢出或者触发了框架内部的显存换出推理速度断崖式下跌。LLM服务里OOM的报错日志、共享GPU场景下其他任务的显存抢占都属于这类。系统开销型瓶颈GPU根本没吃饱但CPU先撑不住了。tokenizer的文本处理、请求的序列化和反序列化、Python侧的GIL锁竞争、网卡中断哪一个超过水位线都会让GPU空转等数据。我在生产环境见过一台GPU利用率只有30%但CPU跑满的服务问题就出在预处理阶段成了瓶颈。这三个瓶颈会随并发升高依次或交叉出现排查时必须同时盯住GPU利用率和CPU使用率不能只看单边指标。1.3 推理与训练在并发上的本质差异训练阶段谈并发通常指数据并行、张量并行这类集群内协同方式因为它处理的是一个固定规模的训练批次。推理阶段完全不同请求是随机到达的长短不一、类型不同系统必须在极短时间内决定是等一等攒批、还是立刻处理。更重要的是训练任务容忍小时级延迟推理却要求毫秒到秒级响应任何多余的排队都必须有明确的目的。理解了这些底层差异再看资源测算和架构设计思路就不会跑偏。2. GPU资源测算从业务指标倒推显卡数量买显卡不靠拍脑袋靠的是“业务指标 → 单卡能力 → 卡数 → 显存校验”这条链路。每个环节都有经验系数我把完整的测算过程和踩过的坑写出来。2.1 算力维度先测单卡基准数据做资源测算的前提是先有一个基准数据**单卡在batch1时一个请求的端到端时延是多少。**这个数据必须实测不能看论文或者宣传材料。把模型用目标框架TensorRT、ONNX Runtime、vLLM、TGI等转换完毕之后单独跑几百个请求取P50时延。假设测出来是40ms理论单卡最大串行QPS就是 1000 ÷ 40 25。但生产环境不能按这个上限规划因为请求不会均匀到达突发流量会导致排队排队时延会直接吃掉RT预算。所以我一般引入一个资源效率系数取值范围0.5到0.8。需要的卡数 目标QPS ÷ 单卡基准QPS × 资源效率系数举例目标QPS200单卡基准QPS25效率系数取0.6那么 200 ÷25 × 0.6≈ 13.3向上取整就是14张卡。如果用A100 80GB需要两台8卡机器如果对低时延有硬性要求P99必须控制在200ms以内效率系数还要再往下压到0.4到0.5因为排队是P99的最大杀手。2.2 显存维度大模型的KV cache吞噬量算力满足了显存可能先爆。以LLM为例显存占用分成三块模型权重、KV cache、推理引擎运行时缓冲区。模型权重好算7B参数、FP16精度大约是 7 × 10⁹ × 2 bytes ÷ 1024³ ≈ 13.1GB加上embedding和层结构实际大概14GB上下。KV cache的公式稍微复杂一点KV Cache ≈ 2 × 层数 × KV头数 × 头维度 × 序列长度 × 2 bytes以7B模型为例32层、8个KV头、每个头维度128序列长度2048时每个并发请求的KV cache大约是 2 × 32 × 8 × 128 × 2048 × 2 ÷ 1024² ≈ 64MB。想想看100个并发请求就是6.4GB。这就引出了一个关键结论**模型权重决定了一张卡能不能装下模型KV cache决定了这张卡能同时服务多少并发。**在64GB显存的卡上14GB权重 预留10GB运行时缓冲区后KV cache可用空间40GB按每请求64MB算大约能支撑600个请求的KV cache但算力能不能处理这么多请求是另一回事。显存和算力哪个先到上限哪个就是当前并发能力的约束条件。2.3 卡型选型对比不同业务的推理负载差异很大我按当前的卡型做了一个选型对照表供参考卡型显存适合场景并发特点典型约束T4 16GB16GB轻量CV模型、小batch预估吞吐一般显存和算力都偏弱L4 24GB24GB中等CV模型、1-7B模型适中功耗低但算力上限可见A10 24GB24GB7B模型量化部署适合中等并发KV cache空间有限A100 40/80GB40/80GB7B-13B模型生产级部署高并发成本高H100 80GB80GB70B级以上模型很高成本极高4090 24GB24GB开发调试、小规模私有化中等消费卡缺少企业级特性需要特别提醒的是消费级显卡虽然性价比高但在生产环境要谨慎。缺少ECC显存纠错、显存温度控制能力弱、长时间满负载运行稳定性存疑这些在7×24小时服务里都是隐患。2.4 预留冗余和扩容阈值资源测算永远要有安全边际。我在实践中习惯预留20%到30%的算力冗余用于应对突发流量和机房故障时的流量切换。扩容触发线一般在指标到60%到70%水位时启动而不是等到打满再扩。报警规则建议同时设三个GPU利用率超过85%持续5分钟、P99 RT超过目标值持续3分钟、排队请求数持续超过并发上限的一半。三个条件满足任意两个就触发扩容。3. 推理架构的典型形态从单实例优化到推理池并发推理架构不是一上来就上K8s和队列中间件它是分层的。我的经验是先从单实例榨干性能再做多实例扩展最后才是重调度。每一步有各自的侧重点。3.1 单实例内的性能优化动态batching与连续批处理同一个模型单卡部署不同并发配置下吞吐差距可以达到数倍关键在推理引擎的执行方式。**动态batching攒批**是基础中的基础。GPU是并行架构算力是固定的处理1个请求耗时40ms处理4个攒在一起的请求可能只要70ms。虽然单请求时延变长了但整体吞吐翻倍。云厂商的推理服务基本都内置了动态batching自研服务要注意攒批等待时间不能设太长否则低流量时延会恶化一般等待时间上限5到20ms比较合理。**连续批处理continuous batching**是我做LLM推理时最推荐的优化手段。传统静态batching要等一个批次里所有序列全部生成完毕才释放资源但LLM的请求天然长度不一短请求很快结束长请求占着坑后面新请求只能排队。连续批处理让计算粒度细化到每个token每步forward做完完成的序列立刻退出新的序列随时加入GPU利用率大幅提升。vLLM、TensorRT-LLM、TGI这些引擎都实现了这个机制做LLM推理服务时建议直接用这些框架不要自己在PyTorch上裸写调度。除了引擎还有一个容易忽略的点**实例内部的并发线程数不是越大越好。**Python的GIL决定了CPU密集的tokenizer预处理不能靠多线程并行异步IO可以扛高并发连接但计算密集部分终究要落到C推理引擎上。工作线程超过物理核心数后上下文切换开销增加性能反而下降。3.2 多实例水平扩展从负载均衡到自动伸缩单实例性能到顶后横向扩展是提升并发能力最直接的手段。推理服务水平扩展的关键是把服务做成无状态的所有请求都落到统一的预处理入口实例之间不共享任何状态这样任意请求可以被任意实例处理负载均衡才能均匀。负载均衡层的选型我通常分两类来看。简单场景直接用Nginx或Envoy按轮询或最少连接数分发请求。K8s环境里用Ingress Controller配合HPAHorizontal Pod Autoscaler按QPS、P99 RT或GPU利用率三个指标自动扩缩容。GPU资源的分配方式这里是个容易踩坑的点。我强烈不建议在推理场景做细粒度的vGPU切分除非有MIG这样的硬件隔离机制。原因很简单推理请求对显存和算力的需求是突发的多个Pod共享一张卡任何一个Pod的显存峰值都可能把其他Pod挤掉。整卡分配虽然浪费一点资源但换来的是稳定性和可预期的性能。A100/H100的MIG切分可以给多路轻量推理服务做隔离但要注意切换配置需要重启GPU。3.3 高并发下的反压、限流与调度流量突然冲过来时系统要做的最重要的事不是“多扛一点”而是“优雅地拒绝”。没有限流层的推理服务会在流量高峰时因为队列无限堆积而把RT拖到几十秒然后客户端超时重试重试又堆进来雪崩就是这么形成的。我设计高并发推理链路时通常会放三个保护层限流层放在最前面使用令牌桶或固定窗口算法按每秒放行的请求数做控制。这个数字取自压测得到的系统能力上限超过就返回429或者走降级逻辑。队列层用来平滑瞬时尖峰。轻量场景用进程内队列即可重流量场景用Kafka或者Pulsar这类消息中间件做异步解耦。队列的长度和消费速度需要专门监控队列堆积是系统即将过载的前兆信号。调度层处理两类不公平问题。一类是大请求和小请求混跑时大请求算力开销巨大会让小请求长时间得不到GPU。另一类是LLM场景的prefill和decode混跑长输入的大请求在prefill阶段占用大量算力后面那些短请求的decode一直被阻塞。解决思路是按请求类型拆成多个优先级队列调度器优先服务低延迟需求的请求同时限制大请求占用的资源上限。3.4 我常用的推理服务架构快照图上这套架构我复用过多次各组件职责很清晰客户端 → API网关鉴权、限流、超时控制→ 负载均衡Nginx/Egress→ 推理Pod预处理Worker 推理引擎 后处理Worker→ 监控体系Prometheus GrafanaAPI网关的价值是统一做限流和身份校验负载均衡负责流量分发推理Pod内部的预处理Worker和后处理Worker独立跑线程池避免阻塞推理引擎。监控体系里除了常规的QPS和时延还要有GPU利用率、显存峰值、队列深度这几个维度。这套结构的好处是每一层都能独立扩展瓶颈定位也很快。4. 压测并发架构合不合格跑一轮才知道架构设计得再漂亮没有经过真实压测的并发能力都是薛定谔的数字。压测的目的不是“测出上限”而是“找出系统在哪里开始崩溃、为什么崩溃”。4.1 压测工具选择我试过几款主流的压测工具按场景区别选择工具适用场景优点缺点JMeter团队统一压测平台界面友好、分布式压测成熟、报告丰富高并发下自身资源占用偏高wrk单机高性能压测极轻量、C语言级性能脚本表达能力有限k6云原生CI/CD集成场景脚本化、云端执行灵活学习曲线略陡自定义Python脚本复杂业务协议压测完全可控、可注入业务逻辑需要自己处理并发模型JMeter是最容易上手的几百并发以内单机JMeter就能扛住。要注意JMeter压测机本身的CPU、内存、端口占用压力机自身成了瓶颈那测出来的数据就是假的。我曾经用JMeter跑5000并发目标的延迟没炸压测机的端口先被耗尽了报出来的数据一片混乱。后来改成分布式压测才拿到真实数据。如果只是快速验证接口的极限吞吐我推荐wrk一条命令跑起来wrk -t12 -c400 -d60s --latency http://你的推理服务地址这个命令用12个线程、400个并发连接压60秒结果会列出QPS、平均时延、P50/P99延迟分布。虽然它不能完全模拟真实用户的请求内容但作为第一轮摸底足够直观了。4.2 压测必须采集的七类数据一轮合格的压测不只记录一个QPS而是要同时采集以下指标并把它们放在同一条时间线上分析QPS每秒完成的请求数P50 / P95 / P99 时延分布错误率超时、5xx、连接拒绝GPU利用率、显存峰值CPU利用率区分用户态和内核态队列深度和排队时延网络连接数和TCP重传率四个关注点错误率必须为零才算合格哪怕0.1%的错误率在高峰期都意味着大量用户投诉P99比平均值重要得多平均RT好看但P99炸裂意味着有一批用户一直在忍受卡顿GPU利用率要跟QPS曲线放在一起看如果QPS上涨而GPU利用率不动说明瓶颈在别处压测结束后的恢复时间也很关键流量停止后系统多久能回到空闲状态反映了资源释放的效率。4.3 一次压测中的完整排查链路说一个我印象很深的案例。某次压测发现GPU利用率只有35%但目标QPS始终上不去加并发以后时延暴涨。第一反应以为是GPU算力不够但利用率这么低又说明GPU确实在闲着典型的“GPU等数据”现象。排查链路如下。第一步看CPU利用率。发现CPU单核打满其他核闲着这是典型的GIL串行化问题。进一步检查发现预处理流程把tokenizer的调用和请求解析写成了同步阻塞所有请求排队等同一个tokenizer线程。第二步看数据流。输入数据从网络栈到CPU预处理再到GPU显存其中有一个环节将numpy数组逐个转成torch tensorPython循环严重耗时。这一步在高并发下被放大。第三步做局部验证。把预处理并行度从1调到8GPU利用率立刻从35%升到70%QPS翻了接近一倍。这个案例的教训是推理性能优化不能只盯着GPU要把它当作一个端到端的数据管道来看CPU侧预处理效率数据搬运路径、后处理逻辑任何一个环节都可能成为并发天花板。我后来干脆把预处理和后处理做成独立的异步流水线输入数据以批量形式进入、批量输出GPU的空转时间大幅减少。4.4 压测中常被忽略的四个细节**热身期必不可少。**GPU推理引擎首次运行时需要加载权重、编译kernel、map显存前几十个请求的耗时可能远高于稳态。压测前至少跑1到2分钟低并发热身再开始正式记录数据。**长尾请求不能截断。**压测程序的超时设置太短会导致超时请求被标记成错误但实际还在服务端处理服务端队列越积越长。要区分“客户端超时”和“服务端排队”两个概念压测结果里这两类要分开统计。**压测数据要分梯度。**不要一上来就打到上限而是按50并发、100并发、200并发、400并发梯度递增每个梯度跑5分钟记录落点。这样能清晰看出系统从健康到过载的拐点这个拐点就是容量规划的基线。**监控要落到进程级。**容器化部署下只监控Node级别的GPU指标不够每个Pod的显存和算力使用都要单独拉出来看才能定位是哪路业务把集群资源吃掉的。5. 边缘设备并发推理另一套游戏规则云端推理的“多卡扩容”思路在边缘设备上完全行不通Jetson Orin这类设备上做并发推理要重新理解“并发”的含义。5.1 边缘端的三个硬约束第一显存和内存共享。Orin的GPU和CPU共享同一块物理内存没有云端那种独立显存。看似显存很大但GPU跑了几个推理实例后整个系统的可用内存都会被吃光甚至触发系统级OOM。第二功耗与散热的硬上限设备的功耗墙和温度墙决定了不能持续满负载运行。第三CPU和GPU抢内存带宽模型加载到GPU核心里计算但数据从内存搬运到GPU的带宽是固定的CPU侧的预处理开销会直接偷走GPU可用的带宽。5.2 边缘端并发如何估算边缘端没法靠多副本扩展所以要精确计算单设备能撑起多少并发。以Jetson AGX Orin 64GB部署llama.cpp跑7B Q4_K_M量化模型为例Q4_K_M量化后的7B模型权重约4GBOrin的内存带宽约204GB/s。推理的瓶颈主要在显存带宽LLM每个token的生成都需要把整个模型权重扫描一遍。理论单次推理的最短耗时大约是 4GB × 2 ÷ 204GB/s ≈ 39ms也就是单用户大约25 token/s。如果要求每个用户至少10 token/s这台设备同时服务2个用户就已经到带宽上限了如果只要求5 token/s并发可以放到5个左右。这个估算告诉我们一个反直觉的结论**边缘端的并发能力取决于你愿意为每个用户牺牲多少响应速度。**云端讲究的是“多少并发都行”边缘端必须明确“每个用户分配到多少资源”。5.3 量化是边缘并发优化的第一杠杆量化对边缘端并发能力的提升效果惊人。同样是7B模型FP16权重约14GBQ4_K_M量化后约4GB内存带宽占用降到原来的28%等效并发能力提升了3.5倍。llama.cpp的命令行里-c参数控制上下文长度它会直接影响KV cache的占用每个token的KV cache大约是几百KB上下文越长能同时处理的用户越少。我实际用Orin跑llama.cpp时的推荐配置是./llama-cli -m ./models/7b-q4_k_m.gguf -c 2048 --threads 4 --batch-size 128--threads不建议直接拉满线程数超过物理核心数时推理时延反而上升。4到6个线程在Orin的CPU上跑7B模型是一个功耗和性能的平衡点。--batch-size控制prompt处理阶段的批大小128是常用值太大容易把内存占满。5.4 边缘端的“并发”选择稳定优于平均边缘设备和云端最大的不同在于它通常只服务于有限的固定用户比如一台工厂设备上的检测系统、一台车载终端。这种场景下我倾向于做一个非常简单的调度固定并发数1到2个请求排队每个请求独占设备直到推理完成。宁可让后来的请求等待也不要同时跑多个推理实例导致所有请求都变慢。优先级设计上实时控制类的请求永远优先于日志上报和状态查询。边缘端资源有限贪心是不可取的给每个请求一个明确的超时时间超时就直接失败返回不能让请求无限积压把设备拖死。结合我自己在边缘和云端两头跑的经验还有一个实用的习惯值得建立每次架构调整后都留一份带时间戳的压测基线记录下次调优直接拿出来对比。并发推理调优不是一次性工程它更像是在性能、成本、稳定性之间做连续的微调。希望这篇内容能让你在踩坑之前先把路看清。