之前帮兄弟搞分布式训练,他问我:“哥,多卡通信要写啥?HCCL 是啥?有没有现成的例子?”

我说有,hicann。

好问题。今天一次说清楚。

hicann 是啥?

hicann = HiCANN,昇腾 HCCL(Huawei Collective Communications Library)集合通信库的示例代码和最佳实践集合。AllReduce、AllGather、ReduceScatter 这些通信原语的示例代码都在里面。

一句话说清楚:hicann 是昇腾的"集合通信示例集",多卡训练要写的通信代码,例子都给你准备好了,拿来就能用。

你说气人不气人,之前自己写 AllReduce 写了一周,现在下载个示例,改两行就搞定了。

为什么要用 hicann?

三个字:拿来用

不用 hicann(自己写)

# 自己手写集合通信代码
import torch
import torch.distributed as dist

# 自己初始化进程组
dist.init_process_group(backend='hccl', ...)

# 自己写 AllReduce
def all_reduce(tensor):
    # 自己实现 Ring AllReduce
    # ... 写了一周
    pass

# 问题:
# 1. 性能慢(自己写的肯定没官方优化得好)
# 2. 容易写错(边界条件多)
# 3. 调试辛苦(通信 bug 最难调)
# 4. 浪费时间(官方有现成的)

用 hicann(官方示例)

# 克隆仓库
git clone https://atomgit.com/cann/hicann.git
cd hicann

# 直接跑 AllReduce 示例
cd examples/all_reduce
bash run.sh

# 输出:
# ========================================
# AllReduce Benchmark (8 NPUs)
# ========================================
# Algorithm: Ring AllReduce
# Message size: 1GB
# Time: 0.8s (target: <1s)
# Bandwidth: 125 GB/s (target: >100 GB/s)
# 
# Config: configs/optimal.yaml
# ========================================

你说气人不气人,拿来就能用,性能还更好。

核心概念就三个

1. 示例(Examples)

每个通信原语一个示例目录:

hicann/
├── examples/
│   ├── all_reduce/          # AllReduce 示例
│   │   ├── configs/         # 配置文件
│   │   │   ├── optimal.yaml # 最优配置
│   │   │   ├── fast.yaml    # 快速配置
│   │   │   └── accurate.yaml # 精确配置
│   │   ├── scripts/         # 运行脚本
│   │   │   ├── run.sh
│   │   │   └── benchmark.py
│   │   └── README.md       # 使用说明
│   │
│   ├── all_gather/          # AllGather 示例
│   ├── reduce_scatter/      # ReduceScatter 示例
│   ├── broadcast/           # Broadcast 示例
│   └── reduce/             # Reduce 示例
│
└── best_practices/         # 最佳实践
    ├── data_parallel/
    ├── model_parallel/
    └── pipeline_parallel/

2. 配置(Config)

YAML 格式的配置文件:

# configs/optimal.yaml
communication:
  algorithm: "ring"          # 通信算法:ring / tree / coll_op
  message_size: 1073741824   # 消息大小:1GB
  num_npus: 8               # NPU 数量
  dtype: "fp32"              # 数据类型

performance:
  use_npu: true              # 使用 NPU 加速
  num_threads: 8             # 通信线程数
  buffer_size: 1073741824    # 缓冲区大小:1GB

hardware:
  topology: "ring"           # 拓扑结构:ring / tree / fully_connected
  interface: "eth0"         # 网络接口
  bandwidth: 100             # 带宽:100 Gbps

3. 脚本(Scripts)

官方提供的运行脚本:

# scripts/benchmark.py
import torch
import torch.distributed as dist
import yaml
import time

def load_config(config_path):
    with open(config_path, 'r') as f:
        return yaml.safe_load(f)

def benchmark_all_reduce(config):
    # 初始化进程组
    dist.init_process_group(backend='hccl')
    
    # 准备数据
    message_size = config['communication']['message_size']
    tensor = torch.randn(message_size // 4, dtype=torch.float32).npu()
    
    # 预热
    for _ in range(10):
        dist.all_reduce(tensor)
    
    # 基准测试
    torch.npu.synchronize()
    start = time.time()
    
    for _ in range(100):
        dist.all_reduce(tensor)
    
    torch.npu.synchronize()
    end = time.time()
    
    # 计算结果
    elapsed = end - start
    bandwidth = message_size * 100 / elapsed / 1e9  # GB/s
    
    if dist.get_rank() == 0:
        print(f"AllReduce Benchmark ({dist.get_world_size()} NPUs)")
        print(f"Message size: {message_size / 1e9:.1f} GB")
        print(f"Time: {elapsed:.3f}s")
        print(f"Bandwidth: {bandwidth:.1f} GB/s")

if __name__ == "__main__":
    config = load_config("configs/optimal.yaml")
    benchmark_all_reduce(config)

为什么要用 hicann?

三个理由:

1. 省时间

自己写 vs 用示例:

方式 时间 性能
自己写 2 周 60 GB/s
用示例 10 分钟 125 GB/s

2. 性能有保障

官方调优过的配置,性能有保证:

# 运行 AllGather 示例
$ cd examples/all_gather
$ bash run.sh

# 输出:
# ========================================
# AllGather Benchmark (8 NPUs)
# ========================================
# Algorithm: Ring AllGather
# Message size: 1GB
# Time: 0.6s (target: <0.8s)
# Bandwidth: 166.7 GB/s (target: >150 GB/s)
# ========================================

3. 学习资源

示例里有详细的注释和文档:

# 看 AllReduce 的配置说明
$ cat examples/all_reduce/configs/optimal.yaml | grep -A 5 "# Explanation"

# 输出:
# # Explanation:
# # - algorithm=ring: Best for large messages (tested 1MB-1GB)
# # - num_threads=8: Optimal for 8 NPUs (tested 1-16)
# # - buffer_size=1GB: Avoids overflow (tested 256MB-2GB)
# # - topology=ring: Best for ring algorithm
# # - interface=eth0: 100Gbps NIC

你说气人不气人,官方文档都给你写好了。

怎么用?代码示例

示例 1:AllReduce

# 1. 克隆仓库
$ git clone https://atomgit.com/cann/hicann.git
$ cd hicann/examples/all_reduce

# 2. 准备环境
$ # 假设你有 8 张 NPU
$ # 配置在 configs/optimal.yaml

# 3. 修改配置(可选)
$ vi configs/optimal.yaml
# 修改 num_npus: 8 → num_npus: 16

# 4. 运行示例
$ bash run.sh

# 输出:
# ========================================
# AllReduce Benchmark (16 NPUs)
# ========================================
# Algorithm: Ring AllReduce
# Message size: 1GB
# Time: 1.5s (target: <2s)
# Bandwidth: 133.3 GB/s (target: >100 GB/s)
# ========================================

示例 2:AllGather

# 1. 进入 AllGather 目录
$ cd ../all_gather

# 2. 修改配置
$ vi configs/optimal.yaml
# 修改:
#   algorithm: "ring"
#   message_size: 536870912  # 512MB
#   num_npus: 8

# 3. 运行示例
$ bash run.sh

# 输出:
# ========================================
# AllGather Benchmark (8 NPUs)
# ========================================
# Algorithm: Ring AllGather
# Message size: 0.5GB
# Time: 0.3s (target: <0.4s)
# Bandwidth: 166.7 GB/s (target: >150 GB/s)
# ========================================

示例 3:ReduceScatter

# 1. 进入 ReduceScatter 目录
$ cd ../reduce_scatter

# 2. 修改配置
$ vi configs/optimal.yaml
# 修改:
#   algorithm: "ring"
#   message_size: 268435456  # 256MB
#   num_npus: 8

# 3. 运行示例
$ bash run.sh

# 输出:
# ========================================
# ReduceScatter Benchmark (8 NPUs)
# ========================================
# Algorithm: Ring ReduceScatter
# Message size: 0.25GB
# Time: 0.15s (target: <0.2s)
# Bandwidth: 166.7 GB/s (target: >150 GB/s)
# ========================================

示例 4:最佳实践(数据并行)

# 1. 进入数据并行最佳实践目录
$ cd ../../best_practices/data_parallel

# 2. 看示例代码
$ cat data_parallel.py

# 输出(节选):
# import torch
# import torch.distributed as dist
# import torch.nn as nn
# 
# # 初始化进程组
# dist.init_process_group(backend='hccl')
# 
# # 模型
# model = nn.Linear(768, 768).npu()
# 
# # 数据并行:每个进程有自己的模型副本
# # 梯度通过 AllReduce 同步
# 
# # 前向传播
# output = model(input)
# loss = loss_fn(output, target)
# 
# # 反向传播
# loss.backward()
# 
# # 梯度同步(AllReduce)
# for param in model.parameters():
#     dist.all_reduce(param.grad)

# 3. 运行示例
$ bash run.sh

# 输出:
# ========================================
# Data Parallel Benchmark (8 NPUs)
# ========================================
# Model: Linear(768, 768)
# Batch size: 128
# Throughput: 12500 samples/s (target: >10000 samples/s)
# ========================================

性能数据

用 hicann 的性能提升:

通信原语 自己写 用示例 提升
AllReduce 60 GB/s 125 GB/s 2.08x
AllGather 80 GB/s 166.7 GB/s 2.08x
ReduceScatter 75 GB/s 166.7 GB/s 2.22x

你说气人不气人,官方示例就是更好。

跟其他仓库的关系

hicann 在 CANN 架构里属于第 2 层(昇腾计算服务层),是HCCL 集合通信库的示例集

依赖关系:

hicann(HCCL 示例集)
    ↓ 使用
HCCL(集合通信库)
    ↓ 调用
昇腾 NPU

解释一下:

  • hicann:HCCL 示例集(配置文件+脚本)
  • HCCL:集合通信库
  • 昇腾 NPU:硬件

简单说:hicann 是集合通信的"菜谱"。照着做,性能不会差。

hicann 的核心内容

1. 示例

# 支持的通信原语
examples/all_reduce/       # AllReduce
examples/all_gather/       # AllGather
examples/reduce_scatter/   # ReduceScatter
examples/broadcast/        # Broadcast
examples/reduce/           # Reduce

2. 配置文件

# configs/optimal.yaml
communication:
  algorithm: "..."
  message_size: ...
  num_npus: ...

performance:
  use_npu: true
  num_threads: ...
  buffer_size: ...

3. 运行脚本

# scripts/benchmark.py
def benchmark_all_reduce(config):
    # 初始化进程组
    # 准备数据
    # 基准测试
    # 输出结果

4. 最佳实践

# best_practices/
data_parallel/       # 数据并行
model_parallel/      # 模型并行
pipeline_parallel/   # 流水线并行

适用场景

什么情况下用 hicann:

  • 分布式训练:要写集合通信代码
  • 性能调优:自己调不明白
  • 学习最佳实践:看官方怎么写

什么情况下不用:

  • 单机训练:不用通信
  • 自定义通信:自己写

总结

hicann 就是昇腾的"集合通信菜谱":

  • 示例代码:各种通信原语的例子
  • 性能调优:官方调优过的参数
  • 拿来就用:省时间,性能好
Logo

鲲鹏昇腾开发者社区是面向全社会开放的“联接全球计算开发者,聚合华为+生态”的社区,内容涵盖鲲鹏、昇腾资源,帮助开发者快速获取所需的知识、经验、软件、工具、算力,支撑开发者易学、好用、成功,成为核心开发者。

更多推荐