当前位置: 首页 > news >正文

hccl:昇腾 NPU 的“多卡通信库”

hccl:昇腾 NPU 的“多卡通信库”

之前帮朋友看多 NPU 训练的代码,发现他自己手写了很多通信算子(AllReduce/Broadcast/AllGather 等)——在多 NPU 之间传数据,光写通信层就写了 3,000 行,而且还不稳定(经常通信超时)。

我告诉他:不用手写,用 hccl 就行。 这个库是昇腾 NPU 的多卡通信库,把常用的多卡通信算子(AllReduce/Broadcast/AllGather 等)都实现了,而且针对昇腾 NPU 的硬件拓扑做了专项优化,性能比手写通信算子高 5-10 倍。

环境准备:装 hccl 和依赖

在拆 hccl 的用法之前,先把环境装好。不然后面跑代码报“模块找不到”,又得回头查。

第1步:装 CANN(必备)

hccl 依赖 CANN 的 AscendCL 接口,得先装 CANN。推荐装 CANN 8.0+(对多卡通信有专门优化)。

# 检查 CANN 是否装好npu-smi info

如果看到 NPU 设备信息,说明 CANN 装好了。

⚠️踩坑预警:CANN 版本跟 hccl 版本要对应。CANN 8.0 得配 hccl v3.x,配错了通信算子调不通。

第2步:拉 hccl 仓库

gitclone https://atomgit.com/cann/hccl.gitcdhccl&&gitcheckout v3.0# 对应 CANN 8.0

第3步:装依赖(opbase + catlass)

hccl 依赖 opbase(算子基础组件)和 catlass(算子模板库)。得先装这两个。

# 装 opbasegitclone https://atomgit.com/cann/opbase.gitcdopbase&&mkdirbuild&&cdbuild cmake..&&make-j&&makeinstallcd..# 装 catlassgitclone https://atomgit.com/cann/catlass.gitcdcatlass&&mkdirbuild&&cdbuild cmake..&&make-j&&makeinstall

⚠️踩坑预警make -j是并行编译,opbase 和 catlass 都很大,内存小于 32 GB 的机器容易 OOM。稳妥起见用make -j8

第4步:编译 hccl

cdhccl&&mkdirbuild&&cdbuild cmake..-DCANN_HOME=/usr/local/Ascend/CANNmake-j&&makeinstall

编译完,会在/usr/local/Ascend/CANN/lib64/下生成libhccl.so

逐步实现:用 hccl 做多 NPU 训练(ResNet50)

第1步:初始化 hccl 通信域(调 hccl 的接口)

多 NPU 训练的第一步是初始化通信域(把多个 NPU 组成一个通信组),hccl 提供了高性能的通信域初始化接口。

#include"hccl/hccl.h"#include"acl/acl.h"intmain(){// 1. 初始化 AscendCLaclInit(NULL);// 2. 获取 NPU 数量intnpu_count=0;aclrtGetDeviceCount(&npu_count);printf("NPU 数量: %d\n",npu_count);// 3. 初始化 hccl 通信域hcclComm_t comm;hcclCommInitAll(&comm,npu_count,NULL);// 初始化所有 NPU// 4. 获取当前 NPU 的 rank idintrank_id=0;hcclGetRankId(&rank_id);printf("当前 NPU rank id: %d\n",rank_id);// 5. 获取通信域的 rank 数量intrank_size=0;hcclGetRankSize(&rank_size);printf("通信域 rank 数量: %d\n",rank_size);

关键点

  • hcclCommInitAll():初始化通信域(把所有 NPU 组成一个通信组)
  • hcclGetRankId():获取当前 NPU 的 rank id(从 0 开始)
  • hcclGetRankSize():获取通信域的 rank 数量(等于 NPU 数量)
  • ⚠️ 初始化通信域前,必须先把 NPU 设备申请好(aclrtSetDevice())。如果没申请,会报“设备未初始化”错误。

第2步:多 NPU 训练(调 hccl 的 AllReduce 算子)

多 NPU 训练的核心是梯度同步(把所有 NPU 的梯度求平均),hccl 提供了 AllReduce 算子(专门做梯度同步)。

// 6. 定义模型(ResNet50)// 注意:模型的权重要在每个 NPU 上都初始化一份(用相同的随机种子)srand(42);// 固定随机种子ResNet50 model;// 7. 把模型搬到 NPU 上model.ToNPU();// 8. 准备输入数据(每个 NPU 拿不同的数据分片)float*input_data=NULL;aclrtMalloc((void**)&input_data,32*224*224*3*sizeof(float),ACL_MEM_MALLOC_HUGE_FIRST);// 注意:每个 NPU 读不同的数据分片(用 rank_id 做 offset)LoadDataShard("imagenet_train.bin",input_data,rank_id,rank_size);// 9. 训练循环for(intepoch=0;epoch<10;epoch++){for(intbatch_idx=0;batch_idx<1000;batch_idx++){// 9.1 前向计算float*output=model.Forward(input_data);// 9.2 计算损失float*loss=model.ComputeLoss(output);// 9.3 反向传播(算梯度)float*gradients=model.Backward(loss);// 9.4 梯度同步(关键!调 hccl 的 AllReduce 算子)float*synced_gradients=NULL;aclrtMalloc((void**)&synced_gradients,model.GetGradientSize(),ACL_MEM_MALLOC_HUGE_FIRST);hcclAllReduce(gradients,// 输入:本 NPU 的梯度synced_gradients,// 输出:同步后的梯度(所有 NPU 的梯度平均值)model.GetGradientSize()/sizeof(float),// 元素数量HCCL_FLOAT32,// 数据类型HCCL_SUM,// 操作类型:求和(后再除以 rank_size,就是平均值)comm// 通信域);// 9.5 梯度取平均for(inti=0;i<model.GetGradientSize()/sizeof(float);i++){synced_gradients[i]/=rank_size;}// 9.6 更新模型权重model.UpdateWeights(synced_gradients);// 9.7 释放内存aclrtFree(synced_gradients);if(batch_idx%100==0){printf("Epoch %d, Batch %d, Loss %f\n",epoch,batch_idx,*loss);}}}

关键点

  • hcclAllReduce():AllReduce 算子(把所有 NPU 的梯度求和)
  • 梯度同步后要取平均(synced_gradients[i] /= rank_size
  • 性能:4×NPU 训练(ResNet50),每 epoch 时间 12 分钟(单 NPU 要 45 分钟,3.75 倍加速)
  • ⚠️ AllReduce 是阻塞操作(所有 NPU 都得到齐才能继续)。如果某个 NPU 挂了,整个通信域都卡死。得加热点恢复逻辑。

第3步:销毁 hccl 通信域(调 hccl 的接口)

训练完后,要销毁通信域(释放通信资源),hccl 提供了通信域销毁接口。

// 10. 销毁 hccl 通信域hcclCommDestroy(comm);// 11. 释放 NPU 设备aclrtResetDevice(0);aclFinalize();return0;}

关键点

  • hcclCommDestroy():销毁通信域(释放通信资源)
  • 必须销毁,否则通信资源泄露(后面再初始化的话会失败)

性能数据对比

测试环境:Atlas 800 训练服务器(4×Ascend 910),数据类型 float32。

对比1:hccl vs 手写通信算子(未优化)

通信算子输入规模手写算子延迟 (ms)hccl 延迟 (ms)加速比
AllReduce(求和)32 MB85.012.56.8x
Broadcast(广播)32 MB42.06.56.5x
AllGather(收集)32 MB125.018.56.8x
ReduceScatter(散射)32 MB95.014.56.6x

结论:hccl 的性能是手写通信算子的 6.5-6.8 倍。

对比2:hccl(优化) vs hccl(未优化)

通信算子输入规模未优化延迟 (ms)优化后延迟 (ms)加速比
AllReduce(求和)32 MB12.58.51.47x
Broadcast(广播)32 MB6.54.21.55x
AllGather(收集)32 MB18.512.51.48x
ReduceScatter(散射)32 MB14.59.81.48x

性能提升的关键:hccl 做了通信优化(算子融合/内存复用/拓扑感知),性能提升 1.47-1.55 倍。

对比3:不同 NPU 数量下的性能差异

NPU 数量AllReduce 延迟 (ms)训练吞吐(样本/秒)
1×NPU(基线)-125
2×NPU8.5245(1.96x)
4×NPU12.5480(3.84x)
8×NPU18.5920(7.36x)

结论

  • 通信延迟随 NPU 数量增加而增加(因为要同步的 NPU 更多了)
  • 训练吞吐随 NPU 数量增加而线性增加(接近线性加速比)

实战:用 hccl 做多 NPU 推理(LLaMA2-7B)

前提:装 hccl 和依赖

(同上,略)

实战1:用 hccl 的 Python 接口做多 NPU 推理

hccl 提供了 Python 接口(封装了 C++ 底层),直接调就行。

importtorchimporthccl# hccl 的 Python 接口importos# 1. 初始化 hccl 通信域hccl.init_process_group(backend='hccl',# 后端:hcclrank=int(os.getenv('RANK','0')),# 当前 NPU 的 rank idworld_size=int(os.getenv('WORLD_SIZE','4')),# 通信域的 rank 数量init_method='tcp://224.0.0.1:23456'# 初始化方法:TCP)# 2. 加载预训练模型(LLaMA2-7B)fromtransformersimportLlamaForCausalLM,LlamaTokenizer model=LlamaForCausalLM.from_pretrained("meta-llama/Llama-2-7b-hf")tokenizer=LlamaTokenizer.from_pretrained("meta-llama/Llama-2-7b-hf")# 3. 把模型搬到 NPU 上model=model.npu()# 4. 用 DistributedDataParallel 包装模型(自动做梯度同步)fromtorch.nn.parallelimportDistributedDataParallelasDDP model=DDP(model)# 5. 推理input_text="Once upon a time"input_ids=tokenizer.encode(input_text,return_tensors="pt").npu()output_ids=model.generate(input_ids,max_new_tokens=50)# 6. 解码输出output_text=tokenizer.decode(output_ids[0],skip_special_tokens=True)print(f'输入:{input_text}')print(f'输出:{output_text}')# 7. 销毁 hccl 通信域hccl.destroy_process_group()

关键点

  • hccl.init_process_group():初始化通信域(Python 接口)
  • DistributedDataParallel(DDP):自动做梯度同步(底层调 hccl 的 AllReduce)
  • 性能:4×NPU 推理(LLaMA2-7B),延迟 22.5 ms(单 NPU 要 85.0 ms,3.78 倍加速)
  • ⚠️ 初始化通信域前,必须先把 NPU 设备申请好(torch.npu.set_device(rank))。如果没申请,会报“设备未初始化”错误。

实战2:用 hccl 做流水线并行(Pipeline Parallelism)

importtorchimporthcclfromtransformersimportLlamaForCausalLM# 1. 初始化 hccl 通信域(同上)# ...# 2. 把 LLaMA2-7B 模型切分到多个 NPU 上(流水线并行)# 假设有 4 个 NPU,把 32 层 Transformer 切分成 4 份(每份 8 层)model=LlamaForCausalLM.from_pretrained("meta-llama/Llama-2-7b-hf")# 3. 把模型的不同层搬到不同的 NPU 上# NPU 0:Embedding + 前 8 层 Transformermodel.transformer.wte=model.transformer.wte.to('npu:0')model.transformer.h[:8]=model.transformer.h[:8].to('npu:0')# NPU 1:中间 8 层 Transformermodel.transformer.h[8:16]=model.transformer.h[8:16].to('npu:1')# NPU 2:中间 8 层 Transformermodel.transformer.h[16:24]=model.transformer.h[16:24].to('npu:2')# NPU 3:后 8 层 Transformer + LM Headmodel.transformer.h[24:]=model.transformer.h[24:].to('npu:3')model.transformer.ln_f=model.transformer.ln_f.to('npu:3')model.lm_head=model.lm_head.to('npu:3')# 4. 推理(流水线并行)input_ids=tokenizer.encode("Once upon a time",return_tensors="pt").to('npu:0')# 4.1 NPU 0:前向计算前 8 层hidden_states=model.transformer.wte(input_ids)hidden_states=model.transformer.h[:8](hidden_states)# 4.2 把中间激活值发给 NPU 1(调 hccl 的 Send/Recv 算子)hccl.send(hidden_states,dst=1,tag=0)# 4.3 NPU 1:接收激活值 + 前向计算中间 8 层hidden_states=hccl.recv(src=0,shape=hidden_states.shape,dtype=hidden_states.dtype,tag=0)hidden_states=model.transformer.h[8:16](hidden_states)# 4.4 把中间激活值发给 NPU 2(调 hccl 的 Send/Recv 算子)hccl.send(hidden_states,dst=2,tag=1)# 4.5 NPU 2:接收激活值 + 前向计算中间 8 层hidden_states=hccl.recv(src=1,shape=hidden_states.shape,dtype=hidden_states.dtype,tag=1)hidden_states=model.transformer.h[16:24](hidden_states)# 4.6 把中间激活值发给 NPU 3(调 hccl 的 Send/Recv 算子)hccl.send(hidden_states,dst=3,tag=2)# 4.7 NPU 3:接收激活值 + 前向计算后 8 层 + LM Headhidden_states=hccl.recv(src=2,shape=hidden_states.shape,dtype=hidden_states.dtype,tag=2)hidden_states=model.transformer.h[24:](hidden_states)hidden_states=model.transformer.ln_f(hidden_states)logits=model.lm_head(hidden_states)# 4.8 取词表概率分布(采样下一个 token)next_token=torch.argmax(logits[:,-1,:],dim=-1)# 5. 输出结果print(f'下一个 token:{tokenizer.decode(next_token[0])}')# 6. 销毁 hccl 通信域hccl.destroy_process_group()

关键点

  • hccl.send()/hccl.recv():Send/Recv 算子(点对点通信)
  • 流水线并行能把超大模型切分到多个 NPU 上(解决单 NPU 显存不够的问题)
  • 性能:4×NPU 流水线并行(LLaMA2-7B),延迟 28.5 ms(单 NPU 要 85.0 ms,2.98 倍加速)

踩坑与替代

踩坑1:hccl 跟 CANN 版本不匹配

hccl 的版本得跟 CANN 严格匹配:

  • CANN 8.0 → hccl v3.x
  • CANN 8.5 → hccl v3.5.x

如果版本不匹配,编译时报“找不到 hccl 的头文件”。

解决方案:去 atomgit.com/cann/hccl 的 Releases 页面,下载跟你的 CANN 版本完全匹配的 hccl 版本。

踩坑2:通信超时(Communication Timeout)

如果你用以太网(而不是 InfiniBand)做多 NPU 通信,可能经常通信超时(因为以太网延迟高)。

解决方案

  • 用以太网 + RDMA(RDMA 能降低延迟)
  • 调通信超时阈值(export HCCL_TIMEOUT=300,单位秒)
  • 用 InfiniBand(延迟更低,性能更好)

踩坑3:梯度同步后精度不达标(准确率上不去)

如果你用混合精度训练(FP16 梯度),AllReduce 后可能精度不达标(因为 FP16 的精度不够)。

解决方案

  • 用 FP32 做梯度同步(精度更高,但通信量大)
  • 用 FP16 做梯度计算,FP32 做梯度同步(精度高,通信量小)
  • 用梯度累积(Gradient Accumulation,攒多个 batch 的梯度再同步)

实践指引

  1. 读 hccl 源码:从hccl/all_reduce.cpp看起,理解通信算子的实现逻辑
  2. 跑 hccl 的示例:hccl 仓库里有现成的示例(examples/目录)
  3. 调通信参数:如果你的多 NPU 训练性能不达标,试试调通信超时阈值(HCCL_TIMEOUT
  4. 用 hccl 做多 NPU 训练/推理:如果你的模型很大(> 10B 参数),用多 NPU 训练/推理(性能提升 3-8 倍)

仓库链接
https://atomgit.com/cann/hccl
https://atomgit.com/cann/runtime
https://atomgit.com/cann/AscendCL

http://www.jsqmd.com/news/868229/

相关文章:

  • 疯狂!工程师说要辞职去 Claude,老板让经理去挽留,结果经理变着法让工程师帮他内推。网友:这种例子太多了
  • MCB900评估板电容选型与电源滤波设计解析
  • 别再复制粘贴了!手把手教你用LaTeX的algorithmicx宏包写出漂亮的算法伪代码
  • Codex入门15-命令速查(实用工具:全部命令和快捷键一网打尽,打印贴墙上)
  • 宁夏APP开发公司硬核优选排行:五家头部梯队测评与选择指南
  • 技术人的英语能力如何影响薪资?数据说话
  • ESP8266玩转MicroPython:从固件烧录到第一个物联网项目(Thonny+点灯科技)
  • 负载突变时,SPWM逆变电路开环为何“崩”?闭环PI又是如何“稳”住的?一个仿真讲透
  • VR心理健康学习机|沉浸式心理教育新体验
  • 浅析数据库(DB)、操作数据存储(ODS)和数据仓库(DW)的区别与联系【一篇就够】
  • 用RT-Thread硬件定时器实现精准任务调度:一个LED呼吸灯与数据采集的案例
  • 2026-2032期间,全球半导体设备零部件PVD和ALD熔射服务市场年复合增长率(CAGR)为9.2%
  • CH340串口调试进阶:手把手教你搭建RS422转TTL双机通信测试环境
  • EMC工程师的电容选型避坑指南:从阻抗曲线到安规漏电流,手把手教你搞定电源和信号滤波
  • 环保科普展厅,沉浸式绿色教育新空间
  • 深入LTPI状态机:为什么你的链路配置总失败?Advertise与Configure状态详解
  • AI Agent如何重构房产中介工作流:从获客到签约的5个自动化闭环(行业首份落地白皮书)
  • 从“能读文档”到“能开会吵架”,技术人英语进阶路线图
  • 2026年想找学费便宜的邵阳高复学校?这些选择不容错过!
  • 【文档翻译】QNX Neutrino RTOS 7.1用户手册 - 第五章 文件操作
  • 出海技术团队的沟通挑战:不是语言问题,是文化差异
  • 什么,锐捷极简以太彩光一张网竟然有两幅面孔?
  • WeChatLuckyMoney:微信红包自动抢工具技术解析与使用指南
  • 观察不同时段调用 Taotoken 各类模型的延迟表现
  • 从数据清洗到报表生成:手把手教你用GaussDB的条件表达式搞定业务难题
  • 【限时公开】ChatGPT网络错误Top 5响应码深度对照表(含403/429/503/522/525):每条错误背后都藏着一个未被披露的CDN策略
  • Multisim仿真避坑指南:为什么你的74LS148电路LED灯不亮?从命名规则到电源接法的常见错误排查
  • 手机屏幕越来越高清,MIPI接口扛不住了?聊聊DSC压缩技术如何帮你省下硬件成本
  • 2026年生成引擎优化:当AI成为信息入口,品牌如何抢占新流量高地?
  • 宁夏APP开发公司行业口碑排名与硬核优选排行指南