AI推理本身并不需要模型服务层。一个Python程序完全可以加载模型,调用CUDA,再把输入Tensor送进GPU完成推理。对于离线任务、研发实验、单用户工具甚至某些嵌入式应用来说,这种方式反而简单直接。真正让模型服务层变得重要的,是AI推理从一个程序内部的计算函数变成一个长期运行、需要同时处理大量请求的在线服务之后,问题开始从“GPU能不能算”转变成“几十、几百甚至更多请求如何共享有限的GPU计算和HBM资源”。
这一区别非常重要。模型服务层并不是在CUDA和GPU之间再插入一个必须经过的神秘中间层,也不是因为应用程序不能直接访问GPU。CUDA Runtime、CUDA Driver、PyTorch、TensorRT等软件本身就负责应用程序与GPU之间的计算接口。模型服务层位于更高层,它主要负责把一个模型变成可以被大量客户端稳定调用的服务,并管理模型实例、请求生命周期、批处理、并发、排队、限流、路由、健康状态和监控。
对于单次推理来说,应用程序通常只需要完成模型加载、输入准备、GPU计算和结果返回。如果程序启动时把模型加载到GPU,模型可以长期驻留在HBM中,后续请求只需要重复使用这份模型权重,并不存在每次请求都重新加载模型的问题。模型服务层解决的是另一类问题:当同时有100个请求到来时,是让100个请求各自启动一次推理,还是建立请求队列,把不同请求组织成更适合GPU执行的工作批次;当一个请求需要几秒钟生成几千个Token时,如何让其他请求继续使用GPU;当GPU已经达到容量上限时,新的请求应该等待、排队、限流还是转发到另一台机器。
LLM推理尤其容易暴露这个问题,因为它并不是传统意义上的“一次输入,一次计算,一次返回”。生成式模型通常包含prefill和decode两个具有不同计算特征的阶段。Prefill阶段需要处理输入上下文,计算量较大;decode阶段则通常逐步生成Token,每一步都需要读取和更新与请求相关的KV Cache。当几十个请求同时处于不同的生成阶段时,如果仍然按照传统的静态batch方式处理,很容易出现GPU计算资源没有被充分利用的问题。
因此,大型语言模型服务经常采用continuous batching,也就是动态地把正在运行的请求组织到GPU计算批次中。一个请求完成以后,可以把新的请求加入后续计算,而不是等待整个固定batch全部结束。这种调度能力并不是GPU硬件自动提供的,也不是普通的模型推理函数天然就能完成的,它需要在服务运行时维护请求状态、KV Cache、batch组成以及调度策略。vLLM等推理系统之所以受到广泛关注,核心能力之一就是围绕这种动态请求调度和KV Cache管理建立了专门的运行时。
KV Cache本身也是模型服务层必须认真管理的一类资源。Transformer生成过程中,一次请求产生的历史Key和Value通常会被保留下来,避免每生成一个新Token都重新计算全部历史上下文。这样可以显著降低重复计算,但代价是每个活跃请求都会持续占用GPU内存。上下文长度越长、并发请求越多,KV Cache占用就越大。因此,一个模型可能在只有几个请求的时候运行正常,但当并发量突然提高时,即使模型权重完全没有变化,也可能因为KV Cache耗尽HBM而出现OOM。
这时就可以看到模型服务层与普通GPU程序之间的区别。普通程序当然也可以自己实现KV Cache管理,但如果企业需要长期维护大量模型和大量并发请求,就必须自己处理缓存分配、释放、碎片、请求取消、超时、batch调度和显存水位控制。模型服务框架把这些问题变成了一个已经实现好的运行时能力,应用程序只需要通过API提交请求,而不是每个业务开发团队都重新发明一套GPU调度系统。
显存管理也经常被误解。CUDA本身已经提供了GPU内存分配机制,PyTorch等框架也拥有自己的memory allocator,因此不能说“应用程序直接调用GPU就没有显存管理”。真正的问题是业务应用通常不应该让每个请求独立决定大型模型实例如何加载和占用GPU资源。假设同一台服务器有一张80GB HBM的GPU,模型权重已经占用60GB,剩余空间还需要容纳KV Cache和运行时workspace。如果多个业务进程各自尝试加载同一个模型,很可能直接把同一份权重重复放进GPU,结果不是GPU计算能力不够,而是内存容量被重复占用。
模型服务通常让模型实例成为长期驻留的资源。多个客户端请求进入同一个服务实例,服务进程复用已经加载到GPU的模型权重,并根据当前HBM水位决定能够接受多少并发请求。这种架构不仅减少重复加载,也让GPU资源从“每个程序自己的资源”变成“服务实例统一管理的资源”。
不过,模型服务层并不能凭空解决HBM容量不足。如果模型本身就无法装进GPU,服务层不会 magically 把80GB变成160GB。此时仍然需要量化、模型分片、Tensor Parallelism、Pipeline Parallelism、CPU offload或者其他内存层级方案。模型服务层做的是把这些能力组织成可运行的服务实例,并在请求层面管理它们。
多GPU推理也是如此。并不是所有多GPU推理都需要AllReduce。具体通信方式取决于模型并行策略。Tensor Parallelism可能需要频繁的AllReduce、AllGather或其他collective communication;Pipeline Parallelism的主要通信模式又不同;某些模型可以采用专家并行、数据并行或者其他架构。直接把“多GPU推理”与“AllReduce”画等号,会掩盖不同并行策略之间非常重要的工程区别。
GPU之间的通信性能同样取决于硬件拓扑。NVLink、NVSwitch和PCIe并不是简单的“速度不同的三根数据线”,GPU服务器内部的拓扑结构决定了不同GPU之间的数据应该经过什么路径。对于Tensor Parallelism来说,如果一个Transformer层需要频繁交换中间结果,GPU间通信延迟和带宽可能直接影响整体推理吞吐。模型服务系统需要知道GPU拓扑,并选择适合的并行配置,而不是简单地看到服务器里有8张GPU就认为拥有8倍单卡性能。
CPU与GPU之间的数据传输也需要重新理解。Pinned Memory并不是一种“显存交换”。Pinned host memory本质上仍然属于CPU主机内存,只是页面被固定,不允许操作系统随意换出,在CPU与GPU进行异步数据传输时通常能够提供更好的条件。GPU无法把普通系统内存当成与本地HBM完全等价的高速工作内存。大量依赖CPU内存offload时,实际性能仍然受到PCIe或其他主机互联带宽以及数据访问模式的限制。
因此,模型服务层的任务之一是尽量避免没有必要的数据搬运。对于一个已经驻留在GPU中的模型,如果每个请求都导致大量CPU与GPU之间的数据往返,即使GPU本身拥有很高的Tensor Core计算能力,也可能出现计算单元等待数据的情况。专业性能分析不能只看GPU利用率,还应该结合HBM带宽、PCIe吞吐、CPU利用率、请求队列长度、Token生成速度以及batch大小一起判断瓶颈在哪里。
服务层另一个非常重要的功能是并发控制。GPU并不是一个可以无限并发的资源。对于LLM服务,增加并发请求通常可以提高GPU利用率,但并发继续提高以后,KV Cache会迅速扩大,排队时间也可能增加,最终让尾延迟明显恶化。于是服务系统必须在吞吐量和延迟之间寻找合理工作点。平均延迟可能看起来不错,但P95、P99延迟已经失控,这对在线API服务往往比平均值更加重要。
这也是为什么一个“GPU利用率95%”并不一定代表系统优化得很好。如果GPU已经长期处于满负载状态,但请求队列不断增长,P99延迟不断恶化,那么服务实际上已经过载。反过来,如果GPU利用率只有40%,也不一定说明GPU浪费严重,可能是请求量不足、模型属于低计算密度工作负载、CPU预处理成为瓶颈,或者服务为了控制尾延迟主动限制了batch大小。性能指标必须结合业务SLO分析,而不能只盯着一个GPU Utilization百分比。
多租户环境又增加了一层复杂性。当一台GPU服务器同时运行多个模型或者多个业务时,需要解决的已经不只是显存分配,而是GPU计算资源、显存、进程、权限和故障域的隔离问题。NVIDIA MIG等硬件能力可以在特定GPU上提供硬件级的资源分区,但MIG并不是所有GPU都支持,也不能简单等同于通用GPU虚拟化。软件层还可能使用容器、CUDA环境、调度器以及Kubernetes等组件完成资源管理。究竟使用哪种隔离方式,取决于GPU型号、工作负载以及对隔离强度的要求。
Kubernetes在这里也不能和模型服务层混为一谈。Kubernetes负责容器编排、调度和服务生命周期管理,GPU Operator负责把GPU相关驱动和Kubernetes资源能力接入集群,而vLLM、TensorRT-LLM等更接近模型运行时。它们可以组合起来,但解决的是不同层次的问题。一个完整的生产系统可能由API Gateway、模型服务、模型运行时、容器平台、GPU调度、监控系统以及底层GPU服务器共同构成。
自动扩缩容同样不是“GPU Operator根据请求自动增加GPU”的简单机制。Kubernetes HPA主要针对Pod副本数进行扩缩容,是否能够有效用于AI推理取决于服务暴露的指标以及底层GPU资源是否已经准备好。GPU服务器本身属于昂贵资源,从云平台申请GPU实例、启动节点、安装或初始化环境、拉取容器镜像、加载几十GB甚至几百GB模型,都可能需要明显时间。因此,面对突发流量,单纯依靠HPA在请求已经爆发以后才开始扩容,可能来不及解决延迟问题。生产环境往往需要结合队列长度、并发请求数、Token吞吐、GPU利用率、节点预留以及容量预测共同设计。
故障处理则是直接调用GPU与服务化架构之间的另一个区别。GPU驱动崩溃、CUDA context异常、Xid错误、显存ECC错误、GPU掉线或者节点故障,都可能使一个正在处理的请求失败。单个程序当然可以自己实现重试和故障转移,但到了生产环境,通常还需要健康检查、实例摘除、请求重试、负载均衡、熔断、日志、指标和分布式追踪共同工作。一个GPU实例发生故障以后,系统应该停止向它发送新请求,并把服务流量转移到其他健康实例,而不是等待业务程序自己发现问题。
不过,模型服务层也不是越复杂越好。如果只是一个研究人员在本地运行一个7B模型进行测试,直接使用PyTorch加载模型然后调用GPU完全合理。为了一个单用户脚本搭建Kubernetes、服务发现、自动扩缩容和多租户系统,成本远高于收益。真正需要模型服务层的场景通常是模型成为共享基础设施以后,包括多个应用共同调用同一个模型、需要高并发API、需要稳定SLO、需要GPU资源共享、需要动态批处理、需要多模型部署以及需要故障自动处理等。
从工程架构角度看,模型服务层的价值并不是“替应用程序调用GPU”,而是把GPU推理从一次计算变成一种可调度、可监控、可扩展的基础设施能力。应用程序只需要提交输入并等待结果,而服务层负责决定哪个模型实例处理请求、什么时候执行、是否进入batch、需要多少KV Cache、是否需要排队以及出现故障后应该怎样处理。底层运行时再负责把这些计算转换成CUDA kernel、Tensor Core操作以及GPU之间的通信。
最终,AI推理系统的性能也不能归结为一张GPU或者一个模型框架的速度。一个H100如果长期等待数据,它不会因为理论算力很高就自动变快;一个拥有巨大HBM的服务器,如果请求调度和KV Cache管理混乱,同样可能在高并发下迅速失去性能。AI服务真正复杂的地方,是把模型、GPU、HBM、CPU内存、GPU互联、请求调度和业务流量组织成一条稳定的数据和计算路径。直接调用GPU解决的是“这一次计算怎么完成”,模型服务层解决的则是“当成千上万个计算请求同时到来时,这套GPU基础设施怎样持续工作”。
AI 推理为什么需要模型服务层,直接让应用程序调用 GPU 会遇到哪些问题
喜欢这篇报道?
使用下面的功能,方便以后继续阅读和分享 MNewsTV
关于文章收藏
收藏不需要注册帐号,收藏信息仅保存在当前浏览器中。
删除收藏请进入「我的收藏」进行管理。
捐助(Paypal): https://www.paypal.me/observeccp 订阅中国观察电报 Telegram : https://t.me/s/ObserveCCP