OpenClaw 这套分布式部署方案,我最近在内部集群上折腾了很久,最花力气去理解的,恰恰就是模型分片和节点间通信这两件事。很多人一听到"分布式部署"就以为只是把模型塞进多张卡,真正跑起来才发现,切分方案决定了显存怎么用,通信机制决定了速度能到多少,这两个问题不搞透,后面全是坑。这篇文章就围绕 OpenClaw 在分布式场景下的分片策略、通信实现,以及张量并行和流水线并行在其中的真实定位,把我踩过的、验证过的东西拆开讲清楚。如果你正准备把大模型推理或训练服务扩展到多节点,这篇内容应该能帮你少走不少弯路。
1. 分布式部署先过三道坎:容量、拆分、通信
1.1 单卡放不下和单机放不下是两回事
先把最基础的场景理清楚。你手里有一个模型,假设 FP16 精度下权重占 30GB,单卡显存只有 24GB,那么单卡放不下。这时候继续往分布式走,就要区分"单卡放不下"还是"单机放不下"。
单卡放不下,但单机有 8 张卡、每张 24GB,总显存 192GB,那问题的解法通常是在一台机器内做并行,把模型切成几块塞进同一台机器的多张卡里。这种情况下节点间通信基本不参与,因为卡与卡之间走的是机内高速互联,带宽非常高,延迟也很低。
但如果模型权重已经到了 200GB,单机 8 卡也扛不住,就必须跨节点。跨节点之后,通信要走网卡、交换机、光缆,带宽一下子降了几个数量级,这时候"怎么把模型切得开"和"切完之后怎么把数据传得动"就成了两个并行的核心问题。OpenClaw 在处理这两类场景时,策略是完全不一样的。
| 部署形态 | 显存上限 | 通信带宽 | 主要瓶颈 | 适用场景 |
|---|---|---|---|---|
| 单机多卡 | 受机内总显存限制 | 机内高速互联,几十 GB/s 以上 | 显存容量 | 中等规模模型、低延迟推理 |
| 多机多卡 | 受集群总显存限制 | 网卡级带宽,通常 10~100 GB/s | 网络带宽与延迟 | 超大模型、高吞吐服务 |
这一点决定了后面对并行策略的判断。OpenClaw 的默认逻辑是:能机内解决的不出机,必须出机的一定要控制通信数据量。很多初学者会犯一个错误,觉得所有并行都是把模型均匀切开就行,没有意识到"切开后每个计算步骤之间需要同步多少数据"才是分布式性能的命门。
1.2 推理服务比训练更在意通信延迟
OpenClaw 在我这里主要承担的是推理服务角色,这和纯训练框架对分布式的需求有差异。训练阶段每个 batch 都要算梯度、同步梯度,通信量大但可以通过流水线掩盖;推理阶段则分为 prefill 和 decode 两步:prefill 一次性处理用户输入的整段文本,计算密集、激活值大;decode 是逐 token 生成,每一步依赖上一步的结果,串行性强,对单步延迟非常敏感。
所以在推理场景下,OpenClaw 的分片和通信设计会尽量保证单 token 延迟不太差。如果每次 decode 都要走一遍跨节点集合通信,那整体延迟基本没法看。这也是标题里那个问题——"是否用了张量并行或流水线并行"——特别有价值的地方:它不是一个简单的是或否,而是要看模型规模、硬件拓扑和推理阶段来组合使用。下面我分块展开。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 分片粒度之争:按层切还是按矩阵切,OpenClaw 怎么选
2.1 流水线分片:按层把模型锯开
模型分片最直观的思路,就是按 Transformer 层来切。假设一个模型有 40 层 Transformer block,4 个节点参与部署,那就把第 1~10 层放到节点 A,第 11~20 层放到节点 B,依此类推。每个节点只持有自己那几层的权重和 KV cache。
这种切法的优点是通信量小。数据从输入开始,逐层向前传播,只有当一个节点处理完自己的层、把激活值传给下一个节点时,才会产生一次跨节点通信。激活值的尺寸取决于 hidden size、序列长度和 batch size,跟模型总参数规模没有直接关系。以 hidden size 5120、序列长度 2048、单 batch 大小 4 为例,一个激活张量就是 5120×2048×4×2 字节,约等于 80MB,这个数字在跨节点传输中是可以接受的。
缺点也很明显:流水线有气泡。当一个 micro-batch 在节点 A 计算时,节点 B 可能处于空闲等待状态。如果切分的节点数太多,每个 stage 的计算量太小,气泡占比会急剧上升。所以 OpenClaw 在纯流水线模式下会严格控制 stage 数量,并且鼓励使用足够多的 micro-batch 来填充气泡。
2.2 张量分片:把一层拆成几份
张量并行则是另一条路:不是按层切,而是在一层内部把权重矩阵切开。Transformer 里最典型的是 attention 的 QKV 矩阵和 MLP 的权重矩阵。
以 MLP 为例,第一个线性层输入维度是 hidden 维度,输出是 4×hidden 维度,如果把权重矩阵按列切成两份分给两张卡,每张卡只需要做一半的矩阵乘法,得到各自的部分结果。但这部分结果还不能直接用,两者必须经过一次 allreduce 合并,才能进入激活函数。第二个线性层反过来,按行切开,输入分半、输出合并,同样需要一次 allreduce 同步。
张量并行的优势是每张卡只放一份权重切片,显存占用均衡,而且计算可以并行;代价是每一层前向都要做至少两次跨卡通信。卡与卡之间如果走机内高速互联,这个成本还可以接受,但如果同一层被切到跨节点的卡上,通信延迟和带宽瓶颈就会把计算收益完全吃回去。OpenClaw 基本只在单机内做张量并行,就是这个原因。
2.3 OpenClaw 的默认组合:机内张量,机间流水
让我直接说结论:OpenClaw 在绝大多数标准部署里采用"机内张量并行 + 机间流水线并行"的组合。也就是说,同一台机器里的 8 张卡先把某一层切分成 8 份做张量并行,然后整台机器作为一个流水线 stage,只负责模型的一部分层,stage 之间通过高速交换网络传递激活值。
这种设计背后的权衡逻辑很清晰:张量并行需要频繁通信,放在高带宽的机内通道上最合适;流水线并行通信频率低,放在跨节点网络上更能容忍带宽限制。如果你看 OpenClaw 的部署配置,通常会看到类似下面这种结构:
yaml复制# OpenClaw 参考配置(示例)
model:
name: "large-language-model"
dtype: "fp16"
parallel:
tensor_parallel_size: 8 # 单机内 8 卡张量并行
pipeline_parallel_size: 8 # 8 台机器组成流水线
data_parallel_size: 1 # 本示例不考虑数据并行
micro_batch_size: 16 # 用于填充流水线气泡
这个配置的意思是,每一层的权重会被 8 张卡分别持有,每台机器负责一部分连续层;整体拓扑是 8 台机器串成一个流水线,每台机器内部又是一个小的张量并行组。这个"机内张量、机间流水"的组合,既保证了显存能放下,又把跨节点通信的频率压到了最低。
3. 节点间通信底层的那些集合操作
3.1 集合原语对应哪类分片场景
分片只是把模型放到了不同设备上,计算时要让所有设备协同工作,就必须有一组通信原语。OpenClaw 的通信层跑的是经典的集合通信模式,拆开看无非是 broadcast、allreduce、allgather、reduce-scatter 这几类。
- broadcast:把一份数据从某个源节点发给所有节点。流水线 stage 从上一个节点拿到激活值之后,如果机内有张量并行,经常需要把这份激活广播给机内的另外几张卡。
- allreduce:所有节点各出一份数据,相加后每个节点都拿到完整结果。张量并行的矩阵乘法合并就是标准的 allreduce。
- allgather:每个节点持有完整数据的一部分,通信结束后每个节点都会持有全部数据。比如张量并行中把 attention 的输出按片段聚合回来。
- reduce-scatter:和 allgather 相反,所有节点把各自的数据统一归约,之后每个节点只保留自己对应的那一份。反向传播求梯度时经常用这个,每个节点分到对应分片参数的梯度。
OpenClaw 会在不同计算阶段组合这些原语。理解了它们各自的场景,才能看懂为什么有的地方通信多、有的地方通信少。你不需要把每个原语都背下来,只要记住一个铁律:所有节点的数据只要发生跨节点合并或交换,就会产生网络流量,流量大小决定了整个分布式系统的上限。
3.2 一次前向+反向的通信序列
我举个例子把整个流程串起来。假设用的是机内张量并行 8 卡的配置:
前向传播时,输入 token 的 embedding 查表结果先要广播给机内 8 张卡。随后每一层 Transformer block 内部,attention 的计算结果需要一次 allreduce,MLP 的输出需要一次 allreduce。到这里你就能明白,张量并行每层两次 allreduce,40 层模型就是 80 次机内通信。
反向传播时,梯度会沿着相反方向流动。MLP 第一个线性层按列切分后,梯度需要 reduce-scatter,归约后各卡保留自己参数切片的梯度;第二个线性层按行切分,前向时输出做了 allgather,反向时需要再同步一次。整个反向过程的通信量跟前向基本对等。
如果是流水线并行,通信序列反而简单得多:每个 micro-batch 在自己所在的 stage 计算完,就通过 send/recv 把激活值交给下一个 stage,反向时把梯度传回上一个 stage。整条通信链路上传输的是整块激活张量,而不是多次小消息,这对于高带宽网络来说效率更高。OpenClaw 的调度器会尽量让通信和计算重叠,也就是这一个 micro-batch 在跨节点传输激活值时,另一个 micro-batch 已经在 GPU 上算矩阵乘了。
3.3 环形拓扑与带宽公式
OpenClaw 在跨节点环境下最常使用的是环形集合通信。环形 allreduce 的完成时间可以近似为:
时间 ≈ 2 × (N-1) / N × 数据量 / 带宽 + N × 单跳延迟
其中 N 是参与节点数。这个公式有两个直观结论:一是当数据量很大时,通信时间趋近于 2 倍数据量除以带宽,跟节点数关系不大,说明环形拓扑在大数据块下扩展性不错;二是当数据量很小时,节点数 N 乘上每跳延迟会成为主导,所以在处理小消息时,节点越多越吃亏。
OpenClaw 在配置通信拓扑时会根据实际网络结构选择环形或分层树形。如果你把模型切得很碎、每个消息都很小,环形拓扑的延迟劣势就会暴露出来;反之,如果像流水线 stage 之间那样传输整块激活值,环形拓扑就是最简单稳当的选择。
| 拓扑 | 大数据块优势 | 小数据块优势 | 适用场合 |
|---|---|---|---|
| 环形 | 带宽利用率高,扩展性好 | 延迟随节点数线性增长 | 激活值、梯度等大块数据 |
| 树形/分层 | 可做局部聚合,适合大规模集群 | 小消息延迟较低 | 控制消息、小梯度同步 |
| 全连接 | 单跳直达,延迟最低 | 延迟最低 | 节点数少、带宽充裕的机内互联 |
4. 张量并行与流水线并行的真实分工
4.1 张量并行:Transformer block 里的矩阵切法
回到标题的问题:OpenClaw 是否使用了张量并行?答案是使用了,但它非常克制,几乎只在单机内使用。我看过它内部对 Transformer block 的处理,思路是标准的两段式切分。
第一段是 attention。QKV 三个矩阵按注意力头维度切分,比如 32 个 head,张量并行 8 卡,每卡分 4 个 head。每卡独立计算自己那部分头的 attention score 和输出,最后通过一个 allreduce 把头维度的结果合并回来。这样切的好处是每卡只算了 1/8 的注意力,显存和计算都分担了。
第二段是 MLP。第一个权重矩阵按列切,第二个权重矩阵按行切。前向传播的合并点用 f 标记,第一个线性层的输出经过 allreduce 后进入激活函数,这里是一个同步点;反向传播的合并点用 g 标记,第二个线性层的梯度在反向时需要先做一次 allreduce。f 和 g 的设计可以理解为"前向少一次通信、反向多一次"还是"反向少一次、前向多一次"的取舍,OpenClaw 选的是让前向只在 MLP 输出处同步一次,这样对推理延迟更友好。
4.2 流水线并行:1F1B 调度与气泡
流水线并行 OpenClaw 也在用,而且跨节点时是主力。它没有采用最朴素的"一个 batch 完整跑完再进下一个"的调度,而是用了 1F1B 调度(one-forward-one-backward),也就是每个 stage 交替执行一次前向和一次反向,尽量让所有设备都在工作。
流水线气泡本质上是不可避免的:第一个 stage 只能在最开始时向前计算若干个 micro-batch,最后一个 stage 在结束前要等待剩余 micro-batch 收尾。气泡占比大约等于 (p-1) / (m+p-1),p 是 stage 数,m 是 micro-batch 数。这个公式说明了两件事:
- stage 数越少,气泡越小。所以 OpenClaw 不会把模型切成几十个流水线 stage,宁可一个 stage 里放多层。
- micro-batch 数越多,气泡被填充得越满。因此配置里 micro_batch_size 不能太小。
我在配置里把 pipeline_parallel_size 从 4 调到 8,理论上显存压力更小,但实测吞吐反而下降,就是因为气泡变大抵消了显存收益。
4.3 OpenClaw 什么时候会跨节点用张量并行
有没有可能让张量并行跨节点?严格来说是支持的,但不是默认路径。只有当"单机内的显存连某一层都放不下"时,OpenClaw 才会把该层的张量切到不同机器上。这种情况通常出现在极大规模模型中,一层 MLP 的权重加中间激活已经超过单机总显存。
但是跨节点张量并行的代价非常大。前面算过,每一层要多次 allreduce,如果这些 allreduce 要跨交换机跑,延迟和带宽都会拖垮整体性能。OpenClaw 要求这类部署必须使用高带宽低延迟的互连网络,并且会把这些跨节点张量并行的卡尽量安排在同一交换机下,减少跨交换机的跳数。换句话说,它没有完全禁用张量并行跨节点,而是把这个问题推给了硬件拓扑。
4.4 一组模拟部署的对比数据
为了把三种策略的差距说得直观一些,我放一组基于 70B 级别模型、FP16 精度、总参数约 140GB 的模拟部署数据。硬件环境设定为 8 节点、每节点 8 卡、总计 64 卡,跨节点网络带宽按常见的 25Gbps 到 100Gbps 量级估算。数据不是官方 benchmark,但在我的环境里基本能反映趋势。
| 并行策略 | 显存峰值 | 单请求平均时延 | 吞吐量 | 主要瓶颈 |
|---|---|---|---|---|
| 纯流水线(8 节点) | 约 18GB | 较高,阶段间等待明显 | 中等 | 流水线气泡 |
| 张量并行 8 + 流水线 8 | 约 12GB | 较低,计算更均匀 | 最高 | 机内通信合流压力 |
| 跨节点张量并行 64 | 约 10GB | 最低但受网络波动 | 较低 | 跨节点 allreduce |
最值得关注的是第三行:显存占用最低,时延看着也很低,但吞吐反而差。原因是每次 allreduce 都要等所有节点到齐,任何一个节点网络抖动都会拉长整步时间。OpenClaw 在实践中宁可牺牲一点显存,也不愿意让跨节点通信成为常规路径。
5. 部署调优中容易被忽略的四个细节
5.1 分片不均匀带来的木桶效应
很多人以为分片就是"参数总数除以设备数",实际完全不是。模型里除了 Transformer block,还有 embedding 层和输出层。一个大词表的 embedding 可能占用好几 GB,如果你把它单独放在某一个 stage,那个 stage 的显存压力会远高于其他 stage,整体表现为显存分配不均,系统被迫降低 batch size 来迁就最紧张的那个节点。
OpenClaw 的处理方式是把 embedding 按词表维度切开,参与张量并行的多张卡各持一部分,前向时通过 allreduce 把查表结果合起来。如果词表非常大,还会把 embedding 层和最后的输出层放在同一个流水线 stage,因为权重通常是共享的,反向时梯度可以就地归约。我一开始没有做这一步,导致流水线中一个节点的显存使用率比别的节点高 30%,后来调整了分片归属才平衡。
5.2 超时重试要跟网络时延匹配
分布式环境里,网络超时是最容易被低估的参数。OpenClaw 默认的通信超时时间在普通局域网下够用,但我遇到过跨地域或经过多层交换的集群,RTT 本身就很高,默认超时时间内一个集合通信还没完成,系统就判定节点异常开始重试。重试又会产生新的网络负载,形成恶性循环。
调优建议很简单:先把节点间延迟测出来,再设置超时。假设两个节点间 ping 的 RTT 是 2ms,一次流水线 stage 传输需要 20ms,那超时至少应该给到百毫秒级。我在配置里把通信超时调到了默认值的 5 倍,故障率立刻下降,这个参数不是越大越好,但一定不能小于"最大单次通信时间 × 安全系数"。
5.3 长序列下通信量会被激活放大
分布式部署的通信量不只是权重决定的,序列长度和 batch size 会直接放大激活值。prefill 阶段用户的 prompt 如果很长,attention 的中间计算结果会非常占显存,同时也让每层张量并行的 allreduce 数据量变大。OpenClaw 在某些版本里引入了序列维度切分来缓解这个问题,但跨节点传输的总量依然会涨。
我在实测中发现,序列长度从 2K 涨到 8K,激活值翻 4 倍,流水线 stage 之间的单次传输从 80MB 涨到 320MB。这个规模在 25Gbps 网络下要传一秒钟左右,如果不用长序列专用的优化或把 batch 拆小,用户延迟会非常难看。推理服务一定要根据业务 Prompt 长度提前规划,而不是只看模型参数量。
5.4 显存估算应该在启动前做完
最后提醒一个基本功:启动分布式任务之前,先把显存估算做完。OpenClaw 的显存占用由四部分组成:模型权重、激活值、KV cache、临时计算缓冲。
- 模型权重:参数量 × 字节数,再除以张量并行度。
- 激活值:跟序列长度、batch size、层数相关,可以用 hidden size 乘以序列长度乘以 batch 再乘以一些经验系数粗略估算。
- KV cache:每层每头都要缓存 key 和 value,和 batch size、序列长度线性相关。
- 临时缓冲:某些矩阵乘法中间结果,一般占 1~2GB。
如果估算下来单卡峰值接近满显存,就一定要降 batch size 或减少并行度配置,否则一旦运行起来触发碎片,直接 OOM,整个分布式任务都要重启。
6. 问题复盘:OpenClaw 分布式部署的典型故障与排查
6.1 并行度乘积不等于卡数
我在一个 16 卡的小集群上第一次部署时,配置里写了 tensor_parallel_size=4、pipeline_parallel_size=4、data_parallel_size=2,理论上乘起来是 32,但实际只有 16 张卡。OpenClaw 启动时 rank 校验直接失败,日志提示"total world size does not match device count"。
这类问题看着低级,但配置多了确实容易算错。排查思路是先把总卡数分解成质因数组合,再决定三者的值。比如 16 张卡可以拆成 tp=2、pp=4、dp=2,也可以拆成 tp=4、pp=2、dp=2,具体怎么拆取决于显存压力和模型层数。稳妥的做法是把 tppp 的配置单独写在一个文件里,启动脚本读取后做一次校验,乘积不对就立刻报错,别等到集群调度完才发现。
6.2 多网卡未绑定导致全链路被慢卡拖死
有一次性能测试,大量数据流只在其中一块网卡上跑,另外一块网卡完全空闲。OpenClaw 的集合通信默认按 IP 选择网络路径,如果同一台机器有多个网卡却没有绑定或路由均衡,所有流量可能压在同一个网卡上,实际带宽直接减半。
排查方法是用流量监控工具看两块网卡的收发速率,如果差距明显,就要在系统层面做绑定,或者给 OpenClaw 指定参与通信的网卡列表。这个坑特别隐蔽,因为日志里看不到任何报错,只有性能数据曲线一直都在低水平徘徊。
6.3 权重分片加载顺序错乱
跨节点加载分片权重时,有一个非常容易出现的问题:每个节点按自己的 rank 去找权重文件,但 OpenClaw 切分权重时可能按层名排序,和实际流水线 stage 的设备顺序不一致。结果模型能启动,推理结果却完全不对。
这个问题让我排查了整整一下午,最后发现是权重文件命名规则和分片索引不一致。OpenClaw 的分片存储前需要先确认是按设备拓扑顺序生成索引,而不是按层名或参数名字典序。如果你用的是外部脚本导出权重,必须保证分片顺序和部署时的 world rank 严格一致。
6.4 一个快速诊断节点间带宽的小脚本
网络问题多了之后,我写了一个极简的带宽测试脚本,不依赖任何额外库,只靠标准库就能在任意两个节点间测出 raw TCP 吞吐。OpenClaw 部署前的硬件检查我都会先跑一遍这个:
python复制import socket, time, sys
def server(port):
s = socket.socket()
s.bind(("0.0.0.0", port))
s.listen(1)
conn, _ = s.accept()
buf = b"x" * (4 * 1024 * 1024)
start = time.time()
total = 0
while time.time() - start < 5:
conn.send(buf)
total += len(buf)
elapsed = time.time() - start
print(f"send throughput: {total / 1024 / 1024 / elapsed:.1f} MB/s")
conn.close()
def client(ip, port):
c = socket.create_connection((ip, port), timeout=10)
start = time.time()
total = 0
while time.time() - start < 5:
chunk = c.recv(4 * 1024 * 1024)
if not chunk:
break
total += len(chunk)
elapsed = time.time() - start
print(f"recv throughput: {total / 1024 / 1024 / elapsed:.1f} MB/s")
c.close()
if __name__ == "__main__":
if sys.argv[1] == "server":
server(int(sys.argv[2]))
elif sys.argv[1] == "client":
client(sys.argv[2], int(sys.argv[3]))
用法是一台机器起 server,另一台机器跑 client 指定对端 IP。如果测出来的吞吐远低于网卡标称值,先查网卡绑定和交换机端口,再考虑 OpenClaw 的参数问题。这个脚本只能反映裸网络质量,但绝大多数分布式性能问题都藏在网络这一层。
如果只是拿 OpenClaw 当单机工具用,永远感受不到这些坑;一旦走出单机,通信就不仅仅是"把数据传过去"那么简单。我在实际部署里最大的体会是:并行策略的选择不是越高级越好,张量并行和流水线并行没有绝对优劣,真正决定成败的是你的硬件拓扑和模型尺寸匹配不匹配。动手之前先把网络带宽、节点延迟、单层显存算清楚,再决定采用哪种组合,这个顺序一定不要反。
