昇腾 NPU 算子性能优化实战:使用 unit_flag 指令级标志位实现 MMAD 计算与 FixPipe 搬出流水并行

发布时间:2026/9/18 11:45:33
昇腾 NPU 算子性能优化实战:使用 unit_flag 指令级标志位实现 MMAD 计算与 FixPipe 搬出流水并行 昇腾 NPU 算子性能优化实战使用 unit_flag 指令级标志位实现 MMAD 计算与 FixPipe 搬出流水并行【免费下载链接】cann-samplesCANN高性能实战演进样例与体系化调优知识库项目地址: https://gitcode.com/cann/cann-samples本文基于 CANN 开源仓库 cann-samples 中的unit_flag样例系统讲解昇腾 NPU 上指令级优化特性 unit_flag 的原理与工程实践如何通过配置计算指令MMAD与搬出指令FixPipe的参数将原本串行的计算-搬出流程改造为以 512B 数据块为粒度的流水线并行从而隐藏数据搬移延迟、提升算子吞吐量。读者读完本文后将掌握 unit_flag 标志位的取值含义、在 MatMul 等算子中的改造方法、编译运行与 Profiling 验证手段以及如何结合源码判断该特性的适用场景。1. 背景搬运瓶颈与 N-Buffer 的局限在算子性能调优过程中带宽搬运瓶颈引起的计算断流是最需要优先优化的对象。业界通常从两个方面入手减少片外内存到片内内存的重复搬运从数据量上做减法提高搬运效率让搬运与计算在时间上重叠从流水并行上做加法。针对第二点仓库中提供了 N-Buffer 特性通过引入多份缓冲区如双缓冲 Ping/Pong、四缓冲等实现数据加载MTE 流水与矩阵计算Cube 流水的重叠执行原理详见 N-Buffer特性介绍。然而 N-Buffer 存在一个天然的局限为了降低重复搬运的数据量传入计算单元的计算基本块会被设计得尽量大导致L0C 缓存块被占满无法开启缓存机制。这样一来计算流水MMAD与搬出流水FixPipe就无法并行——数据必须等一整块全部计算完毕才能开始搬出搬出期间计算单元只能空闲等待。针对这一痛点昇腾 NPU 硬件提供了指令级的优化手段通过对计算和搬出指令的参数进行配置开启unit_flag标志位使计算MMAD与搬出FixPipe以更小的数据块粒度重叠执行搬运效率因此得到进一步提升。2. unit_flag 原理计算与搬出的流水线并行2.1 传统计算方式 vs unitflag两种模式的核心差异在于数据搬出的粒度与并行度传统计算方式计算单元需等待数据完全计算完成后才开始搬运搬移与计算串行执行导致计算单元空闲等待unitflag通过合理配置 MMAD 和 FixPipe 参数系统实现计算与搬运的流水线并行。每当计算单元完成一个512b 数据块的计算FixPipe 便立即将其搬出无需等待全部数据搬运完成即可开始下一块数据的计算从而实现计算与数据搬移的完全重叠执行。下图直观对比了两种模式传统方式下 PIPE_M 与 PIPE_FIX 各处理一个 2048B 的大块串行推进开启 unit_flag 后同一个 2048B 的数据块被拆分为 4 个 512B 的小块PIPE_M 每算完一块PIPE_FIX 就立即搬出一块两条流水紧密咬合2.2 硬件级流水线调度UnitFlag 通过硬件级流水线调度实现数据搬出与计算单元操作的并行执行。其关键机制是当设置 UnitFlag 为特定值时计算指令如 MMAD无需等待前序搬运完全完成即可基于缓冲区中已就绪的数据块立即启动计算后续数据则通过流水线持续供给。这要求软件层面做好两件事合理切分数据块Tiling只有将计算基本块拆成硬件可识别的小块FixPipe 才能实现边算边搬协调数据搬运与 MMAD 计算之间的流水同步通过事件标志或互斥锁Mutex控制流水阶段间的依赖关系最大化隐藏搬运延迟。2.3 预期效果吞吐量提升单位时间内完成的有效计算量增加整体算子性能得到优化资源利用率优化计算单元与搬移单元并行工作硬件资源得到更充分利用。3. 实践在 MatMul 算子中使能 unit_flag3.1 改造前基线实现在未使能 unit_flag 的 MatMul 实现中MMAD 计算与 FixPipe 搬出之间通过硬同步事件M_FIX严格串行——计算完成后才能搬出搬出完成后下一轮计算才能开始// 执行 M-MAD 操作 MmadParams para; para.cmatrixInitVal (iter1 0 iter0 0); para.m actualCurM; para.n actualCurN; para.k curKL0; AscendC::Te::Mad( MmadAtomMmadTraitsMmadOperation, MmadTraitDefault{}, tensorL0C, tensorAL0, tensorBL0, para); // 流水同步 MMAD计算和Fixpipe搬出指令 AscendC::SetFlagAscendC::HardEvent::M_FIX(ZERO_FLAG); AscendC::WaitFlagAscendC::HardEvent::M_FIX(ZERO_FLAG); // 拷贝 L0c 到 GM默认配置 auto copyL0C2GM AscendC::Te::MakeCopy(AscendC::Te::CopyL0C2GM{}); AscendC::Te::Copy(copyL0C2GM, gmBlockC_, tensorL0C);这里SetFlag/WaitFlagHardEvent::M_FIX的作用是等待 MMAD 计算完成、触发 FixPipe 搬出数据并等待搬出完成。它保证了数据一致性但也把两条流水死死绑在了一起。3.2 改造后unit_flag 使能版本改造后主要改动点是配置 mmad 入参和fixpipe 重新配置共三处关键改动// 新增单元标志控制开关 constexpr uint32_t UNITFLAG_DISABLE 0; // 不开启unit_flag constexpr uint32_t NO_FINAL_ACCUMULATION 2; // 非尾轮标志 constexpr uint32_t FINAL_ACCUMULATION 3; // 尾轮标志 // 执行 M-MAD 操作 MmadParams para; para.cmatrixInitVal (iter1 0 iter0 0); para.m actualCurM; para.n actualCurN; para.k curKL0; // 关键改动1根据迭代位置动态配置 unitFlag if constexpr (EN_UNIT_FLAG true) { if (iter1 (kL0IterNum - 1) iter0 (kL1TileNum - 1)) { para.unitFlag FINAL_ACCUMULATION; } else { para.unitFlag NO_FINAL_ACCUMULATION; } } AscendC::Te::Mad( MmadAtomMmadTraitsMmadOperation, MmadTraitDefault{}, tensorL0C, tensorAL0, tensorBL0, para); // 关键改动2删除 MMAD和Fixpipe之间的流水同步 // 关键改动3使用自定义 FixpipeUnitFlagTrait内置 unitFlag3 auto copyL0C2GM AscendC::Te::MakeCopy(AscendC::Te::CopyL0C2GM{}); AscendC::Te::Copy(copyL0C2GM, gmBlockC_, tensorL0C, AscendC::Te::FixpipeParams{UNITFLAG_EN_OUTER_LAST});三处关键改动逐一解读关键改动 1MMAD 侧根据迭代位置动态配置para.unitFlag。K 维L0 迭代与 L1 迭代都处于最后一轮时即整个数据块的尾块配置为FINAL_ACCUMULATION3表示这是最终累加轮数据可以搬出其余中间块配置为NO_FINAL_ACCUMULATION2表示本轮计算后数据仍需在 L0C 中累加不触发搬出。关键改动 2删除 MMAD 与 FixPipe 之间的M_FIX流水同步让两条流水解绑关键改动 3FixPipe 侧搬出指令显式携带 FixPipe 参数内置unitFlag3尾轮搬出标志使 FixPipe 以小数据块为单位随算随搬。3.3 源码级印证main.asc 中的完整实现仓库中该样例的核心内核实现位于 main.asc。从源码结构看它在一个基于双缓冲GM→L1→L0与多核并行的 MatMul 内核上叠加了 unit_flag 配置标志位常量定义main.asc// Unit flag values for cube unit pipelining constexpr uint32_t UNITFLAG_DISABLE 0; // Disable unit flag constexpr uint32_t NO_FINAL_ACCUMULATION 2; // Enable unit flag (inner loops) constexpr uint32_t FINAL_ACCUMULATION 3; // Enable unit flag for outer last iterationTiling 参数main.ascbaseM 256、baseN 256、baseK 128 / sizeof(T)即 bf16 下为 64、kL1 512 / sizeof(T)。内核按mTileNum × nTileNum切分输出 TileK 维再分两层外层iter0遍历 L1 TilekL1TileNum内层iter1遍历 L0 TilekL0IterNum。unitFlag 动态赋值main.ascAscendC::Te::MmadParams para; para.cmatrixInitVal (iter1 0 iter0 0); para.m curM; para.n curN; para.k curKL0; if (iter1 (kL0IterNum - 1) iter0 (kL1TileNum - 1)) { para.unitFlag tool::FINAL_ACCUMULATION; } else { para.unitFlag tool::NO_FINAL_ACCUMULATION; }可以确认只有 K 维内层循环iter1到达最后一个 L0 Tile且K 维外层循环iter0到达最后一个 L1 Tile 时才使用FINAL_ACCUMULATION3其余迭代全部使用NO_FINAL_ACCUMULATION2。这与 README 中最后一块尾块的 UnitFlag 参数配置与中间块不同的说明完全一致。注意样例源码中该赋值是无条件执行的未用EN_UNIT_FLAG编译开关包裹即默认开启该优化。FixPipe 侧配置main.ascauto copyL0C2GM AscendC::Te::MakeCopy(AscendC::Te::CopyL0C2GM{}); AscendC::Te::Copy(copyL0C2GM.with(AscendC::Te::FixpipeParams{tool::FINAL_ACCUMULATION}), tensorCGmBlock, tensorL0C);在样例源码中搬出指令通过.with(FixpipeParams{FINAL_ACCUMULATION})显式携带 unitFlag3与 README 代码片段中的UNITFLAG_EN_OUTER_LAST语义一致。此外从 main.asc 可以看到该实现已用基于 Mutex 的流水同步AscendC::Mutex::Lock/UnlockPIPE_MTE2/PIPE_MTE1/PIPE_M取代了 README 基线代码中的SetFlag/WaitFlag事件同步分别保护 L1 与 L0 双缓冲区的读写依赖内核以KERNEL_TASK_TYPE_DEFAULT(KERNEL_TYPE_AIC_ONLY)声明为 AI Core 专属任务并以 bfloat16 精度完成C A × B的矩阵乘计算。3.4 修改注意点mmad 计算参数配置需注意循环边界处理最后一块尾块的 UnitFlag 参数配置与中间块不同。若所有块都配置为非尾轮2L0C 中的累加结果永远不会被标记为可搬出若都配置为尾轮3则中间块的中间结果会被提前搬出破坏累加语义。必须精确地在最后一轮 K 迭代 × 最后一轮 L1 迭代处切换为FINAL_ACCUMULATION。fixpipe 参数配置需要深入理解 UnitFlag 值的含义与作用顺序根据实际场景按需配置。FixPipe 侧的 unitFlag 必须与 MMAD 侧的尾轮判定保持一致二者共同决定何时允许将 L0C 结果搬回 GM。4. 性能结果对比4.1 测试条件与 Profiling 观察以基础 MatMul 算子为例在相同输入规模M1024, K2048, N4096下进行性能测试通过 Profiling 工具采集硬件流水线执行状态。未开启 unit_flag 优化时FixPipe 数据搬移与 MMAD 计算交替执行流水线中存在明显的等待空洞开启 unit_flag 后FixPipe 数据搬移流水线与 MMAD 计算流水线实现深度并行有效隐藏了数据搬移延迟。值得注意的是优化后的时序图中fixpipe 流水段长度显著增加。其根本原因在于 fixpipe 与 mmad 的流水线解绑解绑后fixpipe 的指令下发时机提前但其对应的数据搬运操作并未同步启动而是延迟至尾轮计算完成、数据累加结束后才进行实际的数据搬移。下一轮 mmad 计算必须等待 fixpipe 完成数据搬运后方可开始导致该轮 mmad 的计算等待时间延长整体流水线出现展宽4.2 Profile 数据明细通过python3 profile_matmul.py 1024 2048 4096采集到的指标kernel / mac / scalar / mte1 / mte2 / fixpipe 时间单位 us以及 icache miss 率如下candidatekernel(us)mac(us)scalar(us)mte1(us)mte2(us)fixpipe(us)icache_miss(%)unit_flag85.90742.2572.67711.13935.63922.8021.100matmul86.87043.8041.85012.99751.8572.9702.200对比两份数据可以得出mte2数据搬入时间从 51.857us 降至 35.639us降幅约 31%——流水并行后搬入效率显著提升这正是搬运效率提升的直接体现macMMAD 计算时间从 43.804us 降至 42.257us计算流水本身也更饱满fixpipe 时间从 2.970us 增至 22.802us与前述fixpipe 流水段展宽的分析吻合——流水解绑后 fixpipe 承担了更多与计算重叠的搬出任务其自身耗时变长但不再阻塞计算整体 kernel 时间从 86.870us 降至 85.907usicache miss 率从 2.2% 降至 1.1%算子整体性能得到提升。可见FixPipe 数据搬移流水线与 MMAD 计算流水线并行执行是整体性能提升的核心来源。5. 结论与适用场景unit_flag 通过硬件流水线实现搬运与计算并行在数据切分合理的大规模矩阵运算中可显著提升性能是优化 NPU 计算效率的关键特性。其典型适用场景包括双缓冲机制下FixPipe 数据搬出与计算单元串行执行影响后续核心的输出效率。为避免因缓存资源争用导致的计算流水线停顿可通过开启 unit_flag 实现搬出与计算的流水并行硬件指令支持以小数据块为单位批量搬出数据从而降低搬出延迟提升流水线整体效率。在决定是否启用该特性前建议先结合 Profiling 数据分析当前瓶颈若mte2/fixpipe等搬运时间占比过高、计算与搬运存在明显串行等待可优先尝试 unit_flag以及 N-Buffer特性介绍 中的双缓冲优化同时需要结合 Tiling 策略确保切分后的 Tile 大小在开启缓存机制后不超出硬件资源。6. 编译、运行与性能测试6.1 编译样例从项目根目录启动构建环境准备与整体构建方式参考项目 README.md。在仓库根目录下完成编译和安装后进入当前样例目录cmake -S . -B build -DNPU_ARCHdav-3510 cmake --build build --parallel cmake --install build --prefix ./build_out cd ./build_out/1_Features/instruction_optimization/unit_flag/如需单独编译当前样例可使用以下指令cmake --build build --target unit_flag cp ./Samples/1_Features/instruction_optimization/unit_flag/scripts/* ./build/Samples/1_Features/instruction_optimization/unit_flag/ cd ./build/Samples/1_Features/instruction_optimization/unit_flag/从 CMakeLists.txt 可以看到该样例通过cann_sample_check_arch(dav-3510)约束了目标架构以add_executable(unit_flag main.asc)构建可执行文件链接platform、tiling_api、cann_samples::tensor_api等库并通过 POST_BUILD 与 install 规则将scripts/目录数据生成、精度校验、性能测试脚本随产物一同分发。6.2 运行样例使用可执行文件直接执行算子用例需要指定矩阵乘维度并随机生成输入数据./unit_flag 1024 2048 4096运行成功后终端将打印类似如下信息主程序会依次调用gen_data.py生成输入与 CPU 参考结果、执行内核、调用verify_result.py校验 NPU 与 CPU 结果一致性Data generated successfully! [verify] shape(1024, 4096), elements4194304 - summary (large matrix, full tensors omitted) abs_err: max2.560000e02, mean7.385254e-03, rmse1.375000e00 rel_err: max6.410256e-03 count(|abs_err| 0.001): 121 / 4194304 cpu golden (top-left 4x4): tensor([[42496., 42496., 41728., 41728.], [41728., 41984., 41216., 40960.], [41984., 41984., 41216., 41216.], [41216., 41472., 40960., 40960.]], dtypetorch.bfloat16) npu out (top-left 4x4): tensor([[42496., 42496., 41728., 41728.], [41728., 41984., 41216., 40960.], [41984., 41984., 41216., 41216.], [41216., 41472., 40960., 40960.]], dtypetorch.bfloat16) max abs diff: 256.0 point error count(0.1): 0/4194304 ratio error count(0.001): 121/4194304, error ratio: 0.000029 [PASS] NPU results are consistent with CPU.如果存在精度问题则会打印错误数据并显示如下结果[ERROR] NPU results differ from CPU.精度判定逻辑位于 verify_result.py要求单点相对误差abs_diff/abs(golden)不超过 1e-1且绝对误差超过 1e-3 的点占比不超过 1e-3。数据生成脚本 gen_data.py 使用torch.bfloat16生成输入并以torch.matmul计算 CPU golden。6.3 测试性能运行性能测试脚本指定矩阵乘法的维度后执行python3 profile_matmul.py 1024 2048 4096打印如下执行结果证明样例性能测试成功[Profile Breakdowm] --------------------------------------------------------------------------------------------- | candidate | kernel(us) | mac(us) | scalar(us) | mte1(us) | mte2(us) | fixpipe(us) | icache_miss(%) | | unit_flag | 85.907 | 42.257 | 2.677 | 11.139 | 35.639 | 22.802 | 1.100 | ---------------------------------------------------------------------------------------------与相同输入规模下的基础 matmul 算子相比[Profile Breakdowm] --------------------------------------------------------------------------------------------- | candidate | kernel(us) | mac(us) | scalar(us) | mte1(us) | mte2(us) | fixpipe(us) | icache_miss(%) | | matmul | 86.870 | 43.804 | 1.850 | 12.997 | 51.857 | 2.970 | 2.200 | ---------------------------------------------------------------------------------------------从 profile_matmul.py 的实现看该脚本会自动发现当前目录下的所有可执行文件作为候选因此只要把基线matmul与优化版unit_flag两个可执行文件放到同一目录即可对比逐个用msprof采集 Profiling 数据解析op_summary_*.csv中的Task Duration(us)、aic_mac_time(us)、aic_mte1_time(us)、aic_mte2_time(us)、aic_fixpipe_time(us)、aic_icache_miss_rate等字段按 kernel 时间升序给出推荐算法排序与完整指标表。可以看到FixPipe 数据搬移流水线与 MMAD 计算流水线并行执行提升了整体性能。7. 支持架构当前样例支持NPU ARCH 3510对应构建参数-DNPU_ARCHdav-3510其他架构暂未适配。若需在其他昇腾硬件上使用该特性建议先确认目标芯片是否支持 FixPipe 的小数据块批量搬出指令级能力。更多指令优化类样例如 N-Buffer、MTE2 预取、weightnz 等可参考 instruction_optimization 目录总览。【免费下载链接】cann-samplesCANN高性能实战演进样例与体系化调优知识库项目地址: https://gitcode.com/cann/cann-samples创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询