CANN PTO-ISA 混合精度 Flash Attention 性能 Kernel 实践指南(Ascend A5)
【免费下载链接】pto-isaParallel Tile Operation (PTO) is a virtual instruction set architecture designed by Ascend CANN, focusing on tile-level operations. This repository offers high-performance, cross-platform tile operations across Ascend platforms.项目地址: https://gitcode.com/cann/pto-isa
本文围绕 CANN PTO-ISA 开源仓库中的 A5 平台 Flash Attention(FA)手工 Kernel 示例展开,介绍其工程结构、构建与运行方式、性能指标定义,以及基于数值稳定 tiled softmax 的四阶段实现(compute_qk / compute_p / compute_pv / compute_gu)与 Cube/Vector 软件流水线编排,并结合源码说明 A5 特有的 UB 路径优化与调参手段。读完本文,你将掌握如何在 A5(Ascend910_9599)上构建、运行、调参与验证一个混合精度的 Flash Attention 性能 Kernel。
概览与应用场景
本示例演示如何使用 PTO(Parallel Tile Operation,Ascend CANN 设计的一种面向 tile 级操作的虚拟指令集架构)在 Ascend A5 平台上实现混合精度的 Flash Attention(FA)算子。所谓混合精度,指的是输入 Q/K/V 以 FP16 存放,而中间结果(QK 累加、Softmax 归一化、PV 累加)以 FP32 计算,最终输出 O 可回落到 FP16/FP32,兼顾精度与吞吐。
该 Kernel 位于 kernels/manual/a5/flash_atten/,是仓库中 kernels/manual/common/flash_atten/ 通用版 Flash Attention 示例的 A5 专用版本。通用版本文档描述了 FA 的数学原理、分块计算策略与流水线编排(支持 A2/A3),而 A5 版本在其基础上针对 A5 架构的内存层次、Cube/Vector 同步机制与流水线深度进行了适配与优化。
本文涉及的全部源码与配置均可从当前仓库对应路径查看,适合从事算子开发、性能优化与内核研究的开发者阅读。
目录结构与工程组成
A5 版本示例的工程结构如下(与仓库 kernels/manual/a5/flash_atten/ 一致):
kernels/manual/a5/flash_atten/ ├── scripts/ │ ├── gen_data.py # 生成输入与 golden 输出 │ ├── generate_cases.py # 生成测试用例(host/kernel 共用的 case 配置头与 JSON) │ ├── pipeline_log_analysis.py # 流水线日志分析 │ ├── pipeline_schedule_gen.py # 流水线调度生成 │ ├── run_timeline.sh # 时间线分析脚本 │ └── validate_buffer_usage.py # 校验所有生成 case 的 buffer 用量 ├── CMakeLists.txt # 构建配置 ├── fa_performance_kernel.cpp # Kernel 实现(compute_qk/compute_p/compute_pv/compute_gu) ├── fa_performance_kernel.h # Kernel 头文件(模板参数与默认调优常量) ├── main.cpp # Host 侧入口(case 分发、数据搬运、校验与性能统计) ├── pto_macro_fa_gu.hpp # FA GU(归约/归一化)宏定义 ├── pto_macro_fa_softmax.hpp # FA Softmax(TSOFTMAXFA)宏定义 ├── pto_macro_matmul.hpp # Matmul 宏定义 └── run.sh # 便捷脚本(一键生成 case、构建、运行)与通用版(A2/A3)相比,A5 版本额外提供了 DN 变体:目录中还包含fa_performance_dn_kernel.cpp、pto_macro_dn_matmul.hpp、pto_macro_fa_dn_gu.hpp、pto_macro_fa_dn_softmax.hpp,通过run.sh --mode_dn启用(详见下文“DN 模式”说明)。
Host 侧入口
main.cpp 是一个独立驱动(不依赖 gtest),核心流程为:
- 读取
generate_cases.py生成的generated_cases.h,通过TFA_FOR_EACH_CASE宏实例化所有模板 case(run_case<float, S0, HEAD, S1, CUBE_S0, CUBE_S1, TILE_S1, QK_PRELOAD, CAUSAL_MASK>); - 根据
--case/--cases/--npu/--intermediate/--sys_cnt_multiple等命令行参数过滤与选择要运行的 case; - 为每个 case 分配 device 侧 FIFO 缓冲(qk_tile_fifo、p_tile_fifo、pv_tile_fifo、exp_max_ififo、o_out、o_parts 等),并写出
device_addrs.toml供时间线分析脚本使用; - 调用模板化的
LaunchTFA<...>启动 Kernel,将中间 FIFO 数据拷回 host,与 golden 文件比对; - 基于 profiling 数据统计
Total task time (us)与TFLOPS,追加写入report.csv,最终打印test success或test failed。
Kernel 侧入口
fa_performance_kernel.h 定义了 Kernel 的模板参数与默认常量:
constexpr int kFaCvFifoSize = 8; // Cube/Vector 间 FIFO 深度 constexpr int kFaCvFifoConsSyncPeriod = 4; // 消费同步周期(FIFO 深度的一半) constexpr int kFaCubeS1 = 128; // Cube matmul 在 S1 维的 tile 尺寸 constexpr int kFaTileS1 = 256; // 逻辑 S1 tile 尺寸 constexpr int kFaQkPreload = 4; // QK 预取(preload)深度 constexpr int VEC_CORES = 2; // Vector 子块数(S0 行在 vector subblock 间切分)这些常量会在后文“调参点”与“流水线编排”中反复出现,是理解 Kernel 行为的钥匙。
构建与运行
环境准备
运行前需要先配置 Ascend CANN 环境(示例路径,请替换为实际安装路径):
source ${ASCEND_INSTALL_PATH}/bin/setenv.bash构建时 CMake 会校验ASCEND_HOME_PATH环境变量(CMakeLists.txt 中通过message(FATAL_ERROR ...)强制要求),因此务必先执行环境变量脚本。编译器使用 bisheng,Kernel 侧通过--cce-pto-enable开启 PTO 支持,并指定 A5 架构--cce-aicore-arch=dav-c310。
一键运行(run.sh)
进入示例目录后即可运行:
cd ${git_clone_path}/kernels/manual/a5/flash_atten # 运行默认 case(与 generated_cases.* 中内置集合一致) bash run.sh -r npu -v Ascend910_9599 # 从内置集合中只运行一个 case bash run.sh -r npu -v Ascend910_9599 -c case_float_H_128_S0_128_S1_1024 # 提供自定义 case(用分号分隔:HEAD_SIZE,S0,S1,CUBE_S0,TILE_S1) # TILE_S1:支持 128(=CUBE_S1)、256、512 bash run.sh -r npu -v Ascend910_9599 --cases "128,128,1024,128,128;128,2048,2048,128,512" # 提供自定义 case,并只运行其中一个 bash run.sh -r npu -v Ascend910_9599 --cases "128,128,1024,128,128;128,512,2048,128,128" \ -c case_float_H_128_S0_128_S1_1024成功时输出:
test success注意:该目录仅支持 A5 平台。run.sh会校验 SoC 版本必须匹配Ascend910_9599前缀,否则直接报错退出:
pattern="^Ascend910_9599" if [[ ! "$SOC_VERSION" =~ $pattern ]]; then echo "[ERROR] Unsupported SocVersion: ${SOC_VERSION}, this folder only support A5." exit 1 firun.sh 完整参数说明
通过阅读 run.sh 的 getopt 解析逻辑,可以整理出以下完整参数表:
| 短选项 | 长选项 | 取值 | 说明 |
|---|---|---|---|
-r | --run-mode | npu/sim等 | 运行模式;npu为板上运行,链接runtime;sim为仿真,链接runtime_camodel(见 CMakeLists.txt 的target_link_libraries分支) |
-v | --soc-version | Ascend910_9599 | SoC 版本,仅支持 A5 |
-n | --npu | 设备号 | NPU 设备 ID,默认0 |
-c | --case | case 名或数值元组 | 只运行指定 case,如case_float_H_128_S0_128_S1_1024 |
-a | --cases | 分号分隔的元组列表 | 自定义 case 集合,格式HEAD_SIZE,S0,S1,CUBE_S0[,TILE_S1] |
-p | --qk-preload | 整数 | QK 预取深度,默认2(对应 Kernel 侧默认QK_PRELOAD=4的生成脚本默认值不同,生成脚本默认4,run.sh未传时用2,注意区分) |
-m | --mode | 0/1/2 | FIFO 模式:0=ALL_GM_PATH、1=ALL_UB_PATH、2=QK_PV_UB_ONLY,默认1(见下文“A5 平台特定优化”) |
-i | --intermediate | - | 开启中间结果(FIFO)校验 |
-d | --debug | - | 调试构建(-DDEBUG_MODE=ON,并追加--cce-enable-print编译选项) |
-k | --mask | - | 开启 causal mask(--causal-mask 1) |
| - | --mode_dn | - | 启用 DN 变体(编译并运行fa_performance_dn) |
run.sh的内部流水为:generate_cases.py生成 case →validate_buffer_usage.py校验 buffer 用量 → CMake 配置(含-DFIFO_MODE、-DDEBUG_MODE、-DMODE_DN等编译宏)→make→gen_data.py生成输入与 golden → 运行./fa_performance(或./fa_performance_dn)。运行参数--sys_cnt_multiple=1.0用于将硬件 cycle 计数换算为时间。
自定义 case 的约束规则
generate_cases.py 对 case 做了严格的合法性归一化,自定义 case 必须满足:
- 元组格式为
HEAD_SIZE,S0,S1,CUBE_S0[,TILE_S1],4 或 5 个逗号分隔值; CUBE_S1固定为128,且S1必须能被CUBE_S1整除;TILE_S1必须是CUBE_S1的整数倍,且S1必须能被TILE_S1整除(默认TILE_S1=128,即每 tile 一个 cube 块);CUBE_S0不能超过S0且必须整除S0,否则自动回退为CUBE_S0 = S0;qk_preload >= 1。
默认内置 case 集合为:
| HEAD_SIZE | S0 | S1 | CUBE_S0 | TILE_S1 |
|---|---|---|---|---|
| 128 | 128 | 1024 | 128 | 128 |
| 128 | 128 | 2048 | 128 | 128 |
| 128 | 128 | 8192 | 128 | 128 |
| 128 | 512 | 1024 | 128 | 128 |
| 128 | 512 | 2048 | 128 | 128 |
| 128 | 512 | 8192 | 128 | 128 |
case 命名规则为case_float_H_{HEAD}_S0_{S0}_S1_{S1},例如case_float_H_128_S0_128_S1_1024。
性能指标定义与参考数据
指标定义
本目录性能统计沿用了通用版本文档的定义:
S0:query 序列长度(Q/O 的行数);S1:key/value 序列长度(K/V 的行数);Total task time (us):每个 task 的端到端 kernel 时间(微秒);GOps:该 task 计数的总运算量。Host 侧计算式为S0 × S1 × HEAD_SIZE × 4 / 1e6(见 main.cpp 中gops的计算),对应 QK 与 PV 两次 matmul 的乘加操作;TFLOPS:GOps / time。
时间数据来源于 Kernel 的 profiling 缓冲:kFaProfileBytesPerBlock = 1024 * 3(cube + 两个 vec subblock),每个 block 取 cube 与 vec 的时间戳首尾包络作为该 block 的起止时间,最终以所有 block 的时间包络计算总时长。
A5 实测性能(参考)
以下数据在 Ascend A5 上测得,表格中的 TBD 表示待测量数据(当前仓库尚未填充):
| Cores | S0 | S1 | Total task time (us) | GOps | TFLOPS |
|---|---|---|---|---|---|
| 1 | 128 | 1024 | TBD | 67.11 | TBD |
| 1 | 128 | 2048 | TBD | 134.22 | TBD |
| 1 | 128 | 4096 | TBD | 268.44 | TBD |
作为参考,通用版(A2/A3)kernels/manual/common/flash_atten/README_zh.md 给出了完整的实测数据,其中 1 核、S0=128 时各 S1 的 TFLOPS 约为 1.59(S1=1024)到 7.20(S1=8192),归一化吞吐随 S1 增大而明显提升。该文档还指出:仿真(simulation)数值可能显著高于板上实测,因为模拟器不会建模所有硬件争用/时延特性,性能决策请以板上数据为准。A5 上的具体性能表现以本目录后续更新的实测为准。
算子实现原理
1. 计算流程(FlashAttention 2.0)
令 Q ∈ ℝ^{S0×H}、K ∈ ℝ^{H×S1}、V ∈ ℝ^{S1×H},其中 H 为HEAD_SIZE。单头 attention 的标准形式(省略 softmax 常数项)为:
$$\text{QK} = Q K^\top \in \mathbb{R}^{S0\times S1}$$ $$P = \operatorname{softmax}!\left(\frac{\text{QK}}{\tau}\right)\in \mathbb{R}^{S0\times S1}$$ $$O = P,V \in \mathbb{R}^{S0\times H}$$
为降低显存占用并提升访存效率,QK 与 softmax 按 (S0, S1) 分块(tile)流式计算,并在遍历 S1-tiles 的过程中持续更新输出 O 的 running sum。缩放系数 $s = 1/\sqrt{\mathrm{HEAD_SIZE}}$。
数值稳定的 tiled softmax(按 S1 分块)
对每一行 i,处理当前 tile 时做如下递推(与常见数值稳定 softmax 写法等价):
- 步骤 1(local row max):$m_i = \max_j X_{ij}$,对应
local_max; - 步骤 2(updated global max):$M_i = \max(M_{\mathrm{prev},i}, m_i)$,对应
new_global_max; - 步骤 3(rescaling factor):$\mathrm{exp_max}i = \exp(s \cdot (M{\mathrm{prev},i} - M_i))$,对应
l1_exp_max,当全局 max 增大时用于重标定历史累加项; - 步骤 4(per-element exponentials):$e_{ij} = \exp(s \cdot (X_{ij} - M_i))$,对应
p_tile_fp32/x_exp(x_exp会 cast 为 fp16 供后续 matmul); - 步骤 5(local sum):$\ell_i = \sum_j e_{ij}$,对应
local_sum; - 步骤 6(updated global sum):$S_i = \mathrm{exp_max}i \cdot S{\mathrm{prev},i} + \ell_i$,对应
l2_global_sum。
处理完所有 tiles 后得到最终 softmax 概率 $p_{ij} = e_{ij} / S_i$。Kernel 会保存x_exp供compute_pv使用,同时保留l1_exp_max与l2_global_sum供compute_gu做 running 累加与最终归一化。
2. 张量形状(按阶段)
- 输入:
- Q:
S0 × HEAD_SIZE(fp16) - K:
S1 × HEAD_SIZE(fp16) - V:
S1 × HEAD_SIZE(fp16)
- Q:
- 每个 S1 tile 的中间量(tile t):
qk_tile:S0 × CUBE_S1(fp32 累加),例如64×128/128×128p_tile(x_exp):S0 × CUBE_S1(fp16,用于 matmul)pv_tile:S0 × HEAD_SIZE(fp32),每个 tile 的部分结果
- 输出:
- O:
S0 × HEAD_SIZE(fp16/fp32)
- O:
3. 分阶段实现与调参
Kernel 核心代码分为四个阶段(在 fa_performance_kernel.cpp 中实现,源码注释将其概括为跨核流水线:compute_qk (Cube) → qk_tile_fifo → compute_p (Vec) → p_tile_fifo → compute_pv (Cube) → pv_tile_fifo → compute_gu (Vec) → o_out)。
compute_qk(Cube matmul)
- 作用:计算单个 S1 tile 的 Q·K_t(cube pipeline)。
- 实现要点:Q tile 做 leftTile 驻留——当
tile_idx == 0时加载一次 Q,后续 tiles 只加载 K,减少从 GM 的重复读取;qk 部分结果写入紧凑的 ping/pong 全局缓冲;复用matmul_macro_pto(matTile → accTile),并维护 left/right tiles 的 ping/pong 状态。 - 调参点:
assign_running_acc_tile让输出 accTile 在compute_qk与compute_pv之间双缓冲;qkPreloadNum同时决定qkp_tile_fifo_size = 1 + qkPreloadNum,用于 cube 生产者与 vector softmax 消费者之间的 FIFO 深度。
compute_p(Vector softmax:TSOFTMAXFA)
- 作用:在 S1 维度按 tile 增量计算并保持数值稳定的 tiled softmax。
- 实现要点:
- Vector tiling:
Vec_S0 = S0 / VEC_CORES,每个 vector subblock 处理Vec_S0 × CUBE_S1;VEC_CORES控制 S0 行在 vector subblock 间的切分(A5 版本默认VEC_CORES = 2); - 每个 vector core 用
get_subblockid()计算全局张量 load/store 的 tile 索引与 qk/p/pv/o buffer 偏移,自然形成 SPMD 并行; TSOFTMAXFA微内核(定义于 pto_macro_fa_softmax.hpp)负责 softmax 递推:保存每个 tile 的l1_exp_max与l2_global_sum,供compute_gu做 running 累加并在最后一步计算最终 O。
- Vector tiling:
- 实现取舍:
- 优先使用固定 tile 尺寸的
TROWMAX/TROWSUM(128/256/512/1024 reduce 轴上的实现通常更高效); - 对动态有效行/列可先做
TFILLPAD(PAD_MIN/-INF)把动态 mask 转成静态(例如处理动态 S0); TROWEXPANDSUB支持原地计算(dst==src),可减少临时 buffer。
- 优先使用固定 tile 尺寸的
- UB 分配(
allocate_vec_tile_buffers):为compute_p/compute_gu的 per-vector tiles 预先规划 UB 偏移,让 vector cores 复用一小组固定 UB 地址。常用参数为SrcBuffers、XexpBuffers、pvVecBuffers、ExpMaxBuffers(ExpMaxBuffers通常等于qkp_tile_fifo_size)。典型分配顺序:qkvec tiles →m1_local_max→m2_global_max→input_reduce_tmp→l1_local_sum→l2_global_sum→l1_exp_max[]→x_exp[]→runningOTile。
compute_pv(P·V matmul)
- 作用:把每个 tile 的 P(softmax 输出)与对应的 V tile 相乘,得到 PV 的部分累加(cube matmul 风格)。
- 实现要点:加载 V tile 与 P tile,并把
pv_tile_fifo写入全局 float buffer 的 per-tile ping/pong 缓冲。 - 调参点:
pv_tile_fifo_size(通常为1 + qkPreloadNum)控制 P 生产与 GU 消费之间的 FIFO 深度。
compute_gu(归约 / 归一化)
- 作用:消费
pv_tile_fifo并累加到runningOTile;最后一个 tile 触发对l2_global_sum的最终除法得到输出 O。 - 实现要点:vector core 驱动,使用
TGU_ND/TGU_LAST_ND宏(定义于 pto_macro_fa_gu.hpp)做 per-tile 累加。 - 实现取舍:保持
runningOTile绑定(assigned)避免重复分配;TROWEXPANDMUL/TROWEXPANDDIV支持原地计算(dst==src),可减少临时 buffer。
4. 流水线编排(Cube/Vector 并行)
跨阶段通过 CV FIFO + 阶段内 ping/pong 做软件流水化。S1 tiles 循环中的典型流程:
- cube:
compute_qk预加载下一批 QK tile,并通过 flag 通知 vector; - vector:
compute_p等待 qk 就绪,在该 chunk 上运行TSOFTMAXFA产出 p tile,并通知 pv 消费者; - cube:
compute_pv消费 p 与 v,生成pv_tile_fifo,写回全局并通知 GU 消费者; - vector:
compute_gu消费pv_tile_fifo并累加到runningOTile。
阶段内关键机制:
matmul_macro_pto+assign_running_acc_tile:leftTile/rightTile/AccTile 的双缓冲,使 cube core 能在 preload 序列里交错compute_qk与compute_pv;compute_p的 qk 输入与 p 输出也做双缓冲;expT提供多 preload buffer,支持更晚的结果转发。
同步机制:A5 版本使用 FFTS(flag-based)同步。Kernel 源码中定义了FftsBufferFlag枚举(fa_performance_kernel.cpp):
enum FftsBufferFlag : uint32_t { BUF0_QK_READY = 0, // qk2smSync:QK 就绪 BUF1_SM_READY = 2, // sm2pvSync:Softmax 就绪 UPDATE_READY = 4, // pv2guSync:PV 就绪 UB_BUF_READY = 6, // ubBufSync:UB 缓冲就绪 PV_UB_BUF_READY = 8, // pvUbBufSync:PV UB 缓冲就绪 CV_BLOCK_END = 10, // CV comm slot block end };每个TSync对象使用两个连续 flag(前向 record/wait 与反向 allocate/free),因此 flag ID 间隔 2 以避免冲突;双核(two vec subblock)场景使用+16的偏移。消费同步通过should_wait_consumption/should_notify_consumption模板按周期(kFaCvFifoConsSyncPeriod = kFaCvFifoSize / 2)发信号,并在 Kernel 尾部用pending_consumption_events排空尚未等待的消费通知。
调参入口(knobs):
qkPreloadNum:允许 cube pipeline 预先产出更多 QK tiles;qkp_tile_fifo_size:qk/p FIFO 深度(通常为1 + qkPreloadNum);pv_tile_fifo_size:PV FIFO 深度(通常与 qk FIFO 深度匹配),用于与 GU 重叠;- 同步:优先用轻量 device flags;在 UB 允许时,尽量用更深的 FIFO/更大的 preload 拉开重叠。
以 Head=128、S0=128、S1=1024 为例,CUBE_S1=128 时共有 4 个 loop,每个 loop 执行compute_qk → compute_p → compute_pv → compute_gu。不做预执行(preload)时四段趋向串行;增大qkPreloadNum后 cube pipeline 可以“跑在前面”,把 vector 侧资源摊平,从而隐藏等待延迟。通用版本文档中还指出,从流水线图可以看到瓶颈更偏向 cube 侧的TSTORE(Cube 利用率约 30%),后续优化会优先围绕这一点展开。
5. 多核切分与负载均衡
- 多核 tiling:QKV 输入是 BNSD(Batch、Head 数、Seqlen、HEAD_SIZE)布局,计算过程中产生中间的 QK(S0,S1)。由于 S1 是归约轴,多核切分通常按 (B, N, S/Cube-S0) 分;在 Flash-decoding 场景中 (B, N, S/Cube-S0) 较小,未来可以考虑沿 S1 轴切分,每核保留部分 O,再通过另一个 kernel 做最终 GU(通用版本文档标记为 TODO)。
- 对很大的 S0(超过最大物理核数)时,中间 FIFO buffer 可能引入浪费与不必要的 L2 回写,可按 core id 做进一步优化(TODO)。
- 负载均衡:引入 causal attention mask 时需要考虑稀疏性(TODO);多核 tiling 也要关注沿 S0 轴的负载不均。当前做法是在大 S0 时采用 block 数大于物理核数的多 block 启动方式。
A5 平台特定优化
相比 A2/A3 平台,A5 版本的 Flash Attention kernel 针对 A5 架构特点进行了以下优化:
- 针对 A5 的内存层次结构调整了缓冲区分配策略;
- 优化了 Cube 和 Vector 核心之间的同步机制(使用前述 FFTS flag 同步体系);
- 调整了流水线深度参数以适配 A5 的硬件特性(
kFaCvFifoSize = 8、kFaQkPreload = 4、kFaTileS1 = 256等默认常量)。
FIFO 路径模式(FIFO_MODE)
A5 版本的核心优化之一是UB/L1 直传路径,通过编译宏FIFO_MODE选择数据在 Cube/Vector 之间传递的媒介(源码注释见 fa_performance_kernel.cpp 顶部,以及 CMakeLists.txt 的UB Path Mode Configuration):
- MODE 0(ALL_GM_PATH):所有数据路径走 Global Memory(baseline):
- QK:
L0C → GM → UB(TSTORE/TLOAD) - P:
UB → GM → L1(TSTORE/TLOAD) - PV:
L0C → GM → UB(TSTORE/TLOAD)
- QK:
- MODE 1(ALL_UB_PATH):所有数据路径使用 UB/L1 直接传输,无 GM 往返:
- QK:
L0C → UB(TMOV) - P:
UB → L1(TMOV ND2NZ + TINSERT) - PV:
L0C → UB(TMOV)
- QK:
- MODE 2(QK_PV_UB_ONLY):QK 与 PV 走 UB 路径,P 路径回退 GM:
- QK:
L0C → UB(TMOV) - P:
UB → GM → L1(TSTORE/TLOAD,无 TMOV+TINSERT) - PV:
L0C → UB(TMOV)
- QK:
通过run.sh -m(或-DFIFO_MODE=)切换,run.sh默认1,Kernel 源码默认2。源码还对模式做了合法性校验:FIFO_MODE必须为 0/1/2;且 MODE 1/2 要求UF_ENABLE = 0(#error拦截非法组合)。三种模式体现了“GM 兜底 → 全 UB 直传 → 部分 UB 直传”的递进优化思路:UB/L1 直传省去 GM 往返的 TSTORE/TLOAD,可显著降低 FIFO 搬运开销,但对 buffer 规划与同步精度的要求更高。
DN 模式
run.sh --mode_dn编译并运行fa_performance_dn变体(DN 布局),对应源码为 fa_performance_dn_kernel.cpp 及pto_macro_dn_matmul.hpp、pto_macro_fa_dn_gu.hpp、pto_macro_fa_dn_softmax.hpp。DN 与 ND 的主要差异在 tile 的数据排布:从 main.cpp 的中间结果校验逻辑可见,DN 构建将 qk/p subtile 按(Cube_S1 × Cube_S0)排布,ND 构建按(Cube_S0 × Cube_S1)排布(#ifdef MODE_DN分支)。此外,DN 变体通过--causal-mask支持 causal attention mask 的中间校验跳过逻辑(skip_for_causal_mask)。
Causal Mask
run.sh -k(--causal-mask)开启 causal mask 支持,mask 状态会随 case 配置写入generated_cases.h/JSON,并作为模板参数CAUSAL_MASK参与 Kernel 实例化与 host 校验。中间校验中,当s1_index > s0_index的 tile 会被跳过(causal 下该区域被 mask 掉)。
调试与验证手段
中间结果校验(--intermediate)
默认运行只做最终 O 的 golden 比对(容差 1e-3)。加上-i(--intermediate)后,host 会额外比对各阶段的 FIFO 中间数据:
qk_fifo、p_fifo(x_exp,fp16)、p_max_fifo(exp_max)、pv_fifo:按 block 逐 tile 与 golden 比对,容差 exp_max 为 1e-2、其余为 1e-3;global_sum_part{t}_out.bin、exp_max_part{t}_out.bin:逐 tile 的 per-tile 全局和与 exp_max 快照;o_part{t}_out.bin:每迭代的 running 输出快照。
校验结果以[CHECK] block b qk=OK/FAIL p=... p_max=... pv=...形式输出,最终汇总为[CHECK] FIFO intermediate ok/failed,并可通过--sys_cnt_multiple调整时间换算系数(默认 A2/A3 为 20.0,run.sh传入1.0)。
时间线分析
目录提供流水线时间线分析工具链:
- scripts/pipeline_log_analysis.py:解析 device 侧指令日志(
instr_popped_log.dump/instr_log.dump)与 device_addrs.toml(main.cpp 运行时生成),输出timeline.csv/timeline.json/timeline_agg.csv/timeline.svg; - scripts/pipeline_schedule_gen.py:生成流水线调度示意;
- scripts/run_timeline.sh:一键执行上述分析,需要先以 debug 模式构建产生指令日志。
Buffer 用量校验
scripts/validate_buffer_usage.py 在构建前对generated_cases.json中所有 case 的 UB/L1 buffer 用量做静态校验(--mode dn/--mode nd),确保配置在硬件容量约束内,避免运行期溢出。
参考与延伸
- 通用版 Flash Attention 文档(A2/A3,含完整的数学推导、张量形状、分阶段实现与实测性能表):kernels/manual/common/flash_atten/README_zh.md
- Kernel 实现与调优注释:fa_performance_kernel.cpp
- Host 驱动与校验逻辑:main.cpp
- 一键构建运行脚本:run.sh
- case 生成与约束规则:scripts/generate_cases.py
- 时间线分析脚本:scripts/run_timeline.sh
PTO 虚拟指令集的整体介绍可参考 docs/PTOISA_zh.md,指令集手册见 docs/PTO-Virtual-ISA-Manual_zh.md。A5 平台的调优参数与性能数据将在后续版本中持续更新,建议以当前仓库实际内容为准。
【免费下载链接】pto-isaParallel Tile Operation (PTO) is a virtual instruction set architecture designed by Ascend CANN, focusing on tile-level operations. This repository offers high-performance, cross-platform tile operations across Ascend platforms.项目地址: https://gitcode.com/cann/pto-isa
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考