智算基础设施:三段独立的 GPU 计算系统建设
已上线在蔚来汽车不同阶段分别解决了三个独立的计算基础设施问题:为 Nomi 大模型搭建在线推理平台并解决 LLM 场景下的并发调度难题;对智驾模型发版前的离线评测流水线做联合优化,把吞吐翻倍、算力减半;对万卡级训练集群做架构重构,独立完成多集群统一和资源池整合。三个项目不是一个连续的平台建设,而是在"智算中心"这个职责域里,针对不同计算层次的三次独立系统建设。
蔚来汽车 · 2022.01–2022.10 / 2023.10–2024.02 / 2025.05–2025.10 · 自动驾驶研发-云端工程部
这三个项目发生在不同时期,面对不同的业务问题,团队构成和我的角色也各不相同。唯一的共同点是它们都落在 GPU 计算基础设施这个域里——从在线推理、离线计算到大规模集群管理,覆盖了三个不同的计算层次。
一、在线推理服务:Nomi 大模型推理平台 0→1
2022.01–2022.10
背景与角色
云端平台计划建设智算中心,整合集团内部 GPU 使用需求。第一个业务方是座舱团队的 Nomi——蔚来车载大模型助手。Nomi 团队负责模型本身和业务层串联,我们作为服务提供方负责两件事:通用推理基础设施,以及针对 Nomi 模型的定向推理优化。
我是项目负责人,借用了实验计算团队,规模 4 到 8 人,下设业务平台和推理计算两个子团队。我没有直接写推理代码,主要负责整体方案设计、用户需求到技术方案的转化、以及两个技术方向的推进——本地化部署的推理服务能力建设和大模型推理优化方案的预研。
并发度负载均衡:大模型调度的新问题
处境
推理服务上线后,第一个必须解决的基础问题是负载均衡。但大模型推理场景下的负载均衡和传统服务完全不同。传统负载均衡关注的维度是 QPS、请求压力或者连接数——请求进来,分发到某个实例,处理完返回,非常快。但 LLM 推理不是这样。一个推理请求发到实例 A 之后,A 不是瞬间返回,而是持续占据一个"并发槽位"直到整个生成过程结束。实例 A 可能能同时处理 4 个请求,B 能处理 5 个,但必须等 A 的某个请求真正结束返回之后,才能把下一个请求打进去。传统 LB 完全不理解这种"并发额度"的概念。
桌上的选项
一条路是在已有的负载均衡组件上做扩展,比如用加权轮询加上实例健康检查来近似处理。这条路的问题是它本质上还是在 QPS 维度做调度,无法精确地管理每个实例的并发额度——结果就是部分实例被打满而其他实例空闲,或者某个实例已经满了但 LB 还在往里塞请求。另一条路是放弃所有已有的负载均衡方案,自己做一个请求调度器,以实例并发额度为核心维度。
我的选择
我们选了后者。自研了一个请求调度器,核心机制是:每个实例预先配置一个最大并发数,调度器以代锁的形式管理每个实例的并发额度。请求进来时,调度器检查哪个实例还有空余的并发槽位,获取锁后把请求发过去;请求完成返回时释放锁。类似令牌桶,但管理的不是速率而是并发数。
这个调度器是业务专用的——只针对 Nomi 的特定模型和业务场景。做成通用 LLM 调度器的复杂度太高,不同模型、不同配置下的并发特性差异很大,通用化在当时没有意义。
高可用方面:只有一个调度器不行,但多个调度器之间的并发状态同步极其困难——状态是实时变化的,分布式一致性的成本太高。最终方案是调度器做主备,常规情况下只有主节点工作,备节点不启用,主节点故障时立即切换到备节点。用最简单的架构解决高可用问题。
代价
业务专用意味着每接一个新模型/新业务都需要单独评估并发特性和配置。主备而非多活意味着调度器本身不能水平扩展,容量上限取决于单节点的处理能力。
结果
这套调度方案在 Nomi 业务上稳定运行,后来扩展到 20 多个业务。并发调度的精确性直接影响了推理服务的响应效率和资源利用率。
回看
这是我在这个项目里最有代表性的技术判断。不是"选了什么组件",而是识别出大模型推理和传统服务在资源模型上的根本差异,然后据此做了完全不同的调度设计。后来行业里陆续出现了类似思路的开源方案,但在 2022 年初我们做这个决策的时候,几乎没有参考。
KServe:复用而不是重造
推理服务的部署体系没有自己造,直接用了 KServe。KServe 解决的是模型部署中的版本管理、Deploy/Service/Ingress 这一整套服务可达性能力。重新做一套对我们没有意义,复用成熟方案能让我们在几周内就把部署链路跑通,把精力集中在真正需要解决的推理优化和调度问题上。
推理框架:持续跟随快速演化的技术栈
推理框架经历了从 vLLM 到 LMDeploy 再到 TensorRT-LLM 的演进。这不是一次性的"选型决策",而是持续跟随——2022 年大模型推理技术迭代极快,几乎每个月都有新的框架版本或优化方案出来,我们需要不断评估、适配和切换。
在 TensorRT-LLM 阶段,我们遇到了一个具体问题:NVIDIA 对当时 Nomi 使用的模型支持不足。我们作为生产用户向 NVIDIA 提出了具体的适配需求并推动其完善——不是我们成为 TensorRT-LLM 的核心开发者,而是通过真实的生产场景反馈推动供应商改进。
HAMI vGPU:从监控异常到资源治理
处境
服务上线运行一段时间之后,从集群监控数据中发现了一个普遍问题:部分业务的 GPU 显存利用率不到 10%,甚至有些卡虽然被占住了但完全没有计算流量——占着资源但什么都没干。整卡分配的模式下,一个只需要 10GB 显存的任务也要独占一张 80GB 的 A100,剩下 70GB 完全浪费。
桌上的选项
一条路是和业务方沟通,要求他们自己优化资源使用。但业务方的优化动力有限——GPU 对他们来说是"免费"的内部资源,多占一点没有成本压力。另一条路是从基础设施层面解决——把 GPU 虚拟化,让资源粒度可以细到业务实际需要的水平。
我的选择
我们用 HAMI 对集群做了虚拟化改造。通过 benchmark 确认了部分数据处理任务的显存需求在 10GB 以下且算力使用量不大,据此把 80GB 的 A100 拆分到 1/8 粒度(约 10GB),让小任务可以只申请它实际需要的资源。
改造不只是开关一个 vGPU 功能。我们要求每一个业务方重新评估自己服务的实际资源使用情况,重新提交资源需求,然后逐个做推理服务的迁移。在这个过程中还清理出了一批实际上完全没有流量的无效服务——它们占着 GPU 但早就没有人在用了。
代价
迁移过程需要逐个业务协调,推进周期不短。vGPU 本身的调度依赖 HAMI + K8s,不是我们自研的能力。
结果
完成整体迁移后,推理服务使用的 GPU 数量从 260 多张降到了 140 多张。这不只是"虚拟化省了资源"——更准确地说,是一次完整的资源盘点、业务重新评估、服务迁移和无效资源清理。
回看
HAMI 和 KServe 一样,底层能力不是我发明的。我的贡献是从监控数据中识别出资源浪费的模式,设计了基于 benchmark 的资源配额方案,然后推动整个迁移和治理过程。工具是现成的,但"发现问题→设计使用方式→推动落地"这条链是我负责的。
推理优化与核心指标
在推理层面,结合 KVCache 和 PromptCache 实现了显著的延迟优化。KVCache 缓存推理过程中的键值对避免重复计算;PromptCache 则利用了 Nomi 业务的一个特点——用户的请求模式相对固定,请求前缀比较确定,大部分情况下可以把请求内容缓存下来复用。模型采用 7B 规模的 INT4 量化,在效果和速度之间取了一个业务可接受的平衡点。
最终指标:A100 上,业务团队在实际业务场景下做端到端 benchmark,平均首 Token 延迟约 30 毫秒。当时同业的基准大约在 150 毫秒,理想汽车曾公开过类似的数字。
技术架构:在线推理服务
业务方(Nomi / 专属群客服 / 工业化 等 20+ 业务)
│
▼
┌──────────────────────────────────┐
│ 跨地域网络层 │
│ 业务侧 → 智算侧,最短路径 │
└──────────────┬───────────────────┘
│
▼
┌──────────────────────────────────┐
│ 自研请求调度器(主备) │
│ │
│ · 管理维度:实例并发额度(非 QPS)│
│ · 机制:代锁/令牌式并发控制 │
│ · 请求进入 → 检查并发槽位 → 获锁 │
│ · 请求完成 → 释放锁 │
│ · 高可用:主备切换,非多活 │
│ · 业务专用(Nomi 模型定向优化) │
└──────────────┬───────────────────┘
│
▼
┌──────────────────────────────────┐
│ KServe 部署层 │
│ · 模型版本管理 │
│ · Deploy / Service / Ingress │
│ · 扩缩容 / 流量调度 │
└──────────────┬───────────────────┘
│
▼
┌──────────────────────────────────┐
│ GPU 集群(A100) │
│ │
│ 推理优化栈: │
│ · 框架:vLLM → LMDeploy │
│ → TensorRT-LLM │
│ · 缓存:KVCache + PromptCache │
│ · 量化:7B / INT4 │
│ · 结果:FT ~30ms(行业 ~150ms) │
│ │
│ HAMI vGPU: │
│ · 80GB A100 拆分到 1/8 粒度 │
│ · 基于 benchmark 确定配额 │
│ · 迁移后:260+ → 140+ 张 GPU │
│ │
│ SLA: │
│ · 服务故障 → 可重启 │
│ · 单机故障 → 可迁移 │
│ · 集群故障 → 可切换 │
│ · 监控可视化 + 故障演练 │
└──────────────────────────────────┘
三个月内从 0 到 1 完成上线。后续扩展到 20 多个业务,集群容量扩展到数百张 GPU。
二、离线推理优化:智驾模型发版评测加速
2023.10–2024.02
背景与角色
智驾团队每次模型发版前都需要跑一轮离线评测——用云端算力对新版本模型做仿真验证。每批大约 10 万条 clip,每条约 5 分钟。投入了几千张卡,但整体运行效率仍然不够。
更麻烦的是时效压力:这不是一个可以慢慢跑的批处理任务,而是发版前的关键卡点。每一次都是发版目标时间点已经被压到极限才到我这里,而且每天可能不止一个版本需要评测。核心矛盾不是单纯节省 GPU,而是在固定且极短的交付窗口内提高吞吐能力。
我主导了这次联合优化的方案设计和推进,与智驾的感知团队深度协作。
数据准备与执行解耦
第一步优化针对的是数据和计算混在一起跑的问题。
评测数据是分布式的——部分在本地 IDC、部分在云端。本地的算力较多但数据可能在云端,云端有部分算力但不多。任务执行时,数据下载消耗的是 IO 和网络带宽,模型推理消耗的是 GPU 和 CPU。如果让这两件事串行执行,GPU 在等数据下载的时候完全空转。
我设计了数据下载和执行的解耦:任务运行之前先启动数据预取,把数据提前准备好,之后才进入模型推理阶段。数据预取跑 IO/网络,模型推理跑 GPU——两者不再互相等待,GPU 可以被充分打满。
计算图拆分与中间结果缓存
处境
数据准备和执行解耦之后,瓶颈转移到了模型推理本身。智驾的离线评测不是一个单一模型的简单推理,而是一条完整的计算图:传感器数据理解(激光雷达、摄像头图像)→ 感知结果 → 后处理(车道线规则、障碍物规则、行车规划、车道线预测、定位检查等多个模块)。整条链路都需要 GPU,但各段对 GPU 的需求强度不同。
我在和感知团队反复讨论执行链路的过程中发现了一个关键特征:前半段(传感器理解、图像识别)的模型变化频率很低——这些基础感知模型相对稳定,不会每次发版都改;而后半段的后处理模块变化频率很高——几乎每个版本都会调整规则和参数。但当时每次评测都是从头到尾跑完整条链路,前半段的计算每次都在重复。
桌上的选项
一条路是接受整条链路每次都跑——实现简单,但浪费大量 GPU 在重复计算上。另一条路是把计算图拆成两段,前半段(稳定的感知模型)的结果做缓存,只有当模型版本真正变化时才重新计算;后半段(频繁变化的后处理)每次正常执行,直接读取缓存的前半段结果。以空间换时间。
我的选择
我推动感知团队把这两个模块进行了拆分。拆分之后,前半段的计算结果被缓存下来,后续评测只需要跑后半段。由于前半段是计算最密集的部分(对激光雷达和原始图像的理解),缓存它的效果非常显著。
这个发现不是我通过阅读感知端代码分析出来的——我没有他们的代码权限。而是在跨团队的技术讨论中,通过不断追问"为什么慢、执行链路是什么、哪段变哪段不变"逐步聊出来的方向。我提出拆分和缓存策略,感知团队负责修改计算图,我们负责云端的调度、缓存管理和资源编排。
代价
缓存占用额外存储空间。感知团队需要改动计算图的结构,增加了他们的工程量。如果前半段模型也发生变化,缓存需要失效重算。
结果
整体运行效率提升 50%。前半段的重复计算被大幅消除,GPU 资源集中在真正需要重算的后处理部分。
GPU 高密度执行
计算图拆分之后,下一步是进一步把每张 GPU 打满。
随着迭代深入,我们发现后处理链路本身也是由多个小模型串联组成的——传感器处理、车道线和障碍物规则处理、行车规划处理、车道线预测、定位检查等等。这些模块对 GPU 资源的需求各不相同:有些模块是 GPU 密集的,有些主要吃 CPU,有些只需要很少的显存。
如果把整条后处理链路作为一个整体运行在一张 GPU 上,那些轻量模块就在浪费 GPU 资源。我们按业务模块进行了拆分,根据每个模块的实际资源用量单独调度——GPU 密集的模块独占算力,轻量模块可以高密度地塞进同一张卡。每个 GPU 节点上运行的不再是完整链路,而是被优化分配的特定模块组合,单点尽可能打满。
技术架构:离线评测优化后的计算流水线
评测任务触发(模型发版前,10 万 clip / 批次)
│
▼
┌──────────────────────────────────┐
│ 阶段一:数据预取 │
│ · 消耗:IO / 网络 │
│ · 不占 GPU │
│ · 提前准备,不让 GPU 等数据 │
└──────────────┬───────────────────┘
│
▼
┌──────────────────────────────────┐
│ 阶段二:前半段(感知模型) │
│ · 传感器理解 + 图像识别 │
│ · GPU 密集 │
│ · 模型变化频率低 │
│ · 结果缓存 → 后续评测跳过此步 │
└──────────────┬───────────────────┘
│ 缓存命中 → 直接跳到阶段三
▼
┌──────────────────────────────────┐
│ 阶段三:后半段(后处理模块组) │
│ · 车道线 / 障碍物 / 规划 / 预测 │
│ · 每版本都会变,不可缓存 │
│ · 按模块拆分,高密度 GPU 执行 │
│ │
│ 拆分逻辑: │
│ · GPU 密集模块 → 独占算力 │
│ · 轻量模块 → 高密度塞卡 │
│ · 每节点运行优化模块组合 │
└──────────────┬───────────────────┘
│
▼
评测结果输出
最终结果:同样 10 万条数据,端到端运行时间从约 8 小时降到约 4 小时,吞吐翻倍;GPU 计算资源减少约一半。这两个数字来自实际生产吞吐量的对比。
三、万卡训练集群架构优化
2025.05–2025.10
背景与角色
智算中心维护了万卡级 GPU 规模。这个阶段的工作不是搭新东西,而是对已有的大规模集群做架构优化,解决两个结构性问题:任务粒度和 GPU 粒度不匹配导致的资源浪费,以及多集群割裂导致的调度障碍。
动态 vGPU 部分是 3 人协作完成的——我负责资源管理层面的改造和推理侧的虚拟化,一位同事负责任务调度,一位负责机器运维。数据处理侧的资源属性改造由我完成后交给业务团队使用。训练虚拟化不是我负责的。
多集群统一架构是我独立完成的——这套系统是从前一位同事离职后接手的遗留代码,代码量极大,只能由我来做整体拆分和重构。
动态 vGPU:任务粒度与资源粒度的匹配
智算中心有大量这样的任务:单任务时长短、任务量大、但每个任务对资源的占用量很少。在原来的模式下,用户已经在一定程度上做了优化——在一个完整 GPU 的任务里并行提交多个计算子任务。但资源利用效率仍然不够高:一个任务可能只用了三分之二张卡的资源,剩下的三分之一就空在那里,而新任务又无法塞进这个空隙。同时大量小任务的提交和管理本身也带来了调度压力。
我们做的优化是为用户提供 vGPU 的细粒度资源申请能力——用户在创建资源或提交任务时声明自己需要的实际资源配额,而不是默认占用整卡。HAMI 的调度器本身具备优先填满单张 GPU 的能力,所以在 K8s 调度层面只需要配置好资源需求就能实现合理的分配。原来三分之二张卡的任务旁边那三分之一的碎片,现在可以被其他小任务利用起来。
多集群统一架构
处境
智算中心由多个训练集群和推理/训练拆分的集群共同组成,部署在不同地域、不同系统环境中,甚至 K8s 版本都不一样。每个集群各自维护自己的资源池、任务队列和调度逻辑。结果就是资源割裂——A 集群有空闲 GPU 但 B 集群的任务排不过去,不同时段下资源无法在集群之间流通。
桌上的选项
一条路是保持多集群现状,在上面加一层跨集群的调度代理。好处是不需要改现有集群的内部结构,坏处是代理层需要理解所有集群的差异(API 版本、资源格式、调度语义),复杂度很高而且容易出错。另一条路是做架构层面的统一——把资源管理、任务配额、任务调度这些和具体集群无关的能力从各个集群中抽象出来,合并到一个核心服务里。
我的选择
我选了后者,独立完成了整个拆分和重构。具体做的事情分两步:
第一步是管理抽象。把原来散落在各个集群里的资源管理、用户配额、任务调度等上层语义全部抽出来,合并到一个统一的管理服务中。从此集群的地域、API 版本、部署方式这些基础设施差异被隔离在抽象层之下,上层只需要面对统一的资源和任务语义。
第二步是资源重新编目。对所有集群的 GPU 资源进行统一标签化——哪个集群、哪个地理区域、什么类型的 GPU、是否支持 vGPU 拆分。调度层面根据这套标签体系进行匹配,把请求路由到最合适的集群和资源上。多地域场景下还需要对各集群的 API 做适配,保证统一调度器能够和不同版本的 K8s 集群正常通信。
最终把多个集群的调度服务合回到一个核心服务里,实现了整个 GPU 集群的统一调度。资源流通之后,不同时段、不同地域之间的资源割裂问题不再存在。
代价
这是一次大规模的架构重构,涉及遗留系统的理解和改造,工程量大且风险高——重构过程中现有业务不能中断。
结果
单位时间吞吐量翻倍。更重要的是,资源从此可以在集群间流通,不再出现"A 集群空闲、B 集群排队"的结构性浪费。
技术架构:万卡集群优化后
优化前:
┌─────────┐ ┌─────────┐ ┌─────────┐
│ 集群 A │ │ 集群 B │ │ 集群 C │
│ K8s v1 │ │ K8s v2 │ │ K8s v1 │
│ 资源池 A │ │ 资源池 B │ │ 资源池 C │
│ 调度器 A │ │ 调度器 B │ │ 调度器 C │
│ 互不流通 │ │ 互不流通 │ │ 互不流通 │
└─────────┘ └─────────┘ └─────────┘
↓ 重构 ↓
优化后:
┌──────────────────────────────────┐
│ 统一管理服务(我独立重构) │
│ │
│ · 资源管理:跨集群统一资源池 │
│ · 用户配额:统一分配与管理 │
│ · 任务调度:统一入口 │
│ · 标签体系:地域/GPU 类型 │
│ /vGPU 可拆分性 │
└──────────────┬───────────────────┘
│
┌─────────┼─────────┐
│ │ │
▼ ▼ ▼
┌────────┐ ┌────────┐ ┌────────┐
│集群 A │ │集群 B │ │集群 C │
│API 适配│ │API 适配│ │API 适配│
│动态 │ │动态 │ │动态 │
│vGPU │ │vGPU │ │vGPU │
└────────┘ └────────┘ └────────┘
动态 vGPU(3 人协作):
· 用户声明实际资源需求
· HAMI 调度器优先填满单卡
· 碎片资源可被小任务利用
· 推理虚拟化:我负责
· 数据处理资源属性:我负责
· 训练虚拟化:非我负责
回看三个项目
这三个项目不是一个连续的平台建设故事,但连起来看能体现一件事:在 GPU 计算基础设施这个域里,我从在线推理、离线计算到大规模集群管理三个层次都实际做过,而且每个层次面对的问题和解法完全不同。
在线推理的核心问题是"大模型推理和传统服务的资源模型不一样"——我识别出了这个差异并据此做了调度设计。离线推理的核心问题是"计算链路中有大量可消除的重复计算"——通过跨团队讨论发现了计算图的拆分点,推动感知团队配合改造。万卡集群的核心问题是"多集群资源割裂"——我独立完成了遗留系统的架构重构,把分散的资源和调度统一起来。
三个项目的共同点是:底层工具(KServe、HAMI、K8s)都是现成的,我的价值不在于发明这些工具,而在于"识别问题→设计使用方式→推动落地"这条链路。技术选型本身不复杂,复杂的是在真实生产环境中把这些能力组织成解决具体业务问题的系统。