CANN/cannbot-skills MC2架构指南
MC2 通算融合架构总览【免费下载链接】cannbot-skillsCANNBot 是面向 CANN 开发的用于提升开发效率的系列智能体本仓库为其提供可复用的 Skills 模块。项目地址: https://gitcode.com/cann/cannbot-skills本文档面向第一次接触 MC2 的 Architect/Developer建立 AIV/AIC 分工、UDMA 通信、4-buffer 流水、M 轴通算并行的整体心智模型。读完本文档应能回答为什么 MC2 必须用 SHMEM/UDMA不能用 HCCLAIV 和 AIC 各自做什么怎么同步4-buffer 流水怎么掩盖通信开销M 轴切分 vs N/K 轴切分的取舍1. MC2 是什么MC2 Multi-Card Compute-Communication Coupling多卡通算融合。典型场景AllToAll MatmulMoE 模型专家路由每张卡把自己负责的 M 段通过 AllToAll 发给其他卡接收方在收到的数据上做本地 Matmul。AllReduce MatmulDDP 前向反向Matmul 后的部分和通过 AllReduce 聚合。ReduceScatter MatmulTP 反向梯度聚合。参考工程all_to_all_matmul/实现的是AllToAll 量化 MatmulMX FP8的融合可作为所有 MC2 算子的起手模板。2. 为什么不用 HCCLHCCLHuawei Collective Communication Library的高阶 APIHccl::AllReduce等是服务端调度的应用层: aclnnApi → HCCL → Server (通信 CPU) → NIC ↑ Kernel 不能干预调度而 MC2 的核心诉求是Kernel 内部下发通信 下发计算让两者在硬件流水上重叠。HCCL 把调度权放在 Server 端Kernel 无法在通信进行中插入计算指令通信与计算是串行的。SHMEM/UDMA 路径把通信下发权交回 Kernel应用层: Kernel 内直接调 aclshmemx_udma_put_nbi(...) ↓ UDMA 硬件引擎异步搬数 ↓ 同时 AIC 管线继续下发 MMAD 指令 ↓ aclshmemx_barrier_all_vec() 等通信完成UDMAUnified DMA对应 URMA 协议——Unreliable Remote Memory Access是 Ascend 950 提供的 Kernel 级跨卡通信原语所有通信下发在 Kernel 内完成可与计算流水深度耦合。官方文档https://shmem-doc.pages.dev/。SKILL.md 列出的 13 类 HCCL API 在本 skill 全部禁用。3. AIV/AIC 分工Ascend 950 一个 Block 内有 AICCube 核和 AIVVector 核两类算力。参考工程把通信和计算分给不同的核核职责在参考工程的位置AIVVector下发 UDMA Put 指令、维护通信流水all_to_all_matmul_impl.h::AllToAllProcess()AICCube在通信 buffer 上做 Blaze Matmul遍历所有 rank 累加all_to_all_matmul_impl.h::MatmulProcess()// all_to_all_matmul_impl.h Process() __aicore__ inline void Process() { if ASCEND_IS_AIV { AllToAllProcess(); // AIV 跑通信UDMA Put Barrier } if ASCEND_IS_AIC { MatmulProcess(); // AIC 在通信 buffer 上做完整 Blaze Matmul } }同一份 kernel 二进制同时跑在 AIV 和 AIC 上靠ASCEND_IS_AIV/ASCEND_IS_AIC编译期分支隔离。跨核同步CrossCore FlagAIV 写完 buffer 后必须通知 AIC 可读AIC 读完必须通知 AIV 可覆盖。这对同步通过CrossCoreSetFlag/CrossCoreWaitFlag完成// AIV 写完 bufferId 后置 flag CrossCoreSetFlag0x2, PIPE_MTE3(mLoopIdx); // AIC 读 bufferId 前等 flag CrossCoreWaitFlag0x2, PIPE_MTE2(mLoopIdx);0x2是 flag 类别自定义编号AIV↔AIC 间用 0x2PIPE_MTE3是 AIV 的 Memory Transfer Engine 3输出管线PIPE_MTE2是 AIC 的输入管线idx 用mLoopIdxAIV 写第 i 轮 buffer → AIC 读第 i 轮 bufferidx 一一对应。流水深度控制AIV 不能无脑往前写——会覆盖 AIC 还没读的旧数据。参考工程用等 AIC 释放信号来限速// AIV写到第 bufferSize 轮后必须等 AIC 释放第 (i - bufferSize) 轮的 buffer if (mLoopIdx bufferSize_) { CrossCoreWaitFlag0x2, PIPE_MTE3(mLoopIdx - bufferSize_); allToAllComm_.BarrierAll(); // 还要等所有其他卡也释放 }对应的 AIC 端在算完第 i 轮后置释放 flagall_to_all_matmul_impl.h::MatmulProcess内。4. 4-Buffer 流水bufferSize4是参考工程的默认深度。把通信流水画出来时间轴 → AIV: |PutScale| Put(0) | Put(1) | Put(2) | Put(3) | Put(4) | Put(5) | ... | Put(N-1) | AIC: |wait(0) | MMAD(0)| wait(1) | MMAD(1)|wait(2)| MMAD(2)| ... | MMAD(N-1)| ↑ 4 个 buffer 轮转使用bufferId mLoopIdx (bufferSize - 1)AIV 的 Put(i) 与 AIC 的 MMAD(i-1) 并行AIV 的 Put(i) 把数据写到 SHMEM 的bufferId i 3位置AIC 的 MMAD(i) 从同一 bufferId 位置读通信延迟被 4 个 buffer 的流水深度掩盖。bufferSize 取舍过浅1通信和计算串行没有掩盖效果过深SHMEM 空间不够默认 1 GB或 AIC 等 AIV 凑够 4 轮才开始算反而增加延迟4 是经验最优值参考工程固定为 4新算子一般不动。5. SHMEM 空间布局每张卡的 SHMEM 空间aclshmem_align分配的 1 GB被分成两段SHMEM base (rankId 视角) ├── Data buffers[bufferSize] ← 每个 bufferBlockSize 存所有 rank Put 过来的当前轮数据 │ ├── buffer[0]: rankSize * commMSize * bytesPerMRow │ ├── buffer[1]: ... │ ├── buffer[2]: ... │ └── buffer[3]: ... └── Scale buffers ← 所有 rank 的 Scale 一次性 Put不参与流水 └── rankSize * M * scaleBytesPerMRow参考工程在all_to_all_comm_udma.h的AllToAllComm::GetDataAddrGm(bufferId)和GetScaleAddrGm()访问这两段。6. 切分策略M 轴通算并行参考工程沿 M 轴切分每块headMSize512行。为什么选 M 轴候选轴通算并行性实现难度适用场景M 轴✅ 高不同 M 段可独立 PutAIC 边收边算中通用首选参考工程采用N 轴低N 轴切会让 B 矩阵跨卡通信量翻倍高不推荐K 轴中K 切分对应 ReduceScatter每段需要 reduce高AllReduce Matmul 场景M 轴切分的好处A 矩阵按 M 段分布在不同卡每卡独立 Put 自己的 M 段无冲突B 矩阵全卡持有相同副本或按 K 段分布无需通信AIC 收到某段 M 后立即可算无需等待所有段到齐。headMSize 取值参考工程默认headMSize512即tileCnt M/512只是经验起点不是最优值。约束必须是 BlazeBlockMmad的baseM典型 128 或 256的整数倍必须让单次 Put 的数据量headMSize * kPerRank * sizeof(dtype)落在 UDMA 高效区间数百 KB ~ 数 MBheadMSize M / tileCnt调整tileCnt即调整headMSize。tileCnt是通算流水的深度旋钮——tileCnt1时无掩盖串行基线tileCnt1时 N-1 级流水掩盖通信延迟。CANNBot 流程中分两阶段调优详见pipeline_tuning.mdStep 2-4 用tileCnt1做串行基线简化精度/审查Step 6 扫描tileCnt找最优。7. UDMA 通信原语速览参考工程用到的三个核心 API详见comm_shmem.md// 1. 非阻塞 Put本地 → 远程 aclshmemx_udma_put_nbi(remoteWinAddr dstOffset, localAddr srcOffset, (__ubuf__ uint8_t*)nullptr, dataSize, remoteRank); // 2. Quiet等本次 Put 真正下发到 UDMA 引擎不等对端收完 aclshmemx_udma_quiet(remoteRank); // 3. 全卡 Barrier等所有卡的 Put 都被对端收完 aclshmemx_barrier_all_vec();Put_nbi是非阻塞的——下发完立即返回AIV 继续下一条 Putquiet保证后续对同一 remoteRank 的 Put 在前面 Put 之后下发保序barrier_all_vec是真正等对端收到的同步点。参考工程在PutToAllRanks中每个 Block 负责发往对应的 remoteRankall_to_all_comm_udma.h::PutToAllRanks让多 Block 并发下发避免单 Block 串行 Put 所有 rank。8. 性能采集要点MC2 算子的性能采集有一条核心特殊点每轮主 kernel 前必须调用heavy_add_kernel刷 L2 cache否则前一轮的 B 矩阵驻留 L2 会让本轮带宽指标虚高。详细流程msprof task-based 采集、4 卡数据后处理、优化速查见profiling_mc2.md。9. 后续阅读想了解读SHMEM API 细节、扩展其他通信原语comm_shmem.mdBlaze 模板选型、BlockMmad 改造matmul_blaze.mdmsprof task-based 采集、4 卡后处理profiling_mc2.md参考工程文件结构、改造食谱codebase_map.md各 Step 的具体动作workflow_integration.md【免费下载链接】cannbot-skillsCANNBot 是面向 CANN 开发的用于提升开发效率的系列智能体本仓库为其提供可复用的 Skills 模块。项目地址: https://gitcode.com/cann/cannbot-skills创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考