引言
在开发昇腾NPU算子的过程中,可能存在以下问题:
算子改动了之后出现精度异常,逐行排查代码才发现原来是同步指令不配对,导致流水线间互相竞争。
算子同步指令错乱,发现流水线上的同步指令pipe参数写错了,后面的同步指令全部没对上。
因为同步指令的不匹配或冗余导致算子异常导致的问题,使用传统的逐行排查方法费时又费力,但在msSanitizer(MindStudio Sanitizer)同步检测功能面前,这都不是问题。
msSanitizer同步检测——基于算子真实运行环境采集指令信息并回放分析,直接输出异常同步指令代码位置,帮助你精准定位异常代码行,全面保障你的算子同步安全。本文将通过实际算子样例,手把手带你体验如何使用工具一秒揪出算子中的同步问题!
同步异常原理解析
同步指令原理
与CPU程序串行执行不同,NPU AI Core在执行算子计算时是异步并行的。不同的执行单元(Vector、Cube、MTE2/3等)各自执行自己的计算指令,形成多个序列流水线。在一个完整的计算任务中,不同的流水之间很可能存在数据依赖,如A流水线任务依赖B流水线的运行结果,此时必须通过同步指令来约束执行顺序。
以一个最简单的“搬运-计算”代码流程为例:
在算子运行过程中,上述两行代码的执行可能是这样的:
从图中可以直观看出:PIPE_V必须等待PIPE_MTE2执行完后才能开始计算。想要算子按我们的意愿执行,必须严格约束各流水线之间的执行顺序,防止出现这种一边没跑完另一边就已经开始了的情况。我们再来看一下正例:
通过使用set_flag和wait_flag控制流水线执行顺序,我们可以保证所有计算按我们的预期执行。同步控制的形式和方法有很多,这里不展开,感兴趣的开发者可以参阅昇腾社区相关文档了解学习更多内容:
https://www.hiascend.com/document/detail/zh/canncommercial/900/API/ascendcopapi/atlasascendc_api_07_0179.html
同步异常及其后果
同步控制对算子正常运行是不可或缺的,但不安全地使用同步指令同样会带来严重后果。典型的同步异常就是set/wait没有配对:
仅有set_flag:和不使用set_flag一样没有阻塞效果,甚至更糟——硬件标志位被置位后,后续该pipe上任何复用了该eventID的wait_flag都会误认为数据已经就绪,导致逻辑错误,即“错位匹配”。这在多算子环境下甚至可能影响其它算子的精度。
仅有wait_flag:被等待pipe将一直维持等待状态,但由于永远不会有 set_flag来置1,当前AI Core会在此处死锁,后续所有指令都无法执行。
针对上面两种场景,msSanitizer提供了同步检测功能,支持主动识别算子内潜在的同步异常,并提示用户进行优化。下面我们就一起来看看怎样使用msSanitizer来优化算子。
实战演练
在本节中,我们将会通过一个简单算子,带你一步一步学会如何使用msSanitizer进行同步场景异常检查。
准备工作
在正式进入实践之前,我们需要做一些基础准备,请按以下步骤确保环境安装好msSanitizer:
msSanitizer安装指南:
https://gitcode.com/Ascend/mssanitizer/blob/master/docs/zh/install_guide/mssanitizer_install_guide.md
同步检测 - 配对检测
完成 msSanitizer 工具安装后,将基于以下算子示例代码开展配对检测功能验证。
分析一下代码:在36行有同步操作,但当前的用法不正确:SetFlag应当与WaitFlag配对使用,否则就会对后续同步指令的配对造成影响,进而引发算子非预期行为。现在,跟着如下三步走策略即可让异常直接显形:
第一步:在CMakeLists.txt内增加检测编译和链接选项--cce-enable-sanitizer或-sanitizer,以保证启用msSanitizer能力:
第二步:执行编译命令,重新编译算子:
mkdir build && cd buildcmake .. && make -j96
第三步:使用检测工具,指定使用同步检测功能,拉起算子二进制进行检测:
mssanitizer -t synccheck demo
命令解析:
- mssanitizer:msSanitizer检测工具关键字
- -t:指定检测类型,具体支持类型参考官方文档
- synccheck:同步检测功能关键字
- test.fatbin:算子二进制文件
现在,符合预期的话,可以看到如下图所示的检测结果:
看起来好像很复杂,我们来对照图里的编号一步一步拆解输出结果:
异常类型,当前有两种:
Unpaired – 同步指令未配对
Redundant – 同步指令冗余
异常指令类型,set_flag or wait_flag。
异常指令所在的源和目的pipe。
发生异常的算子名称。
异常算子所在的aicore类型和编号。
异常算子所在的device的id。
发生异常时的调用栈。
发生异常指令的序列号(可以不用关注)。
异常指令的调用栈,指示源码位置。
以上所有信息组成了一条完整的异常告警。
此时我们可以发现:输出信息第⑨项已经明确指示了异常代码在算子源文件中的位置,如此一来便可以直接把这行罪魁祸首揪出来删掉,再也不用自己一行一行看源码排查了。
到这里,你应该已经明白配对检查的功能了:msSanitizer可以抓出“落单”的同步指令。它会检查算子全量的同步指令是否能构成配对,即相同“pipe组合 + event id”的set_flag/wait_flag需要构成闭环,否则就会生成如上告警提示用户。当你不小心多了一行,或者pipe/event id用错了的时候,可以立即发现并改正。
同步检测 - 冗余检测
接来下我们再学习同步检测的另一项优化检查能力,这项能力可以帮我们更好的优化代码。来看我们的测试代码:
这里36/37和43/44都是同一对pipe对同一ENEVT的同步,二者完全相同,在同步处理的逻辑上我们可以将其优化仅保留一对同步操作。
我们再按刚刚的三步走策略重新编译并使用msSanitizer执行一下算子。所有的命令都与之前相同,但一定要保证编译时携带检测编译选项,否则在算子二进制中将无法使用工具检测能力。
第一步:在CMakeLists.txt内增加检测编译和链接选项--cce-enable-sanitizer或-sanitizer,以保证启用msSanitizer能力:
第二步:执行编译命令,重新编译算子,图略:
mkdir build && cd buildcmake .. && make -j96
第三步:使用检测工具,指定使用同步检测功能,拉起算子二进制进行检测:
mssanitizer -t synccheck demo
算子运行结束后,我们得到如下结果:
此时我们可以看到:同步检查输出了两对冗余异常:分别指示36行和43行的SetFlag冗余、以及17行和20行的WaitFlag冗余。它们在同步指令语义上可以合并成一对,仅保留37/44行的代码即可。
这就是msSanitizer同步检测的冗余检查能力:它可以识别出来算子同步指令中“pipe组合 + event id相同”的同步指令间是否构成冗余,如果这样的指令/指令对间没有任何对应pipe的其他操作,那意味着它们在逻辑上可以合并到一起。这样做可以优化代码结构、提高算子计算的效率,并避免未来演变为配对错误。
总结
经过上述这个简单的示例,相信你已经掌握了msSanitizer同步检测的两大基本能力。我们再来复习一下:
这两项能力可以帮你快速定位同步问题,显著减少人工排查时间,提升算子开发与调优效率。
当然,作为昇腾AI算子异常检测工具,msSanitizer除了同步检测以外,还有内存检测、竞争检测和未初始化检测三大核心功能,可以通过阅读文末的官方文档来获取更多信息。
官方资源:
msSanitizer官方文档:
https://gitcode.com/Ascend/mssanitizer/blob/master/README.md
扫描二维码,一键直达msSanitizer开源社区
扫描二维码,一键直达MindStudio开源技术交流群


