1. 从单卡到集群:先想清楚你卡在哪
很多团队聊起大模型部署,第一反应就是“上集群”,仿佛单机跑不动是必然的,多机分布式才是正经方案。但我在实际项目里见过太多反例:有的场景单张消费级显卡加量化就能扛住线上流量,非要兴师动众搭一套多机推理集群,最后活没干多少,光运维和排查网络就把人耗干了;也有的团队在单机上已经压到极限,却连瓶颈在哪都没测明白,就匆忙引入分布式推理,结果吞吐没提上去,延迟反而翻倍。所以聊演进路径之前,必须先明确一个很俗但很关键的判断依据:你当前到底卡在显存容量,卡在算力吞吐,还是卡在并发访问量。
单机部署最容易踩的坑是“模型能跑起来就以为及格了”。我见过不少同学用一张 24GB 显存的卡加载 7B 模型,量化一下、推理几句,生成速度感觉还行,就放到测试环境给前端联调。结果一旦模拟真实并发,显存被多路请求撑爆,系统直接 OOM。更隐蔽的是,很多模型部署框架默认会把整张卡的一个大块显存预留下来,比如你用 vLLM 或 TensorRT-LLM,它会提前申请一整块显存池。如果机器本身只有 24GB,模型权重加 KV cache 就可能把空间吃满,而你的业务并发还没真正打进来。这时候不是“要不要上集群”的问题,而是连单机资源利用率都还没摸清。
我习惯的做法是:任何模型部署前,先跑一个单请求压测,再跑多并发压测,记录显存峰值、单 token 延迟、吞吐量三个指标。单请求压测能看出模型推理本身的耗时,包括 prefill 和 decode 两个阶段;多并发压测才能看出显存、算力、调度三者的真实短板。很多时候你会发现,显存还有富余,但吞吐已经上不去,那就是算力饱和;显存突然爆掉,那就是并发管理出了问题。搞清楚这些,再决定要不要走向多机集群。
1.1 单机部署的隐藏成本:不是只有一张显卡的事
单机部署听起来简单,其实就是把模型权重加载到显存,然后跑一个推理服务。但实际落地时,隐藏成本会跳出来咬你。首先是 CPU 内存与显存之间的加载瓶颈。一个 13B 模型用 FP16 存储,光权重就有 26GB,如果从机械硬盘加载,启动过程可能超过 10 分钟;就算用固态,也远远慢于容器重启时间。你需要考虑要不要做模型热加载,或者至少提前把权重缓存到系统内存里。
其次是推理引擎的选型。PyTorch 原生推理在单机单卡上往往只发挥了 30% 左右的 GPU 算力;如果用 vLLM 或者 TensorRT-LLM,通过 PagedAttention、算子融合、连续批处理这些优化,同一个模型的单卡吞吐能提升几倍甚至一个数量级。我遇到过一个很典型的案例:某个内容平台要在内部上线一个文档问答助手,最初基于 Transformers 库直接写推理接口,单张 A10 显卡上并发进来 8 个请求就开始超时。后来换成 vLLM,同一张卡扛到 40 并发,单 token 延迟只多了 20%,但吞吐提升了 5 倍。这个改动只花了半天,根本不涉及集群。
所以单机部署的重点不是“能不能跑”,而是“用多低的成本让它高效跑”。连续批处理这个机制尤其关键,它会在推理过程中动态收集等待中的请求,把不同生成进度的序列拼到一个或多个 step 里计算,GPU 就不会在等待某个长尾请求时闲着。如果你连这个优化都没开启,就急着上集群,大概率会带着一个很低效的底座,把问题放大到整个集群。
1.2 用数据和公式判断“下一步应该去哪”
我建议自己做一个非常简单的容灾预估。打开任务管理器或者nvidia-smi,先看当前单卡并发满载时达到的吞吐量 T_0,再算一下未来业务峰值需要的目标吞吐 T_target。如果 T_target 只是 T_0 的两三倍,优先考虑优化单卡、换更强的卡,或者加一卡做数据并行;如果 T_target 超出十倍的量级,才轮到认真设计集群方案。算力不是线性的,多机分布式推理因为通信开销、调度损耗,效率通常很难做到 90% 以上,你加两台机器往往得不到两倍吞吐,能拿到 1.6 倍就已经不错了。
集群和单机的另一个分界线是模型大小与显存的关系。如果你手里的模型权重加上推理状态已经超越单张顶级卡,而你又不想牺牲太多量化精度,那就必须考虑多卡切分。这里有个很实用的经验公式:推荐显存大小大概是模型参数量乘以 2,再乘以一个安全系数 1.2 到 1.4,预留 KV cache 和中间激活值的空间。以 70B 模型为例,FP16 权重就是 140GB,加上推理过程中的动态内存,单卡基本无解,只能走向多卡或多机。
把这些数据都摆到桌面上以后,再谈演进路径,就不会被“别人都在搞集群”这种话带着走。你心里要有一本账:单机是低成本试错的好起点,但也是暴露一切资源瓶颈的最好放大镜。跳过单机直接做集群,等于跳过了理解业务的阶段。
2. 单机多卡部署的核心是把并行策略选对
当单卡确实扛不住的时候,第一步不是上多机,而是在一台机器里插多张卡。单机多卡技术成熟度高,通信走 NVLink 或 PCIe,比跨机走以太网要可靠得多,延迟也低一个数量级。很多团队在单机多卡阶段就能解决大部分问题,不一定需要走到“集群”这个级别。但多卡并行不是简单把模型塞进去,里面有四种常见的并行策略:数据并行、张量并行、流水线并行,以及它们之间的组合。每一种的适用范围和代价都不同,选错策略会直接导致显卡利用率低。
数据并行是最容易理解的方式:每张卡放一份完整的模型副本,把进来的请求切分成多批,分发给不同卡同时处理。它的优点是实现简单,扩容也直观;缺点是显存浪费严重,而且如果模型本身已经大到单卡放不下,数据并行就没法用。数据并行更偏“并发扩展”,适合场景是模型不大但请求量大的情况。在多机场景里,它往往作为最基本的扩展维度存在,配合负载均衡器按卡分发。
在实际部署中,我见过不少人一上来就开数据并行,把请求均匀分发到 4 张卡上,但没有考虑批次大小对每张卡的压力。结果显存占用太高,每张卡的 batch 只能开得很小,反而因为频繁的 kernel 启动开销,导致整体吞吐不如单卡加连续批处理。所以数据并行也不是“无脑加卡就提速”,你得用小批量压测来找到单卡最优 batch,再乘以卡数,才是合理预期。
2.1 张量并行:把一个大矩阵拆成几块
张量并行也叫层内并行,核心是把一个 Transformer 层中的权重矩阵切分成多块,分给不同的 GPU,让它们在计算前向时协同完成同一个矩阵乘法。以多头注意力为例,可以把 Q、K、V 的线性层按列切分到两张卡上,每张卡只计算一半的维度,算完再做拼接;MLP 部分则按行切分,再用 AllReduce 把部分结果合并。这个切分直接决定了每张卡需要的显存变小,但通信量也变大了。
因为每一层的前向计算都要在卡之间同步中间结果,所以张量并行的通信极其频繁。在单机内走 NVLink 还勉强能接受,如果跨机走普通万兆以太网,通信开销会直接把计算收益吃掉。这也是为什么很多人调张量并行,在单机 4 卡上发现提升明显,一扩到两台机器 8 卡,反而性能倒退。我自己的经验是:张量并行尽量只在单机内使用,不要轻易跨机;如果非要跨机,必须保证机间网络有 RDMA 或高速 InfiniBand,否则就选流水线并行来减少通信频率。
在引擎层面,vLLM 和 TensorRT-LLM 都支持张量并行,启动时通过tensor_parallel_size参数控制切分数量。但这里有个坑:张量并行维度不一定要等于卡数,比如你有 8 卡,可以先用 2 卡张量并行把显存问题解决,再用 4 组 2 卡做数据并行,而不是 8 卡全部张量并行。这样既控制了通信开销,又保留了扩展空间。一种常见的组合是“张量并行 × 数据并行”,但我建议先跑一个小测试矩阵,对比 2 度张量并行加 4 度数据并行与 8 度张量并行之间的吞吐差异。实测中,前者往往在中等规模下更稳。
2.2 流水线并行:减少通信,但小心气泡
如果说张量并行解决的是“单层放不下”,流水线并行解决的则是“整层数太多”。它的思路是把模型的层分成多个阶段,每个 GPU 负责其中一段连续层,数据按顺序流过各个阶段。这样一来,各卡之间只需要在层与层的边界传递中间激活值,通信频率远低于张量并行。但也出现了一个新问题:流水线气泡。简单说,当一个 batch 在第一阶段计算时,后面几个阶段的 GPU 只能等着,利用率就会下降。
工程上常用 micro-batching 来填平气泡:把一个大 batch 切得更细,让前一个 micro-batch 在前一阶段算完时,后一个 micro-batch 紧跟着进入,形成流水线。流水线越深,micro-batch 数量越多,气泡占比就越小。我实际调的时候,micro-batch 数量和阶段数的关系可以参考:当 micro-batch 数量等于阶段数时,气泡率达到理论上的最低点附近;继续增加 micro-batch 虽然还能再压一点,但会增大调度的复杂度和显存压力。所以很多框架默认把 batch 切成多个小份,逐级流动。
流水线并行还有一个容易踩的坑是负载不均衡。不同层的计算量其实不一样,尤其是 embedding、注意力层、 FFN,前向耗时差别很大。如果你只是机械地按“每卡 N 层”切分,很可能某张卡成为瓶颈,其他卡空闲等待。比较好的做法是先对模型做 profiling,统计每一层的前向耗时,再按总耗时均匀分配层段。这一步我一般在离线环境做,用真实业务数据集跑一个 epoch,看各层时间占比,最后把耗时接近的层归到一个 stage。
2.3 单机多卡部署的具体操作流程
假设你有一台 8 卡的训练/推理服务器,想部署一个 70B 模型。第一步,用nvidia-smi确认卡间拓扑,是 NVLink 全互联,还是 PCIe 交换机。查询拓扑可以用nvidia-smi topo -m,重点看每对 GPU 之间的连接类型。如果 P2P 链路是 NVLink,可以做张量并行;如果全是 PCIe,张量并行通信会比较吃亏,优先考虑流水线并行加数据并行。
第二步,选择一个推理框架,并进行环境初始化。以 vLLM 为例,安装完成以后启动服务的方式是这样:
python -m vllm.entrypoints.openai.api_server \ --model /data/models/llama-70b \ --tensor-parallel-size 4 \ --pipeline-parallel-size 2 \ --max-model-len 4096 \ --gpu-memory-utilization 0.92tensor-parallel-size 4表示 4 卡做张量并行,pipeline-parallel-size 2表示另外 2 个 stage 做流水线并行,合起来 8 卡。如果你的框架还不支持流水线并行,也可以先用tensor-parallel-size 8全张量并行,然后观察通信是否成为瓶颈。启动后用nvidia-smi看每张卡的显存占用,正常应该很接近;如果某张卡明显偏低,说明权重切分不均,或者某个 stage 没有加载全部层。
第三步,压测。不要一上来就测极端并发,先固定 batch size 跑几个请求,确认功能无误,再用脚本逐步增加并发数。我自己写压测会用 Python 的asyncio和aiohttp,模拟真实请求流。下面这段脚本可以快速测出吞吐和平均延迟:
import asyncio, aiohttp, time, statistics async def send_one(session, url, prompt): payload = {"model": "deploy-test", "prompt": prompt, "max_tokens": 128} start = time.perf_counter() async with session.post(url, json=payload) as resp: await resp.text() return time.perf_counter() - start async def main(): url = "http://localhost:8000/v1/completions" concurrency = 32 prompts = ["你好,请介绍一下你自己"] * concurrency async with aiohttp.ClientSession() as session: tasks = [send_one(session, url, p) for p in prompts] latencies = await asyncio.gather(*tasks) print("avg latency:", statistics.mean(latencies), "qps:", concurrency / sum(latencies)) asyncio.run(main())这一步能清楚看到平均延迟和吞吐量。多卡并行如果调得好,吞吐应该比单卡有明显提升;如果提升低于 1.5 倍,说明通信开销已经压过计算收益,需要回头调整并行策略。
3. 从多机集群部署要跨过的三道坎
走到多机这一步,意味着单机的显存和算力已经彻底不够用了,或者你需要横向扩容来应对不可预测的并发峰值。但多机集群不是把单机部署复制到几台机器,它牵扯到更复杂的网络拓扑、分布式调度和容错机制。我在实际项目里看到,很多团队从单机跳到集群,最不适应的不是模型本身,而是“一个请求到底怎么被分发、怎么聚合结果”这件事。它不像单机多卡那样靠 NVLink 就能无缝协作,跨机通信需要显式的框架来管理。
第一道坎是网络。多机推理的服务质量几乎被网络延迟和带宽锁死。模型推理本身是计算密集型的,但分布式推理需要在节点间传递中间激活值、梯度或 KV cache。机器越多,通信模式越复杂。我做过一个实验:两台机器之间用普通千兆交换机跑张量并行,结果吞吐比单机 2 卡还低 30%;换成 25G 网口之后,吞吐才反超单机。原因就在于每层 Transformer 的前向都要做 AllReduce,千兆网络把 GPU 卡在等待上。所以集群部署前,先确认机器间是万兆起步,最好有 RDMA,并且用ib_write_bw或iperf3测一下实际带宽,别信网卡标称值。
第二道坎是框架选型。不同框架对多机支持差别很大。比如 vLLM 虽然已经支持多机推理,但早期版本更多是为单机高吞吐设计,多机张量并行的通信实现不一定能充分利用高速网络;TensorRT-LLM 在多机支持上也有自己的限制,需要仔细看文档。还有专门做分布式推理的框架,比如针对超大规模模型的跨节点推理方案,它们会对数据并行、张量并行、流水线并行做自动组合。我的建议是:如果团队没有太多分布式开发经验,优先选择生态成熟、社区活跃的方案,哪怕多花一点机器成本;如果团队有底层通信能力,再根据模型结构自己做定制切分。
第三道坎是配置一致性。多机集群的每台机器上,驱动、CUDA 版本、推理框架版本必须完全一致。这个听起来简单,实际上是最容易出问题的地方。我曾经排查过一个诡异的线上问题:12 台机器里有 1 台 A100 偶尔出现illegal memory access,最后发现是这台机器上的一个小版本驱动与其他机器不同,导致 CUDA context 初始化异常。后来我们把所有机器的驱动和容器镜像锁成同一版本,问题就消失了。
3.1 多机部署的网络拓扑与通信策略
多机集群通常会有一个主节点负责接收请求、分发任务,工作节点负责推理。主节点也可以同时承担推理任务。如果模型切分到多个节点上,每个请求都需要把 prompt 广播到所有相关节点,然后每层前向都要做跨机同步,最后再汇总生成结果。因此,机器数量不是越多越好,而是要找到一个最优并行度组合。
以两张 A100 的机器为例,假设你有 4 台,总共 8 卡。如果要部署一个 70B 模型,你可以把tensor_parallel_size设置为 4,只在这 4 卡之间做张量并行,并且尽量选在同一台机器里的卡,避免跨机;然后data_parallel_size设置为 2,即在两台机器之间做数据并行,每个数据并行副本内部使用 4 卡张量并行。这样跨机的通信只发生在请求分发和结果汇总阶段,频率低,网络压力小。
很多框架里还会有一个 prefill 和 decode 解耦的选项。对于长 prompt 场景,prefill 阶段的计算量很大,但中间结果不需要频繁跨机通信;而 decode 阶段每个 step 都要同步,对网络延迟更敏感。如果集群里不同机器的网络性能有差异,可以尝试把 prefill 和 decode 调度到不同节点上。但这套玩法比较复杂,需要自己对框架内核做改造,普通团队不建议硬上。
3.2 配置文件的真实案例:一个 8 卡集群的 yaml 片段
为了让思路落地,我分享一个模拟项目里的配置片段。这个项目是一个内部的代码生成助手,模型是 13B 量级,但并发峰值很高,所以选了 3 台机器,每台 2 张 A100。推理框架用 vLLM 的集群模式,通过 Ray 作为底层调度器。
启动前,先在每台机器上写好相同的模型路径,然后启动 Ray head 节点,再启动 worker 节点。vLLM 的分布式启动命令大概是:
# 主节点 ray start --head --port=6379 --num-gpus=2 # 工作节点 ray start --address=主节点IP:6379 --num-gpus=2然后运行:
python -m vllm.entrypoints.openai.api_server \ --model /data/models/codegen-13b \ --tensor-parallel-size 2 \ --data-parallel-size 3 \ --max-model-len 8192 \ --gpu-memory-utilization 0.90这里的tensor-parallel-size 2表示每个数据并行副本内部有 2 张卡张量并行,>
Unicode码点转UTF-32字节序列:规则、字节序与BOM实战详解
今天聊一个看起来简单、实际坑不少的话题:把Unicode码点转成UTF-32的字节序列。这个操作在字符编码领域里属于“十分钟能讲完规则、一整天填不完坑”的类型,很多刚接触底层文本处理的朋友都以为它不过是“码点直接塞进4个字节”,但真到写代码…
MPU6500裸机驱动与自适应卡尔曼姿态解算实战
简介:本资源是一套面向嵌入式开发者与物联网项目实践者的MPU6500六轴IMU驱动与姿态解算完整实现,聚焦加速度传感器与陀螺仪数据融合,解决运动控制、姿态跟踪等场景下的欧拉角与四元数实时解算难题。包内共157个文件,涵盖24个C源文…
MPU6500驱动算法原理与实战:从原始数据到稳定姿态解算
简介:本资源是一套面向嵌入式开发者与物联网项目实践者的MPU6500六轴IMU驱动与姿态解算完整实现,聚焦加速度传感器与陀螺仪数据融合,解决运动控制、姿态跟踪等场景中原始数据采集难、滤波不稳定、欧拉角万向锁等典型问题。压缩包含157个文件&…
Android APK资源提取与重打包实战:apktool逆向解码避坑指南
简介:这份资源面向需要在 macOS 环境下进行 APK 逆向与资源提取的开发者与安全学习者,核心是 apktool 工具及其配套使用说明,帮助解决在 Mac 上获取、反编译 APK 资源时工具零散、配置繁琐的问题。压缩包共 3 个文件,包含 1 个 ja…
人机协同:好写作AI在写作教学中的角色定位与实践
一堂真实的写作课,学生对着题目憋了二十分钟写不出开头,老师在办公室抱着一摞作文改到深夜,批改完毕发回去,学生看一眼分数就塞进抽屉。这样的循环,在很多课堂里年复一年地上演。直到我开始尝试在写作教学中引入好写作…
STM32飞控实时性设计:从IMU采样到PID执行的确定性链路
简介:本资源是一套基于STM32微控制器开发的无人机飞行控制系统完整工程包,面向嵌入式开发者、飞控爱好者及高校电赛/毕业设计学生,聚焦实时姿态解算、PID闭环控制与多传感器融合等核心难点。压缩包共212个文件,涵盖40个C源码与42个…