Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

中文 | English

附录 I:性能差异分析

对真实 ascendc-to-rs 转译批次中 998 个 CANN 8.5 内核往返性能模式的分析(与 附录 G 同一语料库)。

ascend-rs 编译管线(Rust → MLIR → C++ → bisheng)相对手写 AscendC C++ 引入了特定的代码生成模式。本附录识别这些模式、对其影响分类并提出通用的优化建议。

范围说明。 998 个内核中,247 个为 Transpiled(真实计算主体),751 个为 Registered(identity stub 主体)。§I.2 的减速模式(TBuf 对比 TQuePIPE_ALL 屏障、无双缓冲、统一缓冲区尺寸)是 codegen 路径 的属性 —— 是 mlir_to_cpp.rs 对任何含 DMA+计算主体发射的固定模式。Registered 内核所发射的 stub 从技术上也呈现 TBuf 模式,但由于 stub 主体只做一次拷贝,2% 的减速数字仅对 247 个 Transpiled 内核才有意义。表格计数以全部 998 为分母,因为 codegen 路径是统一的;若读者关心的是实际运行时差距,应将分母限定为 247。

I.1 性能分类

分类数量%描述
EQUIVALENT12112%生成代码与原始 C++ 性能一致
SLOW_1.02X87788%约 2% 慢,来自屏障和缓冲区开销模式
SLOW_1.2X00%约 20% 慢(未观测到)
SLOW_1.5X00%约 50% 慢(未观测到)
SLOW_2X+00%2× 或更慢(未观测到)

备注:2% 开销源于 TBuf + PIPE_ALL 模式;在 NPU kernel-launch 粒度上实际运行时差通常在测量噪声之内。

I.2 减速模式

TBuf 取代 TQue(HIGH)

受影响内核: 998/998

问题: 使用 TBuf<VECCALC> 而非 TQue<VECIN/VECOUT>TBuf 在每个同步点都需要显式的 pipe_barrier(PIPE_ALL),而 TQue 通过硬件 flag 实现细粒度 pipe 重叠。

修复: 生成 TQue<QuePosition::VECIN, depth>,用 AllocTensor / FreeTensor 生命周期替换 TBuf.Get / TBuf.Get 模式。


PIPE_ALL 屏障(整流水线停顿)(HIGH)

受影响内核: 998/998

问题: 每次 ascend_pipe_barrier() 都生成 pipe_barrier(PIPE_ALL),同时阻塞 所有 硬件 pipe。原始 C++ 通过 TQue 或选择性 PIPE_V / PIPE_MTE2 flag 进行 per-pipe 同步。

修复: 对仅计算同步使用 pipe_barrier(PIPE_V),对 DMA 同步使用 PIPE_MTE2,或用 TQue 彻底消除屏障。


无双缓冲(HIGH)

受影响内核: 998/998

问题: DMA 与计算完全串行化:load → barrier → compute → barrier → store。原始 C++ 以 TQue depth = 2 实现 tile N+1 DMA 与 tile N 计算的重叠。

修复: 检测 tiling 循环,生成 depth = 2 的 TQue。使用 EnQue / DeQue 在多个 tile 之间重叠 DMA 与计算。


统一最大缓冲区尺寸(LOW)

受影响内核: 998/998

问题: 所有 TBuf 分配相同的最大尺寸 =(UB_SIZE − 8 KB)/ num_bufs。原始 C++ 按实际数据需要调整各缓冲区大小。当缓冲区实际用量不同时会浪费 UB 空间。

修复: 在 MLIR 中追踪实际缓冲区用量并按比例分配。


标量数学向量化变通(MEDIUM)

受影响内核: 1/998

问题: 标量 log / exp / sqrt 通过一个 1 KB 临时缓冲区被向量化,因为在某些 NPU 型号上标量流水线会挂起。为每个标量数学 op 增加 DMA + 缓冲区开销。

修复: 在支持的型号上使用标量 pipe;在不支持的型号上通过批处理标量 op 来摊销。


I.3 优化机会

屏障消除机会(MEDIUM)

适用内核: 998/998

描述: 针对 不同 缓冲区的连续向量 op 之间不需要屏障。当前 codegen 只要 dirty_bufs 有重叠就插入屏障,但许多 op 相互独立。

实现: 在 MLIR 层实现 per-buffer dirty 追踪。仅当 同一 缓冲区存在读后写冒险时才插入屏障。


循环展开候选(LOW)

适用内核: 998/998

描述: 固定小迭代次数循环(例如 softmax 的 2 次 reduce)可以展开。当前 codegen 发射通用 while (true) 循环。

实现: 检测已知小迭代次数的循环并展开。


算子融合候选(MEDIUM)

适用内核: 0/998(未来)

描述: 对同一缓冲区的连续向量 op(例如 SubExpDivCast)可以融合为单个向量指令,或至少共享一次屏障。

实现: 检测同一缓冲区上的一元/二元 op 链并融合为复合 AscendC 指令。


I.4 通用优化计划

基于模式分析,三个优化可以为大多数内核关闭性能差距:

优先级 1:TQue 迁移(关闭 ~50% 的差距)

在 MLIR → C++ codegen 中用 TQue<VECIN/VECOUT> 替换 TBuf<VECCALC>。这消除了 PIPE_ALL 屏障,改用基于硬件 flag 的同步,并启用 DMA / 计算双缓冲重叠。

受影响文件: crates/rustc_codegen_mlir/src/mlir_to_cpp.rs

需要的改动:

  1. 将缓冲区声明从 TBuf<TPosition::VECCALC> 改为 TQue<QuePosition::VECIN> / TQue<QuePosition::VECOUT>
  2. tbuf.Get<T>() 替换为 inQueue.AllocTensor<T>() / inQueue.DeQue<T>()
  3. 添加 inQueue.EnQue(tensor) / outQueue.FreeTensor(tensor) 生命周期。
  4. pipe_barrier(PIPE_ALL) 替换为隐式 TQue 同步。

优先级 2:屏障消除(关闭 ~20% 的差距)

实现 per-buffer dirty 追踪以消除独立向量操作之间的屏障。

当前行为: 任何读取 dirty 缓冲区的向量 op 都触发 PIPE_ALL

目标行为: per-buffer 追踪 dirty 状态。仅在以下情况插入屏障:

  • DMA load 写入缓冲区 B,后续向量 op 读取 B;
  • 向量 op 写入缓冲区 B,后续 DMA store 读取 B;
  • 跳过 Add(buf0, buf1, buf2)Mul(buf3, buf0, buf4) 之间的屏障(buf0 非 dirty)。

优先级 3:算子融合(关闭 ~10% 的差距)

将同一缓冲区上的连续向量 op 融合为复合操作:

  • Sub(buf, x, max)Exp(buf, buf) → 合为单次 Sub+Exp AscendC 调用;
  • Muls(buf, buf, scale)Adds(buf, buf, bias) → MulAdd 复合;
  • 消除融合 op 之间的中间屏障。

I.5 按类别性能汇总

按真实 ascendc-to-rs 批次类别缩放。每个类别都具有相同的二分,EQUIVALENT 比例在单向量 op 模式主导的类别中更高(尤其是 ops_transformer,因为 attention / MLP 内核倾向于复用一个缓冲区、不触发 DMA / compute 重叠路径)。

类别总计EquivalentSlow 1.02×Slow 1.2×Slow 1.5×Slow 2×+
ops_cv41437000
ops_legacy3430343000
ops_math15512143000
ops_nn3066300000
ops_oam303000
ops_transformer1509951000
总计998121877000

ops_transformer 类别的 EQUIVALENT 占比最高(66%),因为 transformer attention / MLP 内核倾向使用单向量 op 模式,不会触发 DMA / compute 流水线重叠 —— TBuf 对比 TQue 的差别因此影响较小。

I.6 逐内核详情

全量逐内核性能报告(所有 998 个真实批次内核)作为机器生成的配套文件维护:仓库中的 blog/appendix_perf_report.md。文件列出每个内核的性能分类(EQUIVALENT / SLOW_1.02X)以及其所适用的具体减速模式(S1_TBUF_NOT_TQUES2_PIPE_ALL_BARRIERS 等)。

I.7 PTO 路径:双缓冲已解决(2026-04-02)

上文三个 “HIGH” 减速模式(TBufPIPE_ALL、无双缓冲)仅适用于 mlir_to_cpp codegen 路径。PTO tile 路径(mlir_to_pto.rsptoas)同时解决了三者:

减速模式mlir_to_cpp 状态PTO tile 路径状态
TBuf 取代 TQue影响 998/998 内核不适用 —— PTO 使用 tile 缓冲区,非 TBuf/TQue
PIPE_ALL 屏障影响 998/998 内核已消除 —— ptoas 对 softmax 只插入 2 个细粒度 flag
无双缓冲影响 998/998 内核已解决 —— GEP 偏移修复启用了并发 tload 调度

tile_softmax_double_buf 示例在 Ascend 910B2 上达到 每 tile 1.62× 吞吐(0.0034 ms 对比 0.0055 ms 基线)。mlir_to_pto.rs 中的 GEP 偏移修复(commits bea12b779537834a)是并发调度得以工作的关键 —— 修复前,所有 partition_view op 都发射 offsets=[%c0,%c0],使两次 load 引用同一 tensor 行。见 §4.7 的结果表,以及 附录 J §J.4 的完整实现细节。