CANN graph-autofusion SuperKernel 优化与调试选项实战:AOT 样例 example02_sk_options 全解
【免费下载链接】graph-autofusionGraph-autofusion 是一个面向昇腾(Ascend)芯片的轻量级、解耦式组件集合,旨在通过自动融合技术加速模型执行。 目前已开源 SuperKernel 组件和 Autofuse 组件,未来将持续开放更多自动融合相关模块。项目地址: https://gitcode.com/cann/graph-autofusion
本文基于 graph-autofusion 仓库super_kernel/examples/aot/example02_sk_options/下的 AOT 样例,系统讲解 SuperKernel 的 optimize/debug options 如何配置、如何随torch.compile下发到编译管线,以及仓库中SuperKernelOptionsManager对这些选项的解析、校验与默认值处理。读完本文,你能完整跑通该样例(dav-2201/dav-3510双架构),理解super_kernel_optimize_options与super_kernel_debug_options中每个开关的语义、取值范围和源码级实现依据,并掌握静态编译产物与 eager 真值校验的工程做法。
一、样例定位与运行方式
该样例(README)的定位是:演示 SuperKernel 的 optimize/debug options,并用 eager 执行结果校验静态编译(static kernel compile)输出。它属于 SuperKernel AOT(Ahead-Of-Time)编译链路的官方示例,与example01_dual_stream、example03_kernel_pybind并列,专注于"编译期选项"这一主题。
在仓库根目录执行:
bash super_kernel/examples/aot/example02_sk_options/run.sh --npu-arch=dav-2201要点说明:
- 该样例支持
dav-2201和dav-3510两种 NPU 架构,通过--npu-arch参数指定,脚本会根据架构选择对应的 attention 网络脚本(run.sh 中的case分支:dav-2201选main-dav-2201.py,dav-3510选main-dav-3510.py); - 运行设备为"当前可见 NPU"(脚本本身不做设备选择,由
torch_npu默认调度); - 前置依赖是
torch_npu的npugraph_ex编译后端与 SuperKernel 静态编译能力(CANN 环境已安装),运行前请先确认环境。
1.1 run.sh 的完整执行流程
run.sh 只有 26 行核心逻辑,但它串联了完整的"清理—编译—校验—回滚"闭环,值得逐段拆解。其公共函数来自 common.sh(source ../scripts/common.sh):
- 参数解析:
sk_parse_npu_arch基于getopt解析--npu-arch,并做白名单校验(sk_validate_npu_arch只接受dav-2201|dav-3510,其余值报错退出码 2); - 工作区清理:
sk_cleanup_local删除上一轮残留的log、tmp、sk_meta、kernel_meta、profiling、static_kernel_compile_outputs、aclnn_static_shape_kernel_outputs、.static_kernel_records.json,保证每次运行从干净状态开始; - 日志捕获执行:
sk_run_python_with_log以tee方式把 Python 进程的全部输出写入tmp/run.log,并通过PIPESTATUS[0]返回真实退出码; - 静态编译产物校验:
sk_check_static_kernel_outputs ... required检查static_kernel_compile_outputs/下是否生成了*.run安装包,同时排查*_compile_error.log;由于传入了required,没有生成.run包即判定样例失败——这正对应该样例"静态编译产物 + eager 真值校验"的双重验收标准; - 退出回滚:
trap 'sk_uninstall_static_kernel_from_log' EXIT保证无论成功失败退出,都会从run.log中提取本次安装的uninstall.sh路径并执行卸载。该函数还做了严格的安全校验:卸载脚本必须位于$ASCEND_HOME_PATH/opp/static_kernel/ai_core/<包名>/uninstall.sh,与本次生成的*.run包一一对应,拒绝执行符号链接、路径逃逸(含..)或不属于本包目录的脚本,避免误删系统已装的静态内核。
这套"生成即校验、退出即回滚"的脚本结构,对自建 SuperKernel AOT 回归脚本有直接参考价值。
二、被测网络:双架构 attention 图
样例主脚本中的Network是一个刻意构造的多 op 图,用于覆盖 SuperKernel 优化选项的实际作用面。以 main-dav-2201.py 为例,其前向拓扑为:
query, key, value, length | v [fia_01] npu_fused_infer_attention_score | v [res] npu_moe_gating_top_k_softmax_v2 (k=1024) | v [quant] npu_dynamic_quant -> to(float16) | v [ifa_01] npu_incre_flash_attention <--- key, value, length | v [fia_02] npu_fused_infer_attention_score <--- key, value, length | +-----------> torch.add <--- npu_grouped_matmul | v outputs: (add_01, ifa_01)- 输入规模:
batch=3, seq_len=256, hidden=1024,全部float16上 NPU,length=[99,199,180]表示逐样本变长序列; - 图中同时包含 fused attention、MoE gating top-k softmax、动态量化、增量 flash attention、grouped matmul 与 elementwise add,存在跨 op 的数据依赖与并行可能,正是 SuperKernel 跨核融合、op 并行等优化选项需要处理的典型场景。
两个架构版本的主要差异(对比 main-dav-3510.py):
| 项目 | dav-2201 版 | dav-3510 版 |
|---|---|---|
| 中间 attention op | npu_incre_flash_attention(增量解码形态) | npu_fused_infer_attention_score(decode 形态) |
| 序列长度参数 | 单一actual_seq_lengths=length | 拆分为actual_seq_lengths=query_lengths与actual_seq_lengths_kv=kv_lengths(query 长度[1,1,1],模拟 decode 场景) |
| 输出 | (add_01, ifa_01) | (add_01, fia_decode_01) |
也就是说,同一套 options 配置会分别作用于两种不同形状的 attention 图上,这对验证选项行为的架构无关性(以及架构相关内部选项的自动开启,见下文)很关键。
三、SuperKernel 优化选项详解
样例通过torch.compile(..., backend="npugraph_ex", options={...})把 SuperKernel 选项带入编译管线,核心字段是super_kernel_optimize: True(总开关)和super_kernel_optimize_options。样例中实际启用的优化选项如下(取自两个主脚本中完全一致的build_model()):
options={ "static_kernel_compile": True, "super_kernel_optimize": True, "super_kernel_optimize_options": { "auto_op_parallel": 0, "dcci_before_kernel_start": [".*"], "dcci_after_kernel_end": [".*"], "dcci_disable_on_kernel": [".*"], "early_start": 1, "aggressive_opt_strategies": { "value_breaker_bypass": 0b10, "task_breaker_bypass": 0b00, }, }, "super_kernel_debug_options": { "debug_sync_all": 0, "debug_op_exec_trace": 0, "debug_cross_core_sync_check": 0, "debug_per_op_max_core_num": 0, }, },3.1 优化选项逐项解析
各选项的名称、类型、默认值与合法取值范围,由 sk_options_manager.cpp 中的默认选项工厂表(DEFAULT_OPTION_FACTORIES)与SetOptOptionValue解析逻辑共同定义;选项枚举与结构体定义见 super_kernel.h。
| 选项 | 类型 | 默认值 | 合法范围 | 样例取值与含义 |
|---|---|---|---|---|
auto_op_parallel | 整数 | 0 | [0, 1] | 0,关闭自动 op 并行 |
dcci_before_kernel_start | 字符串列表 | 空 | 模式串 | [".*"],对所有 kernel 在启动前允许 DCCI 调度策略 |
dcci_after_kernel_end | 字符串列表 | 空 | 模式串 | [".*"],对所有 kernel 在结束后允许相应 DCCI 策略 |
dcci_disable_on_kernel | 字符串列表 | 空 | 模式串 | [".*"],对所有 kernel 施加该禁用策略 |
early_start | 整数 | 0(ACLSK_EARLY_START_DISABLED) | [0, 1] | 1,启用 early start 全局选项 |
aggressive_opt_strategies | 结构体 | 全 0 | 见下 | 激进融合策略开关 |
补充说明源码中同样注册、但样例未显式设置的优化选项(它们会走默认值):
preload_code:整数,默认1,合法范围 [0, 2];split_mode:整数,默认4,合法范围 [1, 4](splitCnt);stream_fusion:整数,默认1,合法范围 [0, 1];kernel_map:kernel 名映射(aclskKernelMapOption,每个条目最多 4 个sknlNames);ubuf_lock_ignore_kernel:字符串列表,配置按名字模式"忽略 MIX kernel split"的 op;opt_extend_option/debug_extend_option:字符串 map 形式的扩展预留位。
3.2 模式串(正则)匹配规则
dcci_*系列选项接受的是 kernel 名模式列表。从源码结构看(SuperKernelOptionsManager::MatchRegex与IsValidRegexPattern,sk_options_manager.cpp):
- 仅支持字符:字母、数字、
_、-、.、*(其中.匹配任意字符,*为通配); - 模式不允许以
*开头,且不能为空; - 因此样例中的
".*"表示"匹配所有 kernel",是"对全部 op 生效"的写法;实际项目中通常换成如"MatMul_*"这样更窄的模式,只对被怀疑受 DCCI 影响的 kernel 生效。
匹配逻辑基于二维动态规划(matchFlag[m+1][n+1]),对每个 kernel 名逐一尝试列表中所有模式,命中任一即返回真。
3.3 aggressive_opt_strategies:位掩码语义
aggressive_opt_strategies对应 C API 结构体aclskAggressiveOptStrategies(eventBreakerBypass/valueBreakerBypass/taskBreakerBypass三个字段),其含义在 super_kernel.h 的注释中定义得很明确:
eventBreakerBypass:为 event breaker 策略预留,样例未设置(解析侧校验其取值在 0 到类型上限之间);valueBreakerBypass:值依赖内存等待策略的位掩码,取值为枚举aclskValueBreakerBypassFlag:0b00(ACLSK_VALUE_BREAKER_BYPASS_NONE):拒绝 notify/wait 配对关系,保持 wait 不可融合;0b01(ACLSK_VALUE_BREAKER_BYPASS_PAIRED_WAIT):对于"配对"的 notify+wait,仍要求既有规则通过,否则 SuperKernel 优化退出;0b10(ACLSK_VALUE_BREAKER_BYPASS_UNPAIRED_WAIT):对于规则检查后"没有对应 notify 的 wait",放行其融合。样例取0b10,即允许这类单边 wait 参与融合——这是一种更激进(但需自行验证数值正确性)的策略;
taskBreakerBypass:为 1 时启用默认节点旁路,合法范围 [0, 1]。样例取0b00即关闭。
解析侧对这三个字段分别调用GetValidatedUintValue做范围校验:越界时打SK_LOGW并回退到默认值(如value_breaker_bypass默认ACLSK_VALUE_BREAKER_BYPASS_NONE),而不是直接报错。
3.4 选项解析与校验机制(源码视角)
SuperKernelOptionsManager::ParseOptions(sk_options_manager.cpp)是选项进入 SuperKernel 的唯一入口,其行为要点:
- 先执行
RegisterDefaultOptions():注册全部默认选项 → 注册内部选项 → 应用架构相关选项(下一节展开); - 遍历
aclskOptions数组逐项调用SetOptOptionValue;同一optionType重复出现时只取第一个(后到项打 warning 跳过),因此 Python 侧 options dict 的键不应重复下发; - 数值选项统一走
NumberOptOption::SetValue的 [min, max] 区间校验,越界回退默认值;字符串列表选项不允许空列表; - 所有生效值最终可通过
ToJson()序列化(sk_dump_json.cpp 相关链路会将其落盘到 JSON dump),便于排查"我下发的选项到底生效成什么样"。
3.5 架构相关的内部选项
ApplyArchSpecificOptions会根据当前 kernel 架构(GetCurrentSkKernelArch(),见 sk_common.h)自动开启内部选项(SkInnerOptionType,用户不可直接设置,只能通过 JSON dump 观察):
dav-3510(DAV_3510):自动开启mix_kernel_split与simt_op_support;dav-2201(DAV_2201):不开启,保持默认。
这解释了样例为何要按架构拆成两套 main 脚本:同样的选项配置,在两种架构上的内部行为基线本身就不同,attention 网络形态(prefill vs decode)也需要分别构造。
四、SuperKernel 调试选项详解
super_kernel_debug_options下的四个开关在样例中全部显式置0(关闭),但它们的语义与联动关系值得讲清楚,默认注册与联动逻辑均在 sk_options_manager.cpp:
| 调试选项 | 默认值 | 合法范围 | 作用 |
|---|---|---|---|
debug_sync_all | 0 | [0, 1] | 置 1 时进入调试模式(EnableDebug()判debug_sync_all == 1),执行路径上加入全同步行为,用于定位跨核时序问题 |
debug_op_exec_trace | 0 | [0, 1] | 开启 op 级执行 trace |
debug_cross_core_sync_check | 0 | [0, 1] | 开启跨核同步检查 |
debug_per_op_max_core_num | 0 | [0, 1] | 开启"每 op 最大核数"限制调试 |
一个重要的联动规则:当debug_per_op_max_core_num被置 1 时,解析逻辑会自动补开debug_cross_core_sync_check(若其当前不为 1 则自动置 1,并打日志[DEBUG_PER_OP_MAX_CORE_NUM] auto-enabled DEBUG_CROSS_CORE_SYNC_CHECK)。因此在样例中如果只把debug_per_op_max_core_num改为 1,实际生效的调试面会比字面上多一项——读 dump JSON 或日志时可以留意这一自动开启。
调试选项与优化选项的分层设计(super_kernel_optimize_optionsvssuper_kernel_debug_options)让"性能调优实验"与"正确性排查"可以独立开关,这也是该样例将两组选项显式全量列出的原因:它本身就是这两组选项的活文档。
五、正确性校验:eager 真值 vs 静态编译输出
样例main()的验证流程是静态编译可信度的标准姿势(两个主脚本一致):
- 固定随机种子(
torch.manual_seed(1234)、np.random.seed(1234))生成输入; - 先用未编译的
eager_model跑一遍,torch.npu.synchronize()后detach().clone()保存期望输出; - 再用
torch.compile(backend="npugraph_ex", fullgraph=True, dynamic=True, options={...})编译后的模型跑同样(clone 后的)输入,得到实际输出; - 对两个输出分别做
torch.testing.assert_close(actual, expected, rtol=1e-3, atol=1e-2); - 通过后打印两份输出摘要(shape、dtype、mean)以及
真值校验通过 / 测试完成!;失败路径区分返回码:断言失败返回 1(真值校验失败),其他异常返回 2 并打印 traceback。
结合run.sh的sk_check_static_kernel_outputs ... required,该样例的最终通过条件是"双重的":Python 侧数值校验通过且static_kernel_compile_outputs/下确实生成了静态编译*.run包。对dav-2201版还额外校验了npu_incre_flash_attention输出(ifa_out)与add_out两条输出流。
六、延伸阅读:仓库内相关源码与文档
- 选项定义与 C API:super_kernel.h(
aclskOptionType枚举、各aclsk*Option结构体、aclskValueBreakerBypassFlag、aclskOptimize接口声明); - 选项管理器:sk_options_manager.h(
OptOptionBase类型体系、SuperKernelOptionsManager接口与CollectAllOptionsdump 工具)、sk_options_manager.cpp(默认值、范围校验、正则匹配、架构选项、JSON 序列化); - 样例脚本:run.sh、main-dav-2201.py、main-dav-3510.py、公共函数 common.sh;
- 姊妹样例:example01_dual_stream(双流场景)与 example03_kernel_pybind(pybind 自定义 kernel);
- 背景文档:SuperKernel 开发者指南、SuperKernel 迁移设计,以及仓库内 super_kernel/README.md。
README 中指向的 TorchAir SuperKernel 使用说明属于外部仓库文档,本文不展开;本文所有选项语义均以本仓库super_kernel/源码为准。
七、小结
example02_sk_options用一份双架构 attention 图 + 一组显式列出的 optimize/debug options + eager 真值校验,构成了 SuperKernel 静态编译选项的最小可复现实验闭环。实践建议:
- 以样例为模板新建回归脚本时,保留"清理 → 编译 → 校验
.run产物 → 退出自动卸载"的run.sh结构,可显著降低 AOT 实验的环境污染风险; - 调整
dcci_*模式串时记住限制字符集(不允许以*开头),".*"只代表全量生效,不应直接搬进生产配置; - 尝试
value_breaker_bypass=0b10等激进策略后,必须重新走 eager 真值校验流程,并以 options JSON dump 确认实际生效值; - 在
dav-3510上注意mix_kernel_split/simt_op_support内部选项的自动开启,行为基线与dav-2201不同。
【免费下载链接】graph-autofusionGraph-autofusion 是一个面向昇腾(Ascend)芯片的轻量级、解耦式组件集合,旨在通过自动融合技术加速模型执行。 目前已开源 SuperKernel 组件和 Autofuse 组件,未来将持续开放更多自动融合相关模块。项目地址: https://gitcode.com/cann/graph-autofusion
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考