news 2026/9/18 12:17:21

HCCL Reduce 集合通信示例深度解析:基于 HcclReduce API 的多 rank 数据归约实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
HCCL Reduce 集合通信示例深度解析:基于 HcclReduce API 的多 rank 数据归约实战指南

HCCL Reduce 集合通信示例深度解析:基于 HcclReduce API 的多 rank 数据归约实战指南

【免费下载链接】hccl集合通信库(Huawei Collective Communication Library,简称HCCL)是基于昇腾AI处理器的高性能集合通信库,为计算集群提供高性能、高可靠的通信方案项目地址: https://gitcode.com/cann/hccl

导读

本文以开源仓库 CANN/hccl 中的官方示例 examples/02_collectives/05_reduce 为骨架,完整剖析如何使用HcclReduce()在单机多卡(N >= 2)环境完成 Reduce 归约操作。你将掌握从设备探测、rootinfo 标识生成、多线程通信域初始化,到调用HcclReduce()完成"全 rank 求和、结果汇聚到 root 节点"的完整编码流程,并深入理解 API 参数语义、数据类型/归约操作支持矩阵、编译运行方式及底层实现约束,可直接对照源码复现并扩展到自己的集合通信程序中。

示例概述:一个进程内的多 rank Reduce

该示例在单个进程内通过多线程模拟 8 个 rank,每个线程绑定一个 NPU 设备,完成一次完整的 Reduce 通信,覆盖以下关键步骤:

  1. 调用aclrtGetDeviceCount()探测设备并查询可用设备数量;
  2. 调用HcclGetRootInfo(),以rank 0 作为 root生成 rootinfo 标识;
  3. 每个线程内调用HcclCommInitRootInfo(),基于同一份 rootinfo 初始化通信域;
  4. 调用HcclReduce()将所有 rank 的输入数据求和,结果发送到 root 节点并打印。

rootinfo 标识中封装了设备的 IP 地址与设备 ID,必须广播给集群内所有 rank,才能完成通信域的初始化对齐。

目录结构与构建入口

示例目录包含三个核心文件:

├── main.cc # 示例源码:多线程执行 Reduce ├── Makefile # 编译与运行配置 └── reduce # 编译生成的二进制可执行文件

Makefile 是理解编译链路的关键。它要求先设置ASCEND_HOME_PATH环境变量(由 CANN 的 set_env.sh 提供),若未设置会直接报错提示先执行环境变量 source。编译命令核心如下:

CXXFLAGS := -std=c++17 -Werror -fstack-protector-strong -fPIE -pie \ -O2 -s -Wl,-z,relro -Wl,-z,now -Wl,-z,noexecstack LIBS = -L$(ASCEND_LIB_DIR) -lhccl -lascendcl INCS = -I$(ASCEND_INC_DIR) TARGET = reduce all: g++ $(CXXFLAGS) $(SOURCES) $(INCS) $(LIBS) -o ${TARGET} test: ./$(TARGET)

可见示例同时链接了libhccl(集合通信库)与libascendcl(昇腾 ACL 运行时),并启用了-fPIE -pie、栈保护、RELRO 等安全加固选项。make test即直接运行生成的reduce可执行文件。

环境准备

支持的产品形态

示例面向**单机 N 卡(N >= 2)**配置,支持以下产品系列:

  • Ascend 950PR / Ascend 950DT
  • Atlas A3 训练系列产品 / Atlas A3 推理系列产品
  • Atlas A2 训练系列产品
  • Atlas 训练系列产品

这与 HcclReduce API 文档 中给出的产品支持矩阵一致(Atlas 推理系列产品不支持该接口)。

设置环境变量

# 设置 CANN 环境变量,以下以 root 用户默认安装路径为例 source /usr/local/Ascend/cann/set_env.sh

该命令同时会设置ASCEND_HOME_PATH,确保 Makefile 中的头文件路径${ASCEND_HOME_PATH}/include与库路径${ASCEND_HOME_PATH}/lib64可被正确解析。

编译与运行

在示例代码目录下依次执行:

make # 编译生成 reduce 可执行文件 make test # 运行 reduce

补充说明:可通过HCCL_OP_EXPANSION_MODE环境变量配置通信算子扩展模式,例如将通信算子扩展到 AI CPU 通信引擎:

export HCCL_OP_EXPANSION_MODE=AI_CPU

该变量的支持范围与产品型号强相关,不同型号的可选值及详细约束可参考仓库内的环境变量说明 HCCL_OP_EXPANSION_MODE。

源码逐段解析:主流程

1. ACL 运行时初始化与设备探测

main.cc 的入口先初始化 ACL 运行时,并探测设备数量:

ACLCHECK(aclInit(NULL)); // 设备资源初始化 uint32_t devCount; ACLCHECK(aclrtGetDeviceCount(&devCount)); // 查询可用设备数量 std::cout << "Found " << devCount << " NPU device(s) available" << std::endl; int32_t rootRank = 0; ACLCHECK(aclrtSetDevice(rootRank)); // 在 root 设备上生成 rootinfo

示例使用ACLCHECK宏统一做错误码检查(非ACL_SUCCESS即打印文件行号并返回),这是昇腾应用开发的标准防御式写法。

2. 生成 rootinfo 并广播

void* rootInfoBuf = nullptr; ACLCHECK(aclrtMallocHost(&rootInfoBuf, sizeof(HcclRootInfo))); HcclRootInfo* rootInfo = (HcclRootInfo*)rootInfoBuf; HCCLCHECK(HcclGetRootInfo(rootInfo));

HcclGetRootInfo()在 rank 0(root 设备)上生成包含设备 IP 与设备 ID 的 rootinfo。在多进程集群场景中,这份 rootinfo 需要通过 MPI、文件共享或网络等机制广播给所有进程;本例由于是单进程多线程模型,各线程直接共享同一份 rootinfo 指针即可。

3. 多线程构造多 rank 环境

std::vector<std::thread> threads(devCount); std::vector<ThreadContext> args(devCount); for (uint32_t i = 0; i < devCount; i++) { args[i].rootInfo = rootInfo; args[i].rootRank = static_cast<uint32_t>(rootRank); args[i].device = i; args[i].devCount = devCount; threads[i] = std::thread(Sample, (void*)&args[i]); } for (uint32_t i = 0; i < devCount; i++) { threads[i].join(); }

ThreadContext结构体(main.cc)把 rootinfo、root rank、设备号与 rank 总数打包传给每个线程。每个线程在其绑定的设备上独立执行完整流程:aclrtSetDevice→ 申请/初始化数据 →HcclCommInitRootInfo→ 建流 →HcclReduce→ 同步 → 拷贝结果 → 释放资源。

4. 线程内数据准备与通信域初始化

// 设置当前线程操作的设备 ACLCHECK(aclrtSetDevice(static_cast<int32_t>(device))); // 申请 Device 内存存放输入,初始化为 0,1,2,… 递增序列(第 i 个元素值为 i) ACLCHECK(aclrtMalloc(&sendBuf, mallocSize, ACL_MEM_MALLOC_HUGE_ONLY)); void* hostBuf = nullptr; ACLCHECK(aclrtMallocHost(&hostBuf, mallocSize)); float* tmpHostBuf = static_cast<float*>(hostBuf); for (uint64_t i = 0; i < count; ++i) { tmpHostBuf[i] = static_cast<float>(i); } // Host 数据拷贝到 Device ACLCHECK(aclrtMemcpy(sendBuf, mallocSize, hostBuf, mallocSize, ACL_MEMCPY_HOST_TO_DEVICE)); ACLCHECK(aclrtFreeHost(hostBuf)); // 申请 Device 内存用于接收 Reduce 结果 ACLCHECK(aclrtMalloc(&recvBuf, mallocSize, ACL_MEM_MALLOC_HUGE_ONLY)); // 基于 rootinfo 初始化集合通信域 HcclComm hcclComm; HCCLCHECK(HcclCommInitRootInfo(ctx->devCount, ctx->rootInfo, device, &hcclComm)); // 创建任务流 aclrtStream stream; ACLCHECK(aclrtCreateStream(&stream));

要点:所有 rank 的输入长度均为count = devCount(8 个 float),第 i 个元素初始化为 i,即每个 rank 的 sendBuf 都是[0, 1, 2, 3, 4, 5, 6, 7]

5. 核心调用:HcclReduce

HCCLCHECK(HcclReduce(sendBuf, recvBuf, count, HCCL_DATA_TYPE_FP32, HCCL_REDUCE_SUM, rootRank, hcclComm, stream)); ACLCHECK(aclrtSynchronizeStream(stream));

对应的接口声明位于 include/hccl.h:

extern HcclResult HcclReduce( void* sendBuf, void* recvBuf, uint64_t count, HcclDataType dataType, HcclReduceOp op, uint32_t root, HcclComm comm, aclrtStream stream);

参数语义如下:

参数方向说明
sendBuf输入源数据 buffer 地址
recvBuf输出目的数据 buffer 地址,集合通信结果输出至此
count输入参与 reduce 的数据个数(本例为 8)
dataType输入数据类型,本例为HCCL_DATA_TYPE_FP32
op输入归约操作类型,本例为HCCL_REDUCE_SUM(求和)
root输入作为 reduce root 的 rank id(本例为 0)
comm输入通信域句柄
stream输入本 rank 使用的任务流

HcclReduce的语义是:将集群内所有 rank 在相同位置上(按元素)执行指定的归约操作,并把归约结果放到 root 节点的 recvBuf 中。非 root 节点的 recvBuf 内容在该接口下无意义(本例中保持为 0)。

运行结果分析

运行输出如下(以 8 卡为例):

Found 8 NPU device(s) available rankId: 0, output: [ 0 8 16 24 32 40 48 56 ] rankId: 1, output: [ 0 0 0 0 0 0 0 0 ] rankId: 2, output: [ 0 0 0 0 0 0 0 0 ] rankId: 3, output: [ 0 0 0 0 0 0 0 0 ] rankId: 4, output: [ 0 0 0 0 0 0 0 0 ] rankId: 5, output: [ 0 0 0 0 0 0 0 0 ] rankId: 6, output: [ 0 0 0 0 0 0 0 0 ] rankId: 7, output: [ 0 0 0 0 0 0 0 0 ]

验证逻辑非常直观:8 个 rank 的第 i 个元素均为 i,按位求和后第 i 个元素为8 * i,因此 root 节点的输出为[0, 8, 16, 24, 32, 40, 48, 56]。其余非 root rank 的输出保持全 0,符合 Reduce 语义(只有 root 获得最终结果)。

注意源码中在拷贝结果前插入了std::this_thread::sleep_for(std::chrono::seconds(device))(main.cc),作用是让不同设备线程错峰打印,避免输出交错,不影响计算结果。

深入理解:接口约束与支持矩阵

数据类型支持范围

参照 HcclReduce API 文档:

  • Ascend 950PR/Ascend 950DT:int8、int16、int32、int64、uint64、float16、float32、float64、bfp16;
  • Atlas A3 训练/推理系列:int8、int16、int32、int64、float16、float32、bfp16;
  • Atlas A2 训练/推理系列:int8、int16、int32、int64、float16、float32、bfp16(int64 会有一定性能劣化);
  • Atlas 训练系列:int8、int32、int64、float16、float32。

归约操作支持范围

  • Ascend 950PR/Ascend 950DT、Atlas A3、Atlas A2:支持 sum、prod、max、min,其中prod 不支持 int16、bfp16数据类型;
  • Atlas 训练系列:支持 sum、prod、max、min。

从源码看,HcclReduceOp枚举值与字符串映射关系定义于 src/common/hccl_common.h,归约操作在算法内核中按类型分发执行,例如 AIV 通信内核中的case HcclReduceOp::HCCL_REDUCE_SUM(见 aiv_communication_base_v2.h),以及 CCU 内核中对 sum 操作与数据类型匹配的判断(见 ccu_kernel_utils.cc)。

返回值与错误码

接口返回 HcclResult,常见取值:

返回值说明
HCCL_SUCCESS接口调用成功
HCCL_E_PTR指针参数为空(comm、sendBuf、recvBuf、stream 等为 nullptr)
HCCL_E_PARA参数无效(如 count 超过上限、root 越界等)
HCCL_E_NOT_SUPPORT操作不被支持(如 dataType 非法或当前型号不支持、prod 配 int16/bfp16 等)
HCCL_E_INTERNAL内部错误

关键约束

  • 所有 rank 的countdataTypeop必须一致;
  • 输入输出地址需按数据类型对齐:int8 按 1 字节、int16/float16/bfp16 按 2 字节、int32/float32 按 4 字节、int64/uint64/float64 按 8 字节;
  • 同一 Device 上多个通信域的所有通信算子需保证串行下发,不允许乱序、多线程并发下发,也不支持线程重入;
  • 同一 Device 上、同一通信域内的通信算子下发线程必须使用相同的 Context。

这些约束解释了示例为何采用"每线程一个设备、各自独立建流"的结构——它天然规避了单设备上的并发下发问题。

资源释放与收尾

每个线程完成同步打印后,按逆序释放资源:

HCCLCHECK(HcclCommDestroy(hcclComm)); // 销毁通信域 ACLCHECK(aclrtFree(sendBuf)); // 释放 Device 内存 if (recvBuf != nullptr) { ACLCHECK(aclrtFree(recvBuf)); // 释放 Device 内存 } ACLCHECK(aclrtDestroyStream(stream)); // 销毁任务流 ACLCHECK(aclrtResetDevice(device)); // 重置设备

主线程在所有工作线程 join 后,释放 rootinfo 的 Host 内存并调用aclFinalize()完成设备去初始化。完整的"申请-使用-释放"闭环是保证多卡程序可重复运行、不泄漏资源的关键。

延伸:从单进程示例到真实集群

本示例用单进程多线程简化了 rank 拓扑(线程间共享 rootinfo)。在实际多机多卡部署中,需要把第 2 步生成的 rootinfo 通过集群通信手段(如 MPI、共享文件、RANK_TABLE_FILE 等)分发给所有进程,每个进程再各自调用HcclCommInitRootInfoHcclCommInitClusterInfo完成通信域建立。仓库的 示例总览 与 HcclReduce API 文档 提供了从单进程 demo 迁移到集群场景的接口基础;Reduce 算子在仓库中的算法实现(含多种拓扑模板与选择器)位于 src/ops/reduce/algorithm,需要深入算子内部机制时可进一步阅读。

小结

本文以 CANN/hccl 官方 Reduce 示例为主线,完整走通了"设备探测 → rootinfo 生成 → 通信域初始化 → HcclReduce 执行 → 结果验证 → 资源释放"的集合通信编程链路,并补充了数据类型/归约操作支持矩阵、错误码、对齐与并发约束等 API 级细节。对照 main.cc 与 Makefile 即可快速复现,将HCCL_REDUCE_SUM替换为HCCL_REDUCE_MAX/HCCL_REDUCE_MIN/HCCL_REDUCE_PROD、将HCCL_DATA_TYPE_FP32替换为其他支持类型,即可验证不同归约语义与数据类型下的行为,是理解 HCCL 集合通信编程模型的最佳起点。

【免费下载链接】hccl集合通信库(Huawei Collective Communication Library,简称HCCL)是基于昇腾AI处理器的高性能集合通信库,为计算集群提供高性能、高可靠的通信方案项目地址: https://gitcode.com/cann/hccl

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/18 12:16:54

辽宁专升本计算机真题PDF的结构化解析与知识图谱构建

简介&#xff1a;本资源为2022年辽宁专升本考试《计算机基础》科目真题试卷及权威参考答案&#xff0c;面向备考专升本的高职高专学生及计算机基础教学辅导教师&#xff0c;精准覆盖升学选拔核心考点与能力要求。试卷结构完整&#xff0c;含单选题&#xff08;20题&#xff09;…

作者头像 李华
网站建设 2026/9/18 12:15:20

CC Switch 指向 TaoToken:Kimi K2.7 Code 与 MiniMax M3 切换预设怎么存

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/18 12:14:35

做 WorkBuddy 的 Skill 自动化,TaoToken 只提供 Key

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/18 12:13:44

从文本到向量:基于Redis Stack Server的向量化检索实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/18 12:13:18

VS Code配置C/C++开发环境:从编译器到调试的全流程指南

如果你也在 Windows 上用 VS Code 写 C/C&#xff0c;那你一定见过这些场面&#xff1a;装了扩展&#xff0c;写了 Hello World&#xff0c;按下 F5&#xff0c;结果要么弹出“g 不是内部或外部命令”&#xff0c;要么程序窗口一闪而过&#xff0c;要么代码明明编译通过&#x…

作者头像 李华