HCCL 管着 AllReduce、AllGather 这些高层集合通信操作。这些操作最终拆解成最基本的通信原语------Send、Recv、Broadcast。hcomm 就是负责这些底层通信原语的仓库。
HCCL 和 hcomm 的分工清晰:HCCL 负责通信算法(Ring AllReduce 怎么切分数据、树形 AllGather 怎么组织),hcomm 负责把这些算法翻译成硬件上的实际收发操作。
hcomm 与 HCCL 的关系
通信栈的分层:
应用层:torch.distributed.all_reduce
↓
通信库层:HCCL------通信算法实现
↓
通信算子层:hcomm------底层 Send/Recv 原语
↓
硬件层:HCCS 互联 / RoCE 网络
HCCL 的 AllReduce 拆解为 ReduceScatter + AllGather。其中 ReduceScatter 内部调用 hcomm 的 Send 和 Recv 原语来传输数据块。
cpp
// HCCL 的 ReduceScatter 简化实现
// 内部调用 hcomm 原语
void ReduceScatter(float* send_buf, float* recv_buf, int count, int rank, int nranks) {
// 切分数据
int chunk_size = count / nranks;
int recv_chunk = (rank - 1 + nranks) % nranks;
int send_chunk = rank;
for (int step = 0; step < nranks - 1; step++) {
// hcomm 的发送和接收原语
hcomm::Send(send_buf + send_chunk * chunk_size, chunk_size, next_rank);
hcomm::Recv(temp_buf, chunk_size, prev_rank);
// 在接收到的数据上做元素求和
for (int i = 0; i < chunk_size; i++) {
send_buf[send_chunk * chunk_size + i] += temp_buf[i];
}
send_chunk = (send_chunk - 1 + nranks) % nranks;
recv_chunk = (recv_chunk - 1 + nranks) % nranks;
}
}
HCCL 管"数据怎么切"和"每步传给谁",hcomm 管"怎么把数据发过去"和"怎么把数据接回来"。
Tensor 同步为什么会成为瓶颈
分布式训练中每步的通信量由模型大小决定。LLaMA-70B 的梯度 Tensor 约 140GB。Ring AllReduce 把 8 卡场景的每卡通信量降到约 35GB。
35GB 的传输在 HCCS(100GB/s)上需要约 350ms。训练一个 step 的前向+反向计算约 500ms。通信占了 40% 的时间。
hcomm 级别的 Send/Recv 延迟包括:
- DMA 准备。 hcomm 在发送前需要把数据从发送方的计算 Buffer 搬移到通信 Buffer------约 5-15μs
- 链路传输。 数据在 HCCS 链路上传输------跟数据量成正比
- DMA 完成。 接收方从通信 Buffer 搬回计算 Buffer------约 5-15μs
优化方向:hcomm 支持零拷贝路径------发送方直接读计算 Buffer 做 DMA 传输,不经过通信 Buffer。零拷贝适用于训练中的梯度同步(数据已经在显存中),省掉两次 Buffer 搬运。
昇腾通信链路解析
卡 0 向卡 1 发送数据的完整链路:
卡 0:
hcomm::Send(buffer, size, rank=1)
→ 通信 Buffer 分配
→ DMA 从计算 Buffer → 通信 Buffer(如果走非零拷贝)
→ HCCS 链路传输启动
→ Doorbell 通知卡 1
HCCS 链路:
数据从卡 0 的显存 → PCIe/HCCS → 卡 1 的显存
卡 1:
HCCS 控制器收到 Doorbell 信号
→ DMA 从通信 Buffer → 计算 Buffer
→ 通知 hcomm::Recv 调用者"数据已就绪"
整个过程约 10-15μs 的固定开销 + 数据量 / 带宽的可变开销。
分布式推理中的通信
推理场景的通信模式跟训练不同。训练是 AllReduce(每张卡发数据给所有卡),推理是 AllGather(每张卡从所有卡收集计算分片)。
以 8 卡张量并行推理 LLaMA-13B 为例:
Attention 计算时每张卡持有 1/8 的 Head。
每张卡算完自己的 Attention 分片后,需要把所有分片拼起来得到完整的 Attention 输出。
→ AllGather 操作
→ 每张卡把本卡的分片广播给所有卡
→ 每张卡持有完整结果
AllGather 的通信量 = (n-1) × slice_size / n。Attention Head 分片大小 = B × n_heads × seq_len × head_dim / 8。Batch=1、n=4096 时单步约 4MB。8 卡 AllGather 的通信量约 28MB。
hcomm 在 AllGather 场景中支持 Ring 算法和 Tree 算法两种实现。Ring 适合小数据量(延迟低),Tree 适合大数据量(带宽高)。HCCL 根据数据量自动选择。
hcomm 的数据传输路径
hcomm 的 Send 操作涉及的数据路径:
- 发送方将数据从计算 Buffer 拷贝到通信 Buffer(如果是零拷贝模式则跳过)
- 通信 Buffer 的物理地址注册到 DMA 引擎
- DMA 引擎透过 HCCS 或 PCIe 将数据传输到接收方的通信 Buffer
- 接收方将数据从通信 Buffer 拷贝到计算 Buffer(零拷贝模式也跳过)
非零拷贝路径涉及 2 次显存拷贝,零拷贝路径 0 次。零拷贝的代价是需要额外的地址映射------通信双方需要预先注册显存段的访问权限。这个注册在通信域初始化时一次性完成。
hcomm 在推理场景中的使用
推理场景中的 hcomm 主要用于张量并行通信。LLaMA-13B 在 8 卡上做张量并行推理时,每步 Attention 计算需要跨卡 AllGather 结果。hcomm 的 AllGather 原语通过一条 hcomm::AllGather(send_buf, recv_buf, count, comm) 调用完成------内部自动拆解为 Ring 算法或 Tree 算法的 Send/Recv 序列。