目录
[1 为什么有custom_all_reduce](#1 为什么有custom_all_reduce)
[2 节点内的Custom AllReduce](#2 节点内的Custom AllReduce)
[2.1 节点内的Custom AllReduce大体原理](#2.1 节点内的Custom AllReduce大体原理)
[2.2 IPC(HIP IPC)](#2.2 IPC(HIP IPC))
[3 跨节点的Custom AllReduce](#3 跨节点的Custom AllReduce)
[3.1 跨节点的Custom AllReduce大体原理](#3.1 跨节点的Custom AllReduce大体原理)
[3.2 Fabric(HSA RPC Fabric)](#3.2 Fabric(HSA RPC Fabric))
[4 那么IPC和跨机的Fabric通信区别在哪里](#4 那么IPC和跨机的Fabric通信区别在哪里)
[4.1 共同流程(IPC 和 Fabric 都一样)](#4.1 共同流程(IPC 和 Fabric 都一样))
[4.2 区别](#4.2 区别)
[4.3 IPC 模式:其实就是用了HIP API实现了共享显存的原理](#4.3 IPC 模式:其实就是用了HIP API实现了共享显存的原理)
[1. 分配显存](#1. 分配显存)
[2. 导出句柄(64 字节)](#2. 导出句柄(64 字节))
[3. 交换句柄(控制面)](#3. 交换句柄(控制面))
[4. 对端打开句柄](#4. 对端打开句柄)
[5. 限制](#5. 限制)
[4.4 Fabric 模式:其实就是用了hsa的api实现了共享显存](#4.4 Fabric 模式:其实就是用了hsa的api实现了共享显存)
[1. 分配显存](#1. 分配显存)
[2. 导出句柄(256 字节)](#2. 导出句柄(256 字节))
[3. 交换句柄(控制面)](#3. 交换句柄(控制面))
[4. 对端 attach 句柄](#4. 对端 attach 句柄)
[5. 能力](#5. 能力)
[4.5 区别对比表格](#4.5 区别对比表格)
[4.6 IPC 那套 API 属于谁?](#4.6 IPC 那套 API 属于谁?)
[4.7 Fabric 那套 API 属于谁?](#4.7 Fabric 那套 API 属于谁?)
[5 什么是HSA](#5 什么是HSA)
[HSA 是什么?](#HSA 是什么?)
[HSA Runtime 是什么?](#HSA Runtime 是什么?)
[和 HIP 是什么关系?](#和 HIP 是什么关系?)
abstract:
heterogeneous 英/ ˌhetərəˈdʒiːniəs / 有很多种类组成的;(化)不均一的,多相的;(数)不纯一的,参差的, 这里是异构的意思,
fabric 英/ ˈfæbrɪk / 布料,织物;构造,结构;(汽车的)车身,(飞机的)机身 这里是Fabric 在此指超节点 GPU 互联结构;跨节点 CAR 通过 HSA RPC Fabric API 完成显存导出与映射,而不是普通以太网/IB 发包。
一句话概括:Custom AllReduce 的快路径都是通过共享/映射 GPU 显存完成通信------节点内靠 HIP(或 CUDA)IPC 句柄,跨节点超节点靠 HSA Fabric 句柄;差别主要在用什么 API 导出和打开显存句柄。
HSA = Heterogeneous System Architecture(异构系统架构)
1 为什么有custom_all_reduce
Custom AllReduce 是 vLLM 为 Tensor Parallel 场景实现的一条专用 AllReduce 快路径。在满足条件时,它不经过 PyNccl/RCCL 这类通用集体通信,而是通过 vLLM 自研的 GPU 核函数(vllm._custom_ops),配合 GPU 显存 IPC/P2P 直连来完成求和;条件不满足时会自动回退到 NCCL/RCCL。
具体代码路径在 vllm/distributed/device_communicators/custom_all_reduce.py
2 节点内的Custom AllReduce
2.1 节点内的Custom AllReduce大体原理
- 控制面:用
torch.distributed(non-NCCL 的cpu_group,一般是 Gloo)交换 IPC handle - 数据面:用 vLLM 自研 C++ 扩展
vllm._custom_ops,通过 CUDA IPC / HIP IPC 让同机 GPU 直接访问彼此显存 - 计算:自定义 all-reduce kernel 在 GPU 上直接做 reduce
关键代码:
python
def create_shared_buffer(...):
pointer, handle = ops.allocate_shared_buffer_and_handle(size_in_bytes)
...
dist.all_gather_object(handles, handle, group=group) # 控制面:交换 handle
...
pointers.append(ops.open_mem_handle(h)) # 数据面:打开对端显存
以及真正执行 reduce:
python
ops.all_reduce(self._ptr, inp, out, 0, 0) # 自定义 kernel,不是 torch
2.2 IPC(HIP IPC)
你的 GPU 显存 ──transport──> 别的 GPU 能直接读到这块显存 ──> kernel 做 reduce
| 项目 | 说明 |
|---|---|
| 全称 | Inter-Process Communication,GPU 显存句柄共享 |
| 范围 | 同一台物理机(同一 OS、同一 shared-memory namespace) |
| 原理 | 本机 GPU A 导出显存 handle(64 字节),GPU B 用 open_mem_handle 打开,直接访问 A 的显存 |
| 谁在用 | vLLM 原版、aiter 同节点模式 |
| 限制 | 跨物理机不行------两台服务器之间没法用 IPC |
IPC 确实是「进程间通信」
你没理解错。IPC = Inter-Process Communication(进程间通信)。
在 CAR 这里,它特指的是 GPU 显存的 IPC(HIP IPC / CUDA IPC),不是泛指 socket、MPI 那种通用进程通信:
进程 A(GPU0) 导出显存 handle
↓
进程 B(GPU1) 打开 handle,直接访问 GPU0 的显存
所以:
- 名字上:是进程间通信
- 实现上:是 GPU 显存句柄共享 + 直连访问
- 范围上:同一台物理机(同一 OS、同一 shared-memory namespace)
3 跨节点的Custom AllReduce
3.1 跨节点的Custom AllReduce大体原理
aiter 在同样思路上做了超节点扩展,数据面多了一条 Fabric 路径:
| transport | 数据面 | 适用场景 |
|---|---|---|
ipc |
HIP IPC(和 vLLM 类似) | 同节点 |
fabric |
HSA RPC / Fabric 显存共享 | 跨节点超节点 |
auto |
同节点 IPC,跨节点 Fabric | 自动选择 |
aiter 初始化时会选 transport:
python
if self.requested_transport == "ipc":
if not same_node: return # 跨节点不能用 IPC
self.transport = "ipc"
elif self.requested_transport == "auto":
self.transport = "ipc" if same_node else "fabric"
else:
self.transport = "fabric" # 强制超节点 Fabric
3.2 Fabric(HSA RPC Fabric)
| 项目 | 说明 |
|---|---|
| 范围 | 跨物理机(超节点:多台服务器通过专用互联) |
| 原理 | 用 hsa_ext_rpc_memory_create/attach/detach 做跨节点显存导出和映射 |
| handle 大小 | 256 字节(IPC 是 64 字节) |
| 谁在用 | aiter 超节点模式 |
| 前提 | 编译时 AITER_ENABLE_SUPERNODE_AR=1,运行时 AITER_AR_TRANSPORT=fabric |
Fabric 也是「共享显存」思路,只是跨了物理机
可以这么理解:Fabric 把「导出显存 → 对端 attach → 直接访问」这套模型,从单机扩展到了跨节点。
aiter 文档里写得很直白:
| 层次 | 做什么 |
|---|---|
| 跨节点内存导出/映射 | hsa_ext_rpc_memory_create/attach/detach |
| 实际 AllReduce | kernel 直接访问 Fabric 映射后的地址 |
4 那么IPC和跨机的Fabric通信区别在哪里
4.1 共同流程(IPC 和 Fabric 都一样)
每个 rank 在本机 GPU 上分配一块显存
每个 rank 把这块显存「导出」成一个 handle(句柄)
通过控制面把 handle 发给所有 rank(TCPStore / torch.distributed)
每个 rank 用对端 handle「打开/attach」,得到本进程里可访问的指针
kernel 直接通过这些指针 读/写 远端显存,完成 all-reduce
4.2 区别
其实本质区别就在于:怎么把本机 GPU 显存「导出」成句柄,以及别的 rank 怎么「打开/attach」这个句柄,拿到可访问的地址。
其实往下看就能知道,它们两者相当于用了不同的api去实现了共享显存
4.3 IPC 模式:其实就是用了HIP API实现了共享显存的原理
1. 分配显存
hipMalloc / hipExtMallocWithFlags(...) // 本机 GPU 显存
2. 导出句柄(64 字节)
hipIpcGetMemHandle(&handle, gpu_ptr)
得到 hipIpcMemHandle_t(64 字节),描述「这块本机 GPU 显存」。
vLLM 里对应:
custom_all_reduce.cuLines 167-174
// Create IPC memhandle for the allocated buffer.
cudaIpcGetMemHandle((cudaIpcMemHandle_t*)handle.data_ptr(), buffer);
3. 交换句柄(控制面)
dist.all_gather_object(handles, handle, group=group) # vLLM
或 aiter 用 TCPStore pickle 交换
4. 对端打开句柄
hipIpcOpenMemHandle(&mapped_ptr, peer_handle, hipIpcMemLazyEnablePeerAccess)
在本进程地址空间里,映射到同一台机器上另一块 GPU 的显存。
5. 限制
- 必须同一物理机(同一 OS、同一 shared-memory namespace)
- 依赖 GPU P2P / XGMI
- 跨物理机时
hipIpcOpenMemHandle无效
4.4 Fabric 模式:其实就是用了hsa的api实现了共享显存
1. 分配显存
hipExtMallocWithFlags(..., hipDeviceMallocUncached) // 或 finegrained
要求这块显存可被 Fabric 导出。
2. 导出句柄(256 字节)
hsa_ext_rpc_memory_create(gpu_ptr, size, &fabric_handle)
得到 hsa_ext_rpc_memory_t(256 字节),不是 HIP IPC handle。
aiter 源码:
custom_all_reduce.cu Lines 404-425
python
void get_meta_buffer_handle(..., transport) {
if (transport == IPC)
hipIpcGetMemHandle(...); // 64 字节
else
hsa_ext_rpc_memory_create(...); // 256 字节
}
3. 交换句柄(控制面)
和 IPC 同一套机制(aiter 的 _gather_ipc_meta,走 TCPStore),只是 handle 更大、内容不同。
4. 对端 attach 句柄
hsa_ext_rpc_memory_attach(&fabric_handle, 1, &fabric_agent, &mapped_ptr)
通过 HSA RPC Fabric agent,把远端物理机 GPU 显存映射到本机可访问地址。
aiter C++ 里:
custom_all_reduce.cuh Lines 2623-2646
python
char* open_peer_handle(const void* handle) {
if (transport == IPC)
return open_ipc_handle(handle); // hipIpcOpenMemHandle
// Fabric:
hsa_ext_rpc_memory_attach(&fabric_handle, 1, &fabric_agent_, &mapped_ptr);
return mapped_ptr;
}
5. 能力
- 可跨物理机(超节点专用互联)
- 需要编译
AITER_ENABLE_SUPERNODE_AR=1 - 需要链
libhsa-runtime64
4.5 区别对比表格
| 步骤 | IPC | Fabric |
|---|---|---|
| 分配显存 | hipMalloc / uncached |
uncached / finegrained |
| 导出 API | hipIpcGetMemHandle |
hsa_ext_rpc_memory_create |
| 句柄类型 | hipIpcMemHandle_t(64B) |
hsa_ext_rpc_memory_t(256B) |
| 句柄交换 | TCPStore / all_gather | 一样 |
| 打开 API | hipIpcOpenMemHandle |
hsa_ext_rpc_memory_attach |
| 映射结果 | 同机另一 GPU 显存地址 | 跨机远端 GPU 显存地址 |
| 范围 | 单机 | 多机超节点 |
4.6 IPC 那套 API 属于谁?
属于 HIP(ROCm 运行时),不是 aiter 自己的 API。
| API | 所属库 |
|---|---|
hipMalloc / hipExtMallocWithFlags |
HIP Runtime(libamdhip64) |
hipIpcGetMemHandle |
HIP IPC API(仍在 HIP/ROCm 里) |
hipIpcOpenMemHandle |
HIP IPC API |
在 NVIDIA 上,vLLM 用的是同一思路的 CUDA Runtime API:
cudaMalloccudaIpcGetMemHandlecudaIpcOpenMemHandle
4.7 Fabric 那套 API 属于谁?
不属于 aiter,属于 HSA Runtime(libhsa-runtime64) 的超节点扩展 API。
| API | 所属库 |
|---|---|
hsa_ext_rpc_memory_create |
HSA RPC Fabric 扩展 |
hsa_ext_rpc_memory_attach |
HSA RPC Fabric 扩展 |
hsa_ext_rpc_memory_detach |
HSA RPC Fabric 扩展 |
hsa_iterate_agents 等 |
标准 HSA Runtime |
aiter 文档写得很清楚:跨节点内存导出/映射用的是 hsa_ext_rpc_memory_create/attach/detach,并且 .so 会链接 libhsa-runtime64。
所以 Fabric API 是 AMD超节点平台(HSA + Fabric 互联) 提供的,不是 aiter 发明的。
5 什么是HSA
HSA 是什么?
HSA = Heterogeneous System Architecture(异构系统架构)
最初由 AMD 等推动的一套规范,目标是让 CPU 和 GPU(以及其它加速器)在同一套系统模型下协同工作,包括:
- 统一的「设备/agent」抽象
- 统一的内存与访问模型
- 设备发现、队列、同步等运行时能力
可以把它看成:比 HIP 更底层的一整套「异构计算平台规范 + 运行时基础设施」。
HSA Runtime 是什么?
HSA Runtime 是 HSA 规范的具体实现库,在 Linux 上通常是:
它提供的是运行时 API,例如:
| 能力 | 典型 API |
|---|---|
| 发现设备 | hsa_iterate_agents(枚举 CPU、GPU 等 agent) |
| 内存管理 | 各类 HSA 内存分配/映射 API |
| 队列与同步 | 提交任务、信号量等 |
| 超节点扩展 | hsa_ext_rpc_memory_create/attach/detach(Fabric 跨节点显存) |
你们 aiter Fabric 模式里用的 hsa_ext_rpc_memory_*,就是 HSA Runtime 提供的扩展 API,不是 aiter 自己定义的。
和 HIP 是什么关系?
在 AMD/海光 ROCm 生态里,可以粗略看成两层:
你写 kernel、跑算子
↓
HIP ← 类似 CUDA,日常开发主要用这个
↓
HSA Runtime ← 更底层,管 agent、部分内存/互联能力
↓
驱动 / 硬件
| HIP | HSA Runtime |
|-------|------------------------------|---------------------------------|
| 角色 | GPU 编程接口(写 kernel、malloc 显存) | 异构系统运行时(设备模型、底层互联) |
| 类比 | CUDA | 比 CUDA Driver 更「系统级」的一层 |
| 开发者 | 经常直接写 | 多数时候不直接写,库/驱动会调 |
| 在你们场景 | IPC 用 hipIpcGetMemHandle | Fabric 用 hsa_ext_rpc_memory_* |
所以:
- IPC:走 HIP 自己的同机显存句柄机制
- Fabric:跨机时 HIP IPC 不够,要借助 HSA Runtime 的 Fabric/RPC 内存扩展