登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  科技周边 >  人工智能

DeepSeek Ascend 工具链中的算子与通信分工

来源:17golang原创

时间:2026-10-11 01:38:06 375浏览 收藏

在 DeepSeek 模型迁移到昇腾 NPU 时,最容易混淆的是“算子”和“通信”:前者回答单个设备上如何计算,后者回答多个设备之间如何交换张量。实际工程里,MoE 的 token dispatch、expert 计算和 combine 往往同时出现,若一开始没有分边界,调优就会变成反复改 kernel。

官方资料:https://github.com/deepseek-ai/DeepEP-Ascend

Ascend 文档:https://www.hiascend.com/document/detail/en/CANNCommunityEdition/910/commlib/commopdev/docs/en/comm_op_dev_guide/intro.md

要点速览
  • 本地矩阵、激活、归约等计算优先归入算子边界。
  • AllReduce、AllGather、ReduceScatter、AlltoAll 以及 rank 间数据交换归入通信边界。
  • 先确认 CANN、驱动、固件和拓扑,再比较 kernel 时间与通信时间。

先把三类职责拆开

问题主要职责优先看的组件
一个 NPU 内怎样完成计算输入布局、分块、向量或矩阵计算、尾块处理Ascend C、算子库、BiSheng
多个 rank 怎样交换数据拓扑、集合通信、同步、带宽和通信资源HCCL、HCOMM、通信算子 API
模型怎样调用它们张量分发、stream、生命周期、框架后端PyTorch/torch_npu、DeepSeek 项目封装

这个划分不是按目录名猜,而是按数据是否跨设备判断:数据仍在当前设备上流动,通常属于算子实现;数据需要经过 HCCS、RoCE、PCIe 或其他互联路径,通常属于通信实现。两者在一个 kernel 中融合时,也要分别记录计算和搬运耗时。

DeepSeek Ascend 工具链中本地计算算子、框架调用与张量布局的边界说明图
图1:DeepSeek Ascend 场景中,从框架张量到设备侧计算算子的静态边界说明图。

单卡计算优先走 Ascend C 与现有算子库

如果需求是激活函数、量化反量化、局部归约、格式转换或矩阵计算,第一选择通常是查 CANN 已有算子和 Ascend C 编程模型。只有现有算子无法满足布局、精度或性能要求时,才进入自定义 operator 的实现、tiling 和编译流程。

本地算子至少要固定三件事:输入输出 shape 和 dtype、内存布局与对齐要求、以及尾块和动态 shape 的处理方式。不要把跨 rank 的等待塞进普通计算算子,否则单卡正确性和多卡时序会同时变得难以定位。

// 这里只展示职责边界:kernel 负责本地计算,不负责跨卡同步。
__aicore__ void ComputeLocal(const LocalTensor& input,
                             LocalTensor& output,
                             int32_t count) {
  // 生产代码应根据 tile 大小处理尾块,避免越界读取。
  for (int32_t i = 0; i 

跨卡交换交给 HCCL/HCOMM

当问题变成“每个 rank 的 token 怎样送到目标 expert”“各卡怎样合并部分结果”,就应先看 HCCL 的集合通信能力和通信算子开发接口。官方文档把 AllReduce、AllGather、ReduceScatter、AlltoAll 等列为通信原语;这些 API 描述通信任务,真正的执行时机还要配合对应的 commit 或框架调度语义。

DeepEP-Ascend 的公开说明也体现了这种分工:MoE 的 dispatch/combine 是上层业务语义,底层使用 HCCL/HCOMM、UBMEM 与 URMA 等能力完成数据交换。阅读项目代码时,先找 buffer、rank、group 和 stream 的生命周期,再看 kernel 如何处理本地布局,效率会高很多。

DeepSeek Ascend 中 MoE dispatch combine 与 HCCL HCOMM 通信层的关系结构图
图2:MoE dispatch/combine 与 HCCL/HCOMM 通信层的关系静态结构图,不是运行截图。

MoE 场景的推荐排查顺序

  1. 环境层:确认 CANN、驱动、固件、torch_npu 和编译器版本匹配,先排除加载失败与 ABI 问题。
  2. 拓扑层:确认 rank 数、设备映射、网卡或互联路径,以及所有进程是否使用同一通信初始化参数。
  3. 正确性层:用小 batch 检查 dispatch 后 token 数、expert id、combine 权重和 padding;先确认数据没有丢失或重复。
  4. 性能层:分别记录本地 expert kernel、通信等待、数据转换和 epilogue,不能只看端到端耗时。
  5. 生命周期层:所有异步操作完成后再复用或销毁 buffer;遇到卡死先查 stream、barrier 和未完成通信。
# 这是排查配置的最小记录模板,不会启动真实通信。
config = {
    "backend": "hccl",       # 分布式后端,需与 torch_npu 环境匹配
    "world_size": 8,          # 所有 rank 必须对同一组大小达成一致
    "master_addr": "10.0.0.1",  # 仅示例地址,生产环境使用实际协调节点
    "master_port": 29500,     # 端口需在节点间可达且不被占用
}
# 先记录配置,再把通信耗时与本地算子耗时分开采样。
print(config)

不要把工具链职责混成一层

如果只改 Ascend C kernel,却没有检查 AlltoAll 的消息规模和拓扑,通信瓶颈不会消失;如果只调 HCCL 环境变量,却忽略本地 layout 转换,通信带宽也可能被无效搬运吃掉。更稳妥的落地方式是:先用已有算子和通信原语跑通小规模闭环,再逐个替换热点算子,最后根据 profile 决定是否使用融合通信算子。

版本差异也要单独记录。CANN 社区文档会随产品和版本增加通信算子开发能力,DeepSeek-Ascend README 里的硬件、固件和性能条件同样不是所有 Ascend 设备的通用结论。没有在目标设备上验证的数字,只能作为项目资料中的参考,不能当作部署承诺。

相关问题

算子慢和通信慢应该先查哪个?

先用 profile 分开本地 kernel、通信等待和同步时间。若 kernel 占比高,查布局、tile 和精度;若等待占比高,查消息规模、拓扑和 rank 配置。

所有跨卡操作都要自己写通信算子吗?

不需要。常规集合通信优先复用 HCCL;只有算法需要特殊的 dispatch、融合或调度语义时,才考虑通信算子开发接口。

为什么单卡正确,多卡却卡住?

单卡不会暴露 rank 数、集合顺序、stream 和 barrier 约束。先核对所有进程的 group、设备映射和 collective 调用顺序,再检查异步 buffer 是否提前复用。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>