ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

异构算力池化实战:如何将GPU利用率提升1.3倍

异构算力池化实战:如何将GPU利用率提升1.3倍 我最近在一家做AI视觉检测的企业里帮忙梳理训练平台看到监控面板上满是让人尴尬的数字集群里插着几十张高端的GPU卡但单卡平均利用率很长时间连20%都摸不到有些甚至一整天都在跑空的空闲等待。行业的普遍认知是GPU贵、缺货、要省着用但真到了调度层面大家用的还是最原始那套——“一张卡绑一个活”资源切不开、异构卡调不动、排队全靠运气。也是在这个背景下我开始认真研究基于ZStack AIOS这类智算操作系统去做异构算力池化与调度最终在一个实际环境里把整组GPU的等效利用率提升了1.3倍。这篇就把整个思路、做法和踩过的坑一次讲清楚。这里说的异构算力不只是指NVIDIA全家桶还包括昇腾、海光、寒武纪这些国内主流的AI加速卡甚至Intel、AMD的GPU也该算进来。对很多企业来说手里不光有新旧两代N卡可能还有几台昇腾训练节点这种情况在智算中心、高校实验室和制造企业的AI部门里特别常见。ZStack AIOS这类产品的核心价值就是把这些规格不一、驱动各异、调度接口不同的算力统一纳管起来让上层应用看不出底下的区别让每一份显存和算力都被有效用起来。下面不再讲PPT上的概念直接拆一拆我们落地时实际遇到的问题和对应的解题路径。1. 为什么GPU利用率长期“虚高显示”算力碎片化的真实状态1.1 一张监控页背后的浪费清单我最早建议客户上调度平台不是因为大家不会用GPU而是因为那张监控页太触目惊心了。白天业务高峰期两张A100跑着训练显存用掉70%以上但SM算力波形却像心电图一样一下满载一下归零。到了晚上绝大多数卡直接空转风扇都懒得转。这种状态说出去是“我们买了高性能GPU”实际上有效产出非常有限。拆开看浪费主要出现在几个层面。第一个是任务与卡的绑定方式太粗糙一个Pytorch训练脚本默认吃满一张卡可很多脚本尤其是推理服务真实负载只有10%到30%。第二个是数据预处理、模型评估和人工标注这些环节根本不占GPU但任务排队时会把整卡锁住其他人想用只能干等。第三个是显存与算力不匹配小批量推理用不着整张A100的80GB显存可旧方式里只要进程起来卡就归你了别人碰不得。这些问题的根因不是运维懒而是缺少一个能把“卡”转换成“可分割、可调度、可计量算力单元”的中间层。传统的OpenStack或者基本KVM虚拟化方案里对GPU的支持通常停留在PCIe透传阶段一块卡要么整张给A虚机要么整张给B裸机根本没法细分。1.2 异构算力集群里的新麻烦驱动、厂商与接口各说各话如果只是NVIDIA一种卡的利用率低事情还简单些。真正让问题复杂化的是“异构”两个字。我手头这个客户的环境里既有NVIDIA A100也有几台昇腾910B还有两批老的T4用作推理。三批卡各有各的驱动体系NVIDIA靠CUDA生态昇腾有自己的CANN工具链T4虽然也是N卡但算力孱弱。想让一个训练任务透明地跑在不同卡上连软件适配这关就够喝一壶的。以前大家的常规做法是分集群N卡归N卡昇腾归昇腾谁也别碰谁的。这样管理省事代价是资源池被割裂成好几块每一块都“不够用”同时又“用不满”。某训练任务高峰期N卡集群排队两小时旁边的昇腾集群却有一半在闲着——因为那边没有配套的算法栈资源再多也干瞪眼。把异构卡统管起来第一步是解决“认卡”的问题。ZStack AIOS在这个环节做的事情说复杂也复杂说简单也简单它屏蔽了底层不同厂商的驱动差异向上提供一个统一的设备接入模型。你不需要为昇腾单独搭一套调度系统也不需要为N卡写专门的设备插件平台层会用一套抽象的“算力设备”概念把这些卡统一建模。对我们的运维团队来说最直接的感受是不用再盯着厂商各自的运维工具来回切换了。1.3 虚拟化这条路为什么走不通也有人说GPU利用率低上虚拟化不就行了如果是在五年前我可能也这么想。但实际做下来就会发现传统CPU虚拟化的思路搬到GPU上处处碰壁。CPU虚拟化靠的是指令集模拟和超分复用GPU的指令体系、显存带宽和内核调度机制完全不同强行搞虚拟化要么性能损耗大到不可接受要么兼容性一塌糊涂。NVIDIA自己提供的 vGPU 方案以及 AMD 的 MxGPU本质上都是厂商私有的硬件虚拟化方案性能是好但只认自家产品和“异构纳管”这个前提天然冲突。如果客户的集群里只有NVIDIA一种卡用官方vGPU确实够用但一旦引入昇腾或者其他加速卡统一调度就成了必须解决的问题。ZStack AIOS的解题思路不是去做硬件虚拟化而是站在更高的调度层做“算力切分”通过设备虚拟化能力和容器运行时的隔离机制把一张物理GPU划分成多个可独立调度的算力实例。每一份实例拿到的是真实的算力配额而不是模拟出来的假资源。所以不夸张地说异构算力池化的核心不是硬件也不是驱动而是中间那层调度逻辑。2. ZStack AIOS盘活异构算力的核心方法从“分配整卡”到“调度算力”2.1 先定资源模型算力也要像云主机一样分规格过去申请GPU资源是“给我一张卡”现在用ZStack AIOS的时候脑子要转个弯改成“我要一份算力”。这一转变是整个利用率提升的起点。为了说清这个模型我在客户的集群里遇到过一个很典型的例子一个目标检测推理服务峰值吞吐要求是每秒处理200张图片。我用工具实测过这样一个服务用T4卡跑需要分配大概4GB显存加一小部分算力如果用A100的切片去跑同样性能占用的资源更少但A100单价高老板不一定舍得。在旧体系里这种任务会把一整个T4卡占住显存用不到一半GPU计算单元更是大量闲置。而在算力池化之后我可以给它在同一张T4上开出多个“小T4”实例每个实例独立跑一个推理副本显存隔离算力按比例共享整卡利用率自然就上去了。在这个模型里ZStack AIOS重点抽象了两个维度。一个是显存它是硬隔离的任务能看到的显存大小是切死的防止互相挤爆。另一个是算力配额它对应的是GPU上的计算核心或流处理器占用比例用类似云主机CPU配额的方式做软限制。任务突发时可以借一部分算力但长期占用不能超过配额。这套做法对NVIDIA和昇腾都适用只是底层实现方式不同而已。2.2 设备插件与调度器K8s生态下的协调机制ZStack AIOS的调度底座没有另起炉灶而是站在Kubernetes的肩膀上。它的做法是通过自定义的Device Plugin向Kubelet上报设备信息然后由调度器根据节点的空闲资源和任务的资源需求做匹配。这个机制让我这种已经熟悉K8s的运维少走了很多弯路因为基本的Pod调度、亲和性、污点容忍这些概念都能直接用。这里面最有文章可做的是“设备上报粒度”。默认情况下一个节点如果插了8张A100Kubelet上报给调度器的就是8个完整设备。但在算力池化场景里如果平台支持把每张A100切分成若干个计算实例那么上报的设备数量就会变成几十个“小设备”每个小设备有自己的显存大小和算力权重。调度器看到的就是一个大池子池子里是密密麻麻的算力单元任务按需申请按匹配度分配。实际调试中我还发现一个容易踩的坑如果设备插件上报的实例数量太多调度性能会明显下降Pod调度一轮可能要等上好一会。所以合理控制每张卡的切分粒度很重要不是切得越碎越好。经验值是对A100这种卡单卡切分到4到8个实例是比较均衡的选择太细了调度开销大太粗了又回到整卡分配的老路。2.3 混布策略训练与推理共用一张卡的大胆尝试很多团队的既定印象是“训练和推理必须分开否则互相干扰”。这句话在资源充足时是对的但在算力紧张时属于奢侈。我在客户环境里尝试了训练和推理混布在同一张物理卡上前提是显存硬隔离、算力按比例切分。举个例子一张80GB显存的A100白天切开三份——50GB给一个中等规模的语言模型训练任务20GB给一个在线推理服务剩下10GB留作弹性缓冲。训练任务属于长期稳定占用型推理服务则是突发型早高峰时推理瞬间能冲到满负荷但平时只用一半不到。放在以前这两种任务根本不可能共存但现在通过算力配额机制训练任务不会把推理服务的突发请求饿死推理服务也不会在闲时白白浪费算力。我们持续监测了一周这张卡的总体利用率稳定在70%上下而过去一张卡跑一个训练任务时利用率只有35%左右。这个“1.3倍”就是这么一分一分抠出来的。当然混布有一个前提条件就是任务本身对延迟抖动不敏感或者推理服务有足够的副本冗余。真要跑那种对性能时延极其敏感的在线业务还是建议预留整卡别拿生产稳定性去赌节省出来的那点资源。3. 落地接入实操驱动、纳管、切分细节与验证过程3.1 驱动与固件版本核对异构纳管的第一步是“对齐”这一段展开讲具体操作方便大家直接对照自己的环境。首先无论你用的是哪种卡驱动版本和底层固件必须对齐到平台支持的兼容清单。我在现场吃过一次亏节点上有张T4的驱动版本过旧设备插件上报之后调度器始终不认为这张卡可用排查了很久才发现是驱动与CUDA运行时不匹配。后来统一升级NVIDIA驱动到535系列同时把昇腾节点的CANN工具链也升级到配套版本纳管才算顺利。建议先做一张表格把每批异构卡的型号、驱动版本、固件版本、映射的算力模板名登记清楚。这步虽然繁琐但后面排查问题时非常救命。批次卡型号驱动版本固件版本算力模板ANVIDIA A100 80G535.15492.00.01.00a100-50g / a100-20gBNVIDIA T4 16G535.15490.00.02.00t4-full / t4-halfC昇腾910B 32GCANN 7.024.1.rc1ascend-16g / ascend-8g表格里的“算力模板”先让平台管理员自定义后面会用到。3.2 在ZStack AIOS里创建算力模板显存、算力权重与隔离策略这一步是整个接入过程里最有技术含量的一环。找到AIOS控制台的“算力模板”或“GPU规格”菜单新建模板时需要填三个核心参数。第一个是显存大小必须是整数GB不能超过物理卡总显存第二个是算力权重我用一个简单的百分比表达比如100%表示整卡25%表示四分之一算力第三个是隔离策略选择硬隔离或共享模式。我给客户设计模板时遵循的原则是“推理服务走共享训练任务走硬隔离”。具体来说T4卡上的推理副本选共享模式显存各分4GB算力权重各占30%A100上的训练任务则用硬隔离50GB显存加70%算力权重确保训练过程的稳定性。设置完模板之后平台会自动生成对应的YAML描述可以保存下来作为后续运维复查的依据。验证模板是否生效不能只看控制台显示。我建议进到实际运行的Pod内部执行nvidia-smi或者昇腾的npu-smi info确认容器内看到的显存大小与你申请的模板一致。注意nvidia-smi里显示的是“虚拟显存”大小物理卡的总显存依然会完整显示在宿主机层这是正常的。我见过有些同事第一次看到这种显示差异误以为切分没生效来回折腾了一下午。3.3 调度策略配置让任务找到最合适的算力单元模板建好之后调度策略决定了任务会被放到哪一块算力上。ZStack AIOS支持配置多种调度策略包括优先级抢占、亲和性调度、装箱调度和拓扑感知调度。在一个实际生产环境里单一策略基本不够用我最后采用的是组合策略对于批处理训练任务使用装箱调度尽量把任务堆叠到少量物理卡上把空闲卡留给突发的高优任务。对于在线推理服务使用拓扑感知调度优先分配与数据面网络同节点的算力减少跨节点通信。对于需要多卡通信的分布式训练则必须开启“整卡组”模式把同一节点上的8张卡一起分配避免跨机通信拖垮带宽。组合策略的配置过程不复杂但有一个逻辑要先想清楚装箱调度和分布式训练是有冲突的。一旦你把一张卡切成多份那么这张卡上的多个小实例之间是互相独立的大模型的多卡训练任务不能直接利用这些小实例除非平台支持把这些实例重新聚合为一个逻辑设备。好在ZStack AIOS对多卡通信场景有特殊的整卡调度支持我在做大模型微调时就是走整卡模式推理和小规模训练才走切片模式。3.4 联调验证用一个测试任务验证切分与隔离效果配置完之后我习惯用一段极简的PyTorch代码验证每个切分实例是否真的独立、隔离是否有效。测试思路很简单在同一个节点上同时运行两个Pod各自申请同一个物理卡上不同算力模板然后在一个Pod里跑一个烧算力的循环观察另一个Pod的延迟波动。import torch import time # 模拟计算密集型任务 a torch.randn(4096, 4096, devicecuda) b torch.randn(4096, 4096, devicecuda) while True: c torch.matmul(a, b) torch.cuda.synchronize() time.sleep(0.1)如果硬隔离生效那么满载那个Pod的算力会明显高于另一个Pod如果共享模式生效两个Pod会抢算力体现在矩阵乘法速度上就是双双下降。测试下来硬隔离模式的算力干扰率控制在5%以内共享模式在负载不均时的抢算力现象也确实存在。有了这组数据后续给业务方写资源交付说明时更有底气。4. 利用率提升1.3倍的验证路径数据口径、测试方法与运维调优4.1 先搞清楚“1.3倍”是怎么算出来的讲到“利用率提升1.3倍”这个数字必须先厘清统计口径否则很容易被当成宣传口号。我在向客户汇报之前专门定义了两个指标。第一个指标叫“物理卡平均利用率”统计粒度是整卡看的是GPU计算核心在一段时间内的平均繁忙程度这是给老板看的总账。第二个指标叫“算力有效利用率”统计粒度是实际分配给任务的算力实例看的是真正被业务用掉的算力占总可分配算力的比例。前者反映硬件状态后者反映调度效率。在我们验证的场景里优化前8张A100的物理卡平均利用率约为31%。经过算力池化、切分和混布之后同样的物理卡上运行的推理服务实例从10个增加到了23个物理卡平均利用率提升到了约42%折算下来就是1.35倍约等于标题里的1.3倍。同时所有在跑任务的算力有效利用率也明显改善过去排队等GPU的作业数量减少了将近一半。需要特别说明的是这个数据不是跑一个benchmark测出来的瞬时值而是连续监测两周后取的平均值。测量工具用的是DCGMNVIDIA数据中心GPU管理工具加Prometheus昇腾那边用npu-smi自带的监控接口。监控数据采集间隔设为10秒更粗的粒度会把短时算力峰值平均掉导致利用率数字看起来比真实情况低。4.2 测试方法论怎么证明“提升”不是因为运气光有监控数据还不够我还设置了三组对照实验避免“提升”被其他因素干扰。第一组是“不切分、按整卡分配”的基线记录一周的平均利用率和任务吞吐。第二组是“仅切分、不混布”验证单纯切分带来的提升。第三组是“切分加混布”验证综合效果。三组数据放在一起结论就十分清晰单纯切分那一组利用率从31%提升到了36%左右提升主要来自显存资源的碎片化利用加入混布之后利用率进一步跳到42%。这说明1.3倍的提升不是某一项技术的功劳而是“切分释放空闲资源、混布填补资源碎片”这套组合拳打出来的。此外我还测了一个重要的业务指标推理服务的P99延迟。很多团队担心算力池化会拉高延迟但实测下来硬隔离的推理实例P99延迟只增加了不到3毫秒完全在业务容忍范围内。这个测量结果对说服业务方特别有用毕竟运维再怎么强调利用率业务方只关心自己的服务稳不稳。4.3 调优过程中踩过的三个坑第一个坑是显存碎片。混合部署一段时间后我注意到有张卡明明总显存还剩不少但新的推理任务却调度不上去了。排查后发现是显存分配和释放的碎片化问题——几个小实例分布在物理显存的不同地址段无法合并成一块连续的可用区域。解决方案是在共享模式下开启平台的内存整理功能同时调整实例申请策略优先把新任务调度到显存使用率低的卡上。第二个坑是库依赖冲突。同一个算力实例上跑不同框架版本的推理服务时CUDA和cuDNN依赖可能出现版本互斥。我们遇到过一个情况一个实例内的TensorRT版本升级后另一个实例里的老版本服务直接起不来了。后来规范了容器镜像统一在基础镜像里锁定CUDA和框架版本才把这个问题根治。第三个坑是算力抢占导致的“邻居噪声”。虽然硬隔离模式能限制大部分干扰但L2缓存和显存带宽是物理共享的。当实例A在做大规模卷积时实例B如果刚好也在做同样操作两者会在缓存层面互相挤占。缓解办法是错峰调度重要任务同时给关键业务配置更高的算力权重让它能在竞争时拿到更多缓存份额。5. 从“能用”到“好用”算力池化后续的扩展与治理思路5.1 配额管理与成本核算让业务方对算力有实感算力池化做好之后一个直接好处是每个业务方都能看到自己到底消耗了多少算力。我在ZStack AIOS里给不同项目组划分了配额比如算法团队的A项目组有40%的算力配额数据分析团队有20%剩下的留作公共弹性池。每个任务结束时平台会自动记录算力用量并折算成小时数月度汇报时直接导出一张账单。这个功能对推动内部资源共享特别有用。以前业务方总觉得“GPU不够用”是因为集群总量小现在看到自己的项目组长时间只用了配额的六成就不会轻易再提购买新卡了。有一个项目组甚至主动把闲置的夜间配额释放出来供其他组使用反正自己的任务白天跑完就行。这个转变是资源治理层面最实打实的收益。5.2 弹性伸缩与自动扩缩容利用率持续性提升的另一只手利用率提升不能只靠一次性配置还要靠动态伸缩来维持。AIOS本身可以对接监控数据配置弹性策略当推理服务实例的GPU用量连续5分钟超过80%时自动扩容出一个新的算力实例当用量连续半小时低于20%时自动缩容回收。这个策略上线之后我们不再需要人工盯着监控去扩缩容省了很多夜间值班的精力。自动伸缩要注意一个细节扩容时机不能太灵敏。有一次我把阈值设成连续1分钟超过70%就扩容结果下午业务波动时实例一下子多了好几个隔几分钟又缩回去调度器来回折腾得够呛。后来改成5分钟窗口加10%的缓冲裕量情况才稳定下来。5.3 面向大模型微调场景的特殊处理建议最后想单独说说大模型微调这个场景因为最近咨询这块的人特别多。大模型微调和普通训练任务不一样它的显存占用和通信模式对调度有非常特殊的要求。如果只是简单地把模型塞进小算力实例里去跑很容易因为显存不够直接被OOM杀掉。我的建议是大模型微调任务在ZStack AIOS里继续走整卡或整节点调度不要强行切片。原因有三个。一是大模型训练的张量并行和数据并行需要模型参数在所有GPU上同步切片实例之间的通信开销不可控二是显存需求动不动就是几十GB起小实例根本装不下三是训练任务一旦跑起来就是几小时起步中途被抢占的代价太大不适合和突发型推理任务共卡。如果确实需要在这类任务上省资源更好的思路是开启平台对多卡通信场景的聚合调度能力把多个物理卡的算力组织成逻辑上的一组设备。但这要求平台对高速互联拓扑有足够的感知能力普通模式下还是别贪这个便宜。还有一个容易忽略的点在显存分配时要给训练预热阶段留余量。PyTorch的缓存分配器会在第一个迭代时一次性申请大量显存如果模板给定的显存刚好卡在模型要求的临界值很容易在预热阶段就触发OOM。一般建议预留10%-15%的显存缓冲别把模板参数算得太满。我个人的习惯是生产环境的算力模板宁可先宽后紧也不要一开始就压到极限。把资源利用率做到70%到80%是一台好机器的状态硬要冲到95%一旦任务波动整个池子都会变得脆弱。算力池化这件事科学地讲是分蛋糕但对一线运维来说更重要的是让每块蛋糕都被端上桌而不是切得越多越好。这套平台我们用下来最大的感触是调度逻辑要对业务场景有敬畏心工具给你切分的自由你得自己判断什么时候该切、什么时候不该切。
返回列表