Closed
flash_attn DN不合轴模板 s2BaseSize 切换为 256 #7137
jiang-liruiAdd on  8 days agoClosed at  8 days ago
jiang-liruiCollaborators
8 days ago Create

flash_attn DN不合轴模板 s2BaseSize 切换为 256

  • 分支:flash_attn_s2base256(master e5969b59d + 单提交 2d2de8ca5,7 文件 +182/−67)
  • 结论:template 2(不合轴 DN)全档统一 S2=256,无 maxSeqlenKv 门控;相对 S2=128 基线,
    223 条语料平均加速 1.0700、总耗时 1.1131,D=64 最高 1.1298;唯一回归桶是
    「mask=3 且 S2≈256」的单块场景(21 条,总耗时 0.957)。

1. 为什么要从 128 切到 256

template 2 的路由是 vHeadDim<=128 && maskMode∈{0,3} && maxSeqlenQ>=256,也就是 Q 至少 256 行。
此时 KV 方向若仍按 128 切块:

  1. 块数翻倍:单个 Q 块要串行跑 2 倍数量的 S2 迭代,cube 的 mmad 流水启动/排空开销被重复摊薄;
  2. mmRes 槽不够:S2=128 时 UB 只能放 1~2 个分数槽,bmm2 无法独立成槽,vec 与 cube 的重叠受限;
  3. D=64 最敏感:N=64 时单次 mmad 的 N 维本来就小,再叠加 S2=128,有效吞吐明显下滑(实测 D=64 收益最大)。

放大到 256 后,S2 迭代数减半、分数槽按 2 个配置、D=64 还能额外开 bmm2 独立槽,流水重叠明显变好。

2. 方案总览

层 改动
档位 template 2 专用 config 10–13(S2=Aligned256),与通用档 0–9 分离;上游 D 泛化占用 8/9,故由原 8/9/10 重编号
host CalcFaKernelConfigArch35::CalcBaseSize:FA_Template_DN_Unmerged → sInner=256;UpdateTilingKeyConfig 按真实 D 映射 10–13
kernel *_dn_unmerged 的 cube/vec block 用 ConfigValue 的 count 常量推导 UB 槽数与同步 id(不再用 dBaseSize>128?2:4 之类的硬编码),并支持 MM2 独立槽
AICPU template 2 的 s2BaseSize 直接取 CalcConfig 的 sInner,删除原来的二次覆盖
UT Route_Q256_*_DnUnmerged 两条期望 tilingKey 同步到 config 11(D=128 档)

3. 档位表(config 10–13)

ConfigParams 字段:s1 / s2 / d / dv / originalDV / ubMm1ResBufcnt / ubMm2ResBufcnt / ubVec1ResBufcnt / ubMaskBufcnt / l1PBufcnt / l1QBufcnt / l1KvBufcnt。

config 真实 D 模板 D 模板 DV originalDV sOuter sInner UB counts(mm1/mm2/vec1/mask/l1P/l1Q/l1Kv)
10 64 64 64 64 64 256 2 / 1 / 2 / 0 / 3 / 2 / 4
11 128 128 128 128 64 256 2 / 0 / 2 / 0 / 3 / 2 / 4
12 192(DV=128) 192 128 128 64 256 2 / 0 / 2 / 0 / 3 / 2 / 4
13 72 128 128 72 64 256 2 / 0 / 2 / 0 / 3 / 2 / 4

要点:

  • originalDV 保留真实 V 宽度(D=72 为 72),供输出拷贝选 ROW_PAD;D=72 不再复用 D=128 的 originalDV;
  • ubMask=0:不合轴 DN 用解析式 mask,不拷 mask 表;
  • ubMm2=1 只在 D=64 开(2 个分数槽 + 1 个 bmm2 槽装得下),其余档 bmm2 复用分数槽;
  • ConfigValueArch92 同步补 10–13 占位(arch92 不下发,仅为共享 SEL 实例化)。

4. 各层实现

4.1 host 与 AICPU

// arch35/flash_attn_kernel_config_arch35.h
} else if (config.templateId == FA_Template_DN_Unmerged) {
    config.sOuter = 64;
    config.sInner = 256;          // 全档统一
}
// arch35/flash_attn_tiling.cpp: UpdateTilingKeyConfig
if (kernelConfig_.templateId == FA_Template_DN_Unmerged) {
    if (qkHeadDim == 64)                       config = 10;
    else if (qkHeadDim == 72)                  config = 13;   // originalDV=72
    else if (qkHeadDim == 128)                 config = 11;
    else if (qkHeadDim == 192 && vHeadDim==128) config = 12;
    return;
}

AICPU 侧不需要特判:mBaseSize_/s2BaseSize_ 来自 CalcConfig,template 2 自然拿到 sInner=256。

4.2 kernel(cube/vec block)

  • flash_attn_block_cube_dn_unmerged.h:UB_MM1_RES_BUFCNT = FA_T::UB_MM1_RES_BUFCNT、
    UB_MM2_RES_BUFCNT = FA_T::UB_MM2_RES_BUFCNT、MM_RES_SEPARATE_BMM2 = (UB_MM2 != 0);
    CROSSCORE_L1P_0 = CROSSCORE_MM_0 + UB_MM_RES_FLAGCNT(同类 buffer 的 id 连续),
    InitBuffers 的 UB 大小按 count 累加;跨核 wait/set 按 UB_MM_RES_FLAGCNT > 2/3 分级。
  • flash_attn_block_vec_dn_unmerged.h:CC_L1P_0 = CC_MM_0 + UB_MM_RES_FLAGCNT;
    UB_VEC1_RES_BUFCNT = FA_T::UB_VEC1_RES_BUFCNT(MTE3(P→L1) 暂存桶深度,S2=256 时为 2);
    vec2 / vec1Res / LSE 的核内同步 id 按"同类连续"推导。
  • 这样 S2=256 与 128 的差异全部由 ConfigValue 的 count 表达,block 代码不再出现档位特判。

4.3 tiling key

flash_attn_template_tiling_key.h 中 template 2 的 Config SEL 由 0..9 改为 10, 11, 12, 13;
template 0/1 仍用 0..9(8/9 为 D=72 档)。因此 template 2 的 kernel 实例数由 10 档降为 4 档
(× 4 layout × 2 mask = 32 个 key)。

5. 收益(223 条语料,S2=256 vs S2=128 基线)

口径:同一台 Ascend950PR,hot、5 次取 min;perf/analysis/speedup_T1_s256_vs_T0_s128.csv。

分组 n 平均加速比 中位 总耗时
全部 223 1.0700 1.0634 1.1131
D=64 85 1.1298 1.1681 1.1862(最高 1.2894)
D=128 94 1.0427 1.0525 1.0989
D=72 44 1.0126 1.0150 1.0399
S1=8192 92 1.1041 1.1032 1.1138
S1=256 120 1.0512 1.0525 1.0880
S2=131072 40 1.1040 1.1005 1.1115
S2=32768 40 1.1095 1.1074 1.1236
S2=8192 72 1.0810 1.0511 1.1012
mask=0 125 1.0804 1.0655 1.1005
mask=3 98 1.0567 1.0533 1.1345
BNSD / BSND 106 / 117 1.0807 / 1.0602 1.0721 / 1.0527 1.1230 / 1.1052

6. 已知回归与风险

  1. 「mask=3 且 S2≈256」的单块场景:21 条,平均 0.970、总耗时 0.957、最差 0.8496。
    原因:整个 KV 恰好是一个 256 块,S2 放大后没有第二个块与之重叠,反而损失了原来
    "128 块 × 2" 的跨块流水。可选补救:单块时把 s2 循环拆成两段(未做)。
  2. D=72 的 padding 浪费:模板按 128 列计算、真实 72,MFU 分析(10_mfu_report.md)显示
    D=72 组 MFU 中位 47%、最高 56%,提升空间在窄模板而非 S2 策略。
  3. 尾块:S2 不整除 256 时最后一个块利用率低(如 S2=224/127)。DN_MASK_* 一类的 S2 微调策略
    在参考分支 perf/dn-plus-noncoaxis 里出现过(causal 用 208/224),本分支未启用。
  4. UB 压力:S2=256 让每个分数槽 64KB(m=64),D=128 档已无 bmm2 独立槽;后续再加 buffer 需要先算预算。
  5. 行为面:template 2 不再复用 config 0..9,UT/工具里任何写死旧 config 的地方都要同步
    (本分支已修 2 条 UT)。

7. 验证清单(本分支实测)

项 结果
编译 128 key(template 0: 0–9;template 1: 0/2/6/8;template 2: 10–13)0 error
精度 _vf_p_layout 30/32、_vf_tail_edge 20/21、_s224_prec 10/10、functional 5/5(与重构前 s2 分支逐条一致;2 条 bf16 为既有长尾)
性能(对齐) 相对原 s2=256 分支 run_T2_split_refactor:平均 0.9988(即重编号/重构未改变行为)
UT transformer_op_host_ut 374/374 通过(含 Route_Q256 两条)

复现:perf/README.md 的 run_perf_raw.py + perf/analysis/ab_min.py;UT 用
bash build.sh -u --ophost_test --noexec --ops=flash_attn -j16 --soc=ascend950 后运行
build/tests/ut/framework_normal/op_host/transformer_op_host_ut(需 BUILD_PATH=<build 目录>)。

8. 后续可选项

  1. mask=3 单块拆分:S2 总量 ≤ 256 时把 s2 循环拆两段,恢复跨块重叠,目标救回 0.8496 的最差项;
  2. D=72 窄模板:按真实 72 列(或 80 对齐)建模板,消除 44% 列 padding;
  3. VEC2 改 DN 计算:参考 perf/dn-plus-noncoaxis,见 09_vec2_dn_feasibility.md;
  4. causal S2 微调:参考分支的 208/224 档,与单块拆分二选一或叠加,需单变量拆测。

修订记录

  • 2026-11-05:新建终版方案。汇总 s2=256 的动机、config 10–13 档位表、host/kernel/AICPU/UT 各层改动、
    223 条收益数据(1.0700/1.1131)、回归桶与风险、验证清单及后续可选项;历史推导引用 02/06 号文档。
likedislike
Jjiang-liruiCollaborators
8 days ago Modify description of issue
jiang-liruiCollaborators
8 days ago Comments:

/assign @jiang-lirui

likedislike
CANN-robotCANN-robotCollaborators
8 days ago Assign jiang-lirui as responsible person
周云飞
周云飞Collaborators
8 days ago Comments:

问题说明

统一 S2=256 属于性能取舍,现有数据支持整体收益,但不能说明所有场景都更快。按报告,Ascend950PR 上 223 条语料平均加速比为 1.0700;mask=3、KV 长度约 256 的单块场景总耗时加速比为 0.957。因此,不设 maxSeqlenKv 门控意味着接受这部分回退。单块缺少跨块重叠的解释仍需结合流水实现验证。

处理建议

可以考虑以 template 2 专用 config 10–13 承载该策略,保持 host、AICPU 和 kernel 的 S2、槽数及同步编号一致,并保留 D=72 的真实 originalDV,避免影响输出拷贝。是否全档切换,建议结合单块回退的可接受程度决定;单块拆分可作为独立优化评估,同时关注尾块精度和 UB 容量约束。

likedislike
CANN-robotCANN-robotCollaborators
8 days ago Close issue
CANN-robotCANN-robotCollaborators
8 days ago Added label: resolved
周云飞
周云飞Collaborators
3 days ago Comments:

已合入 PR !15177,对 Flash Attention DN 不合轴模板进行了性能优化:

  • 将 s2BaseSize 从 128 调整为 256,并按 QK、V 的头维度选择对应的 tiling 配置。
  • 调整 BMM1 的分块计算,以及 BMM1/BMM2 结果缓冲区和核间同步逻辑,以适配新的基本块大小。

关联 PR:PR #15177

likedislike