CANNBot-DSL 数据搬运全解:nd2nz、nz2dn、Load3D 与 fixpipe 拷贝引擎如何驱动昇腾NPU算子性能

发布时间:2026/8/23 15:37:02
CANNBot-DSL 数据搬运全解:nd2nz、nz2dn、Load3D 与 fixpipe 拷贝引擎如何驱动昇腾NPU算子性能 CANNBot-DSL 数据搬运全解nd2nz、nz2dn、Load3D 与 fixpipe 拷贝引擎如何驱动昇腾NPU算子性能【免费下载链接】cannbot-dsl基于 CANNBot-DSL 的 Ascend NPU 复杂算子示例集合。项目地址: https://gitcode.com/cann/cannbot-dslCANNBot-DSL 是一套面向昇腾 NPU 的 DSL 算子开发框架与复杂算子示例集合nd2nz、nz2dn、Load3D 与 fixpipe 拷贝引擎正是它驱动算子性能的核心数据搬运四件套。本文用 6 个真实算子示例带你快速看懂这些搬运机制如何工作以及为什么它们是 NPU 算子性能的关键。一、为什么数据搬运比计算更决定算子性能 昇腾 NPU 的 Cube 架构有多级片上存储GM全局内存→ L1 → L0A/L0B/L0C数据要经过 MTE2、MTE1、FIXPIPE 等搬运引擎逐级流动Cube 单元MMAD只负责计算。算子慢往往不是算得慢而是搬得慢——L1/L0 之间出现气泡计算单元就会空转。CANNBot-DSL 的做法是把每条搬运链路显式建模用make_copy_engine声明引擎用mem_copy描述流向编译器自动生成 CAPI 指令序列并做双缓冲流水线。二、nd2nzGM → L1 的第一站矩阵乘的入口nd2nz是 format 转换引擎把 GM 中连续布局ND的数据转成 NZ 格式加载进 L1。NZ 格式按 Cube 的 16x16 tile 组织数据让 MTE1 后续读取 L0A/L0B 时对齐硬件块避免越界和填充浪费。在 samples/matmul/matmul.py 中matmul 算子为 A/B 两个输入各建一个 nd2nz 引擎nd2nz_engine_a make_copy_engine(format_transformnd2nz, dtypet.a_dtype, pad_value0.0) mem_copy(l1_a, gm_a_tile, enginend2nz_engine_a)这条链路在三个算子里反复出现是典型的GM → L1MTE2, nd2nz入口matmulsamples/matmul/matmul.pyL584、L616A/B 双引擎配合滑动窗口多核调度pointnet_sasamples/pointnet_sa/pointnet_sa.pyL411、L443shared MLP 的矩阵乘部分flash_attnsamples/flash_attn/flash_attn.pyL88-L109Q/K/V 三张量共享一个 nd2nz 引擎把 GM tile 依次搬进 Q/K/V 的 L1 缓冲一个实用技巧多个形状相近的张量可以复用同一个拷贝引擎如 flash_attn 的 Q/K/V减少引擎对象开销。三、Load3D卷积算子的展开指令普通矩阵乘用 MTE1 从 L1 直接搬 L0但卷积的 L0A 是im2col 展开出来的——它并不在内存里而是由 Load3D 指令在搬运时按滑窗几何实时生成。voxel_convsamples/voxel_conv/voxel_conv.pyL30-L31、L162是仓库中 Load3D 的代表实现阶段搬运方式说明GM → L1conv2d_load_fmap/conv2d_load_filterDN2NZ特征图与权重打包进 L1L1 → L0Aconv2d_load_im2colLoad3D滑窗展开成 L0A 矩阵 tileL1 → L0Bmem_copy权重搬到 L0BL0A × L0B → L0Cmatmul沿 Cin 方向矩阵乘累加Load3D 的意义在于卷积被降维成矩阵乘而 im2col 展开完全融进搬运指令不占用额外的存储和指令开销。你只需描述 tile 几何编译器自动生成整套 CAPI 搬运序列。四、nz2dn 与 fixpipe结果写回的最后一公里 计算在 L0C 中完成后结果必须回到 GM这里有两种引擎nz2dnFIX 引擎是 nd2nz 的逆操作把 NZ 格式的 L0C 结果转回 ND 连续布局写回 GM。voxel_conv 的conv2d_store_outputsamples/voxel_conv/voxel_conv.pyL178就是这条写回链路对应数据流末端 L0C → GMFIX, NZ2DN。fixpipe则用于 L0C → UB统一缓冲的跨核/双写搬运在注意力类算子中非常关键。以 flash_attn 为例samples/flash_attn/flash_attn.pyL89、L124-L129self.fixpipe make_copy_engine(dtypedtypes.float32, dual_dst_ctl1) mem_copy(ub_ch, self.l0c, engineself.fixpipe, partitionpartition)dual_dst_ctl表示 fixpipe 可同时向两个目的地址写partition支持按 M 方向切分——这正是 Flash Attention 分块 online softmax能在多核间流水的前提一个核算完 L0C 分片fixpipe 立刻把结果送进 UB 做 softmax 归约与下一个分片的矩阵乘重叠执行。flash_kdasamples/flash_kda/flash_kda.pyL309-L310、L1422-L1424更是配置了 fixpipe 的多种变体dual_dst_ctl1/2、dst_c0_stride、unit_flag_mode分别服务状态矩阵递推、packed64 打包写 L1 等不同链路——复杂算子的性能往往就藏在这种细粒度的搬运编排里。五、四个核心算子的搬运链路一览算子数据流关键搬运点matmulGM → L1 → L0A/L0B → L0C → GMnd2nz 入口 FIXPIPE 写回L1/L0 双缓冲 ping-pongflash_attnGM → L1 → L0 → L0C → UB → GM共享 nd2nz 引擎 fixpipe 跨核双写flash_kdaGM/UB ↔ L1 ↔ L0C多组 nd2nz/fixpipe 引擎分工chunk 间状态递推voxel_convGM → L1 → L0A/L0B → L0C → GMLoad3D 滑窗展开 NZ2DN 写回作为对照rms_normsamples/rms_norm/rms_norm.py不走 Cube而是 GM → UBMTE2→ 向量计算 → GMMTE3展示了同一框架下向量算子的搬运路径。它的实现由 CANNBot CANNBot-DSL 在 1 天内自动生成并调优六、上手路径从样例到自定义算子 仓库结构与关键文件NPU ARCH 3510Ascend 950PR / 950DT算子实现samples/matmul/matmul.py、samples/flash_attn/flash_attn.py、samples/flash_kda/flash_kda.py、samples/voxel_conv/voxel_conv.py、samples/pointnet_sa/pointnet_sa.py、samples/rms_norm/rms_norm.py精度测试test/目录下按算子分目录存放 pytest 用例如test/matmul/test_matmul.py性能对比图figures/目录与各算子 README 中的对比数据对应测试配置test/test_config.yaml建议的学习顺序先读samples/matmul/理解 nd2nz fixpipe 的基本盘 → 再看samples/voxel_conv/掌握 Load3D → 最后挑战samples/flash_kda/感受多引擎编排的威力。每个算子的 README 都给出了完整参数表、运行方式和性能对比。git clone https://gitcode.com/cann/cannbot-dsl pytest test/matmul/test_matmul.py -v总结nd2nz 管进Load3D 管展开nz2dn 与 fixpipe 管出。CANNBot-DSL 让这四条搬运链路变成声明式的描述编译器负责生成指令序列和流水线调度——这正是它相比手写 AscendC 大幅降低昇腾 NPU 复杂算子开发门槛的核心所在。【免费下载链接】cannbot-dsl基于 CANNBot-DSL 的 Ascend NPU 复杂算子示例集合。项目地址: https://gitcode.com/cann/cannbot-dsl创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考