已合并
增加 MsaIndexScore 算子 950 实现 #10672
oscillated创建于 8月26日
增加 MsaIndexScore 算子 950 实现 #10672
已合并
共 34 个文件变更+3839-288
| @@ -4,9 +4,9 @@ | |||
| 4 | 4 | ||
| 5 | | 产品 | 是否支持 | | 5 | | 产品 | 是否支持 | |
| 6 | | --------------------------------------------------------- | :------: | | 6 | | --------------------------------------------------------- | :------: | |
| 7 | -| <term>Atlas A2 训练系列产品/Atlas 800I A2 推理产品</term> | √ | | 7 | +| <term>Atlas A2 训练系列产品/Atlas A2 推理系列产品</term> | √ | |
| 8 | | <term>Atlas A3 训练系列产品/Atlas A3 推理系列产品</term> | √ | | 8 | | <term>Atlas A3 训练系列产品/Atlas A3 推理系列产品</term> | √ | |
| 9 | -| <term>Ascend 950PR/Ascend 950DT</term> | × | | 9 | +| <term>Ascend 950PR/Ascend 950DT</term> | √ | |
| 10 | 10 | ||
| 11 | ## 功能说明 | 11 | ## 功能说明 |
| 12 | 12 | ||
| @@ -45,53 +45,190 @@ | |||
| 45 | > - D(Head Dim)表示单个注意力头维度 | 45 | > - D(Head Dim)表示单个注意力头维度 |
| 46 | > - PageAttention 场景下 $block\_num$ 为物理 block 总数、$block\_size$ 为每个 block 的 token 数,$maxBlockNumPerSeq$ 为每个 batch 最大逻辑 block 数(通常 $\ge\lceil S2/block\_size\rceil$),$M_b=\lceil S2/block\_size\rceil$ 为逻辑 block 总数 | 46 | > - PageAttention 场景下 $block\_num$ 为物理 block 总数、$block\_size$ 为每个 block 的 token 数,$maxBlockNumPerSeq$ 为每个 batch 最大逻辑 block 数(通常 $\ge\lceil S2/block\_size\rceil$),$M_b=\lceil S2/block\_size\rceil$ 为逻辑 block 总数 |
| 47 | 47 | ||
| 48 | -| 参数名 | 输入/输出/属性 | 描述 | 数据类型 | 数据格式 | | 48 | +<table style="undefined;table-layout: fixed; width: 1280px"><colgroup> |
| 49 | -| ------------------------ | -------------- | ------------------------------------------------------------ | ---------------------------- | -------- | | 49 | +<col style="width: 160px"> |
| 50 | -| query | 输入 | 公式中的 $Q_{idx}$。当前仅支持 TND,shape 为 $[T1, N1, D]$ | BFLOAT16, FLOAT16 | ND | | 50 | +<col style="width: 120px"> |
| 51 | -| key | 输入 | 公式中的 $K_{idx}$。支持 TND($[T2, N2, D]$)、BNBD($[block\_num, N2, block\_size, D]$)、BBND($[block\_num, block\_size, N2, D]$) | BFLOAT16, FLOAT16, INT8 | ND, NZ | | 51 | +<col style="width: 560px"> |
| 52 | -| block_table | 可选输入 | PageAttention 的逻辑 block → 物理 page 映射表。PA 场景必须传入,二维,第二维长度不能小于 $maxBlockNumPerSeq$;shape 为 $[B, S2/block\_size]$ | INT32 | ND | | 52 | +<col style="width: 320px"> |
| 53 | -| scale | 可选输入 | 公式中的 $scale$,反量化系数。非量化必须为空;量化场景必选。PA 为 $[block\_num, N2, block\_size]$ 或 $[block\_num, block\_size, N2]$;TND 为 $[T2, N2]$ | FLOAT | ND, NZ | | 53 | +<col style="width: 120px"> |
| 54 | -| atten_mask | 可选输入 | 控制因果可见的 mask。仅在 `sparse_mode=3` 时使用;取值为 1 表示该位不参与计算,为 0 表示参与计算;shape 为 $[2048, 2048]$ | INT8 | ND | | 54 | +</colgroup> |
| 55 | -| actual_seq_qlen | 可选输入 | 每个 Batch 中 Query 的有效 token 数。query 为 TND 时必须传入,单调不减(前缀和),shape 为 $[B+1]$ | INT32 | ND | | 55 | +<thead> |
| 56 | -| actual_seq_klen | 可选输入 | 每个 Batch 中 Key 的有效 token 数。key 为 TND 时必须传入(前缀和);PageAttention 场景下为各请求可见 $S2$,shape 为 $[B]$ | INT32 | ND | | 56 | + <tr> |
| 57 | -| start_loc | 输入 | 当前 query 所在逻辑 block 索引(非 token 前缀),用于生成 $local\_mask$;shape 为 $[B]$ | INT32 | ND | | 57 | + <th>参数名</th> |
| 58 | -| layout_key | 属性 | key 布局。`"TND"` / `"BBND"` / `"BNBD"`。aclnn 参数名为 `layoutKeyOptional`,不传时默认 `"BBND"` | STRING | - | | 58 | + <th>输入/输出/属性</th> |
| 59 | -| sparse_mode | 属性 | sparse 模式。0:defaultMask(`atten_mask` 传空);3:rightDownCausal(须传入 $[2048, 2048]$ 的 `atten_mask`) | INT64 | - | | 59 | + <th>描述</th> |
| 60 | -| init_blocks | 属性 | $local\_mask$ 强制选中的头部 block 数。对逻辑 block $[0, init\_blocks)$ 写入高分 $1\mathrm{e}30$。可选,默认 $0$ | INT64 | - | | 60 | + <th>数据类型</th> |
| 61 | -| local_blocks | 属性 | $local\_mask$ 强制选中的局部窗口长度。窗口为 $[max(0, start\_loc+1-local\_blocks), start\_loc]$,写入高分 $1\mathrm{e}29$(覆盖同位置的 `init_blocks`)。可选,默认 $1$(对齐 MiniMax HF);与 Triton raw score 对齐时置 $0$ | INT64 | - | | 61 | + <th>数据格式</th> |
| 62 | -| score | 输出 | 公式中的 $score$,逐 block 重要性分数;shape 为 $[N1, T1, RoundUp(maxBlockNumPerSeq, 16)]$ | FLOAT | ND | | 62 | + </tr> |
| 63 | +</thead> | ||
| 64 | +<tbody> | ||
| 65 | + <tr> | ||
| 66 | + <td>query</td> | ||
| 67 | + <td>输入</td> | ||
| 68 | + <td>公式中的 $Q_{idx}$。当前仅支持 TND,shape 为 $[T1, N1, D]$。</td> | ||
| 69 | + <td>BFLOAT16、FLOAT16、HIFLOAT8、FLOAT8_E5M2、FLOAT8_E4M3FN</td> | ||
| 70 | + <td>ND</td> | ||
| 71 | + </tr> | ||
| 72 | + <tr> | ||
| 73 | + <td>key</td> | ||
| 74 | + <td>输入</td> | ||
| 75 | + <td>公式中的 $K_{idx}$。支持 TND($[T2, N2, D]$)、BNBD($[block\_num, N2, block\_size, D]$)、BBND($[block\_num, block\_size, N2, D]$)。Ascend 950 上 PA(BBND/BNBD)允许首轴(物理 page)按 stride 非连续存放,page 内其余轴须连续。</td> | ||
| 76 | + <td>BFLOAT16、FLOAT16、INT8、HIFLOAT8、FLOAT8_E5M2、FLOAT8_E4M3FN</td> | ||
| 77 | + <td>ND</td> | ||
| 78 | + </tr> | ||
| 79 | + <tr> | ||
| 80 | + <td>block_table</td> | ||
| 81 | + <td>可选输入</td> | ||
| 82 | + <td>PageAttention 的逻辑 block → 物理 page 映射表。PA 场景必须传入,二维,第二维长度不能小于 $maxBlockNumPerSeq$;shape 为 $[B, S2/block\_size]$。</td> | ||
| 83 | + <td>INT32</td> | ||
| 84 | + <td>ND</td> | ||
| 85 | + </tr> | ||
| 86 | + <tr> | ||
| 87 | + <td>scale</td> | ||
| 88 | + <td>可选输入</td> | ||
| 89 | + <td>公式中的 $scale$,反量化系数。非量化必须为空;量化场景必选。PA 为 $[block\_num, N2, block\_size]$ 或 $[block\_num, block\_size, N2]$;TND 为 $[T2, N2]$。</td> | ||
| 90 | + <td>FLOAT</td> | ||
| 91 | + <td>ND</td> | ||
| 92 | + </tr> | ||
| 93 | + <tr> | ||
| 94 | + <td>atten_mask</td> | ||
| 95 | + <td>可选输入</td> | ||
| 96 | + <td>控制因果可见的 mask。仅在 <code>sparse_mode=3</code> 时使用;取值为 1 表示该位不参与计算,为 0 表示参与计算;shape 为 $[2048, 2048]$。</td> | ||
| 97 | + <td>INT8</td> | ||
| 98 | + <td>ND</td> | ||
| 99 | + </tr> | ||
| 100 | + <tr> | ||
| 101 | + <td>actual_seq_qlen</td> | ||
| 102 | + <td>可选输入</td> | ||
| 103 | + <td>每个 Batch 中 Query 的有效 token 数。query 为 TND 时必须传入,单调不减(前缀和),shape 为 $[B+1]$。</td> | ||
| 104 | + <td>INT32</td> | ||
| 105 | + <td>ND</td> | ||
| 106 | + </tr> | ||
| 107 | + <tr> | ||
| 108 | + <td>actual_seq_klen</td> | ||
| 109 | + <td>可选输入</td> | ||
| 110 | + <td>每个 Batch 中 Key 的有效 token 数。key 为 TND 时必须传入(前缀和);PageAttention 场景下为各请求可见 $S2$,shape 为 $[B]$。</td> | ||
| 111 | + <td>INT32</td> | ||
| 112 | + <td>ND</td> | ||
| 113 | + </tr> | ||
| 114 | + <tr> | ||
| 115 | + <td>start_loc</td> | ||
| 116 | + <td>输入</td> | ||
| 117 | + <td>当前 query 所在逻辑 block 索引(非 token 前缀),用于生成 $local\_mask$;shape 为 $[B]$。</td> | ||
| 118 | + <td>INT32</td> | ||
| 119 | + <td>ND</td> | ||
| 120 | + </tr> | ||
| 121 | + <tr> | ||
| 122 | + <td>layout_key</td> | ||
| 123 | + <td>属性</td> | ||
| 124 | + <td>key 布局。<code>"TND"</code> / <code>"BBND"</code> / <code>"BNBD"</code>。aclnn 参数名为 <code>layoutKeyOptional</code>,不传时默认 <code>"BBND"</code>。</td> | ||
| 125 | + <td>STRING</td> | ||
| 126 | + <td>-</td> | ||
| 127 | + </tr> | ||
| 128 | + <tr> | ||
| 129 | + <td>sparse_mode</td> | ||
| 130 | + <td>属性</td> | ||
| 131 | + <td>sparse 模式。0:defaultMask(<code>atten_mask</code> 传空);3:rightDownCausal(须传入 $[2048, 2048]$ 的 <code>atten_mask</code>)。</td> | ||
| 132 | + <td>INT64</td> | ||
| 133 | + <td>-</td> | ||
| 134 | + </tr> | ||
| 135 | + <tr> | ||
| 136 | + <td>init_blocks</td> | ||
| 137 | + <td>属性</td> | ||
| 138 | + <td>$local\_mask$ 强制选中的头部 block 数。对逻辑 block $[0, init\_blocks)$ 写入高分 $1\mathrm{e}30$。可选,默认 $0$。</td> | ||
| 139 | + <td>INT64</td> | ||
| 140 | + <td>-</td> | ||
| 141 | + </tr> | ||
| 142 | + <tr> | ||
| 143 | + <td>local_blocks</td> | ||
| 144 | + <td>属性</td> | ||
| 145 | + <td>$local\_mask$ 强制选中的局部窗口长度。窗口为 $[max(0, start\_loc+1-local\_blocks), start\_loc]$,写入高分 $1\mathrm{e}29$(覆盖同位置的 <code>init_blocks</code>)。可选,默认 $1$(对齐 MiniMax HF);与 Triton raw score 对齐时置 $0$。</td> | ||
| 146 | + <td>INT64</td> | ||
| 147 | + <td>-</td> | ||
| 148 | + </tr> | ||
| 149 | + <tr> | ||
| 150 | + <td>score</td> | ||
| 151 | + <td>输出</td> | ||
| 152 | + <td>公式中的 $score$,逐 block 重要性分数;shape 为 $[N1, T1, RoundUp(maxBlockNumPerSeq, 16)]$</td> | ||
| 153 | + <td>FLOAT</td> | ||
| 154 | + <td>ND</td> | ||
| 155 | + </tr> | ||
| 156 | +</tbody> | ||
| 157 | +</table> | ||
| 63 | 158 | ||
| 64 | ## 约束说明 | 159 | ## 约束说明 |
| 65 | 160 | ||
| 66 | - 当前 $block\_size$ 仅支持 128。 | 161 | - 当前 $block\_size$ 仅支持 128。 |
| 67 | - `layout_key` 必须显式指定:`"BBND"` / `"BNBD"` / `"TND"`,与 `key` 实际 shape 一致。 | 162 | - `layout_key` 必须显式指定:`"BBND"` / `"BNBD"` / `"TND"`,与 `key` 实际 shape 一致。 |
| 68 | - PageAttention(`layout_key` 为 `"BBND"` / `"BNBD"`)场景下,`block_table` 必须传入;TND key 场景不得传入 `block_table`,`actual_seq_klen` 为 `[B+1]` 前缀和。 | 163 | - PageAttention(`layout_key` 为 `"BBND"` / `"BNBD"`)场景下,`block_table` 必须传入;TND key 场景不得传入 `block_table`,`actual_seq_klen` 为 `[B+1]` 前缀和。 |
| 69 | -- 非量化场景下,`key` dtype 与 `query` 相同(当前为 BFLOAT16 / FLOAT16),`scale` 必须为空;量化场景下仅支持 INT8,`scale` 必选:PA 为 $[block\_num, N2, block\_size]$ 或 $[block\_num, block\_size, N2]$,TND 为 $[T2, N2]$,dtype 为 FLOAT。当前不支持 FP8 与 <term>Ascend 950PR/Ascend 950DT</term>。 | 164 | +- 非量化场景下,`key` dtype 与 `query` 相同(BFLOAT16 / FLOAT16;Ascend 950 另支持 HIFLOAT8 / FLOAT8_E5M2 / FLOAT8_E4M3FN),`scale` 必须为空。量化场景仅支持 INT8(fp16 query),`scale` 必选:PA 为 $[block\_num, N2, block\_size]$ 或 $[block\_num, block\_size, N2]$,TND 为 $[T2, N2]$,dtype 为 FLOAT。三种 FP8 仅 950,且 query 与 key 必须同型、不得传 `scale`。 |
| 70 | - `sparse_mode` 当前仅支持 0、3: | 165 | - `sparse_mode` 当前仅支持 0、3: |
| 71 | - 为 0 时,代表 defaultMask 模式,`atten_mask` 传入空; | 166 | - 为 0 时,代表 defaultMask 模式,`atten_mask` 传入空; |
| 72 | - 为 3 时,代表 rightDownCausal 模式,`atten_mask` 必须传入,shape 为 $[2048, 2048]$,取值为 1 代表该位不参与计算,为 0 代表该位参与计算。 | 167 | - 为 3 时,代表 rightDownCausal 模式,`atten_mask` 必须传入,shape 为 $[2048, 2048]$,取值为 1 代表该位不参与计算,为 0 代表该位参与计算。 |
| 73 | - `init_blocks`、`local_blocks` 必须 $\ge 0$ 且不超过逻辑 block 数(PA 为 `block_table` 第二维;TND 为 score 末维对齐宽度)。两者均为 0 时跳过 $local\_mask$。 | 168 | - `init_blocks`、`local_blocks` 必须 $\ge 0$ 且不超过逻辑 block 数(PA 为 `block_table` 第二维;TND 为 score 末维对齐宽度)。两者均为 0 时跳过 $local\_mask$。 |
| 169 | +- PageAttention `block_table` 第二维可以大于实际 KV 逻辑 block 数;score 末维为 $\mathrm{RoundUp}(width, 16)$。Ascend 950 C2UB 对超过 256 列按 256 列滑窗 flush。 | ||
| 170 | +- A2/A3 与 Ascend 950:`q_len` / `kv_len` 允许为 0(含整 batch)。对应请求跳过 QK;空 KV 的 score 填 `-inf`;整 batch `T1=0` 时 `SetBlockDim(1)`。 | ||
| 171 | +- A2/A3 与 Ascend 950:按估计 M-task 数启动 MIX(`B=1` 时为 $\mathrm{CeilDiv}(T1\cdot N1, 128)$;多 batch 用 packed+$B$ 上界),短 decode 不打满 AIC。 | ||
| 74 | - 本算子输出止于 block score,**不包含** TopK。 | 172 | - 本算子输出止于 block score,**不包含** TopK。 |
| 75 | 173 | ||
| 76 | ## 调用示例 | 174 | ## 调用示例 |
| 77 | 175 | ||
| 78 | -| 调用方式 | 样例代码 | 说明 | | 176 | +<table style="undefined;table-layout: fixed; width: 1100px"><colgroup> |
| 79 | -|----------|----------|------| | 177 | +<col style="width: 180px"> |
| 80 | -| aclnn 单算子调用 | [test_aclnn_msa_index_score.cpp](./examples/test_aclnn_msa_index_score.cpp) | 内置 CPU golden 的端到端精度自验证 | | 178 | +<col style="width: 400px"> |
| 81 | -| 接口文档 | [aclnnMsaIndexScore.md](./docs/aclnnMsaIndexScore.md) | 两段式接口说明 | | 179 | +<col style="width: 520px"> |
| 82 | -| 测试说明 | [tests/README.md](./tests/README.md) | 用例矩阵与运行方式 | | 180 | +</colgroup> |
| 83 | -| PyTorch API | - | 通过[msa_index_score](../../attention/msa_index_score/docs/torchapi_msa_index_score.md)接口调用msa_index_score算子。 | | 181 | +<thead> |
| 182 | + <tr> | ||
| 183 | + <th>调用方式</th> | ||
| 184 | + <th>样例代码</th> | ||
| 185 | + <th>说明</th> | ||
| 186 | + </tr> | ||
| 187 | +</thead> | ||
| 188 | +<tbody> | ||
| 189 | + <tr> | ||
| 190 | + <td>aclnn 单算子调用</td> | ||
| 191 | + <td><a href="./examples/test_aclnn_msa_index_score.cpp">test_aclnn_msa_index_score.cpp</a></td> | ||
| 192 | + <td>内置 CPU golden 的端到端精度自验证</td> | ||
| 193 | + </tr> | ||
| 194 | + <tr> | ||
| 195 | + <td>接口文档</td> | ||
| 196 | + <td><a href="./docs/aclnnMsaIndexScore.md">aclnnMsaIndexScore.md</a></td> | ||
| 197 | + <td>两段式接口说明</td> | ||
| 198 | + </tr> | ||
| 199 | + <tr> | ||
| 200 | + <td>测试说明</td> | ||
| 201 | + <td><a href="./tests/README.md">tests/README.md</a></td> | ||
| 202 | + <td>用例矩阵与运行方式</td> | ||
| 203 | + </tr> | ||
| 204 | + <tr> | ||
| 205 | + <td>PyTorch API</td> | ||
| 206 | + <td>-</td> | ||
| 207 | + <td>通过<a href="./docs/torchapi_msa_index_score.md">msa_index_score</a>接口调用msa_index_score算子。</td> | ||
| 208 | + </tr> | ||
| 209 | +</tbody> | ||
| 210 | +</table> | ||
| 84 | 211 | ||
| 85 | -编译与运行: | 212 | +编译与运行(Ascend 950,x86 包名为 `linux-x86_64.run`;A2/A3 将 `--soc` 换成对应平台): |
| 86 | 213 | ||
| 87 | ```bash | 214 | ```bash |
| 88 | -bash build.sh --pkg --soc=ascend910b --ops=msa_index_score -j32 | 215 | +cd /path/to/ops-transformer-ar-950 |
| 89 | -./build_out/cann-ops-transformer-custom_linux-aarch64.run --quiet --install-path=/tmp/msa_opp | 216 | +bash build.sh --pkg --soc=ascend950 --ops=msa_index_score -j32 |
| 90 | -export ASCEND_CUSTOM_OPP_PATH=/tmp/msa_opp/vendors/custom_transformer | 217 | +bash ./build_out/cann-ops-transformer-custom_linux-x86_64.run --quiet --install-path=/path/to/msa_opp/ |
| 91 | -bash build.sh --run_example msa_index_score eager cust --vendor_name=custom | 218 | +source /path/to/msa_opp/vendors/custom_transformer/bin/set_env.bash |
| 219 | +export ASCEND_CUSTOM_OPP_PATH=/path/to/msa_opp/vendors/custom_transformer | ||
| 220 | + | ||
| 221 | +# aclnn example 必须带 --soc=ascend950(否则默认 910b) | ||
| 222 | +bash build.sh --run_example msa_index_score eager cust --vendor_name=custom --soc=ascend950 | ||
| 223 | +# 通过:末行 [PASS]: 47/47 cases passed | ||
| 224 | +# 矩阵:39 条 fp16/bf16/int8(BBND/BNBD/TND,含 pad / 空序列 / key dim0 stride / 宽 block_table / 短 decode)+ 8 条 FP8(D=128) | ||
| 225 | +# 容差:fp16/bf16/int8 1e-3,FP8 2e-2 | ||
| 226 | +# A2/A3:同上矩阵去掉 FP8,期望 [PASS]: 39/39(skipped 8 FP8) | ||
| 92 | ``` | 227 | ``` |
| 93 | 228 | ||
| 94 | -> **实现备注(A2/A3)** | 229 | +torch_extension 见 [msa_index_score.md](./docs/torchapi_msa_index_score.md)。`cann/set_env.sh` 会把 cann 自带 `site-packages` 插到 `PYTHONPATH` 前面,必须把本仓 `torch_extension` 再插回最前。 |
| 230 | + | ||
| 231 | +> **实现备注(A2/A3 / Ascend 950)** | ||
| 95 | > | 232 | > |
| 96 | > - key 布局由属性 `layout_key`(aclnn:`layoutKeyOptional`)指定,支持 PageAttention **BBND** / **BNBD**,以及 packed **TND**(无 `block_table`,`actual_seq_klen` 为 `[B+1]` 前缀和)。默认 `"BBND"`。 | 233 | > - key 布局由属性 `layout_key`(aclnn:`layoutKeyOptional`)指定,支持 PageAttention **BBND** / **BNBD**,以及 packed **TND**(无 `block_table`,`actual_seq_klen` 为 `[B+1]` 前缀和)。默认 `"BBND"`。 |
| 97 | > - `sparse_mode=3` 的 `atten_mask[2048,2048]` 在 host 校验必选;device 侧按 rightDownCausal | 234 | > - `sparse_mode=3` 的 `atten_mask[2048,2048]` 在 host 校验必选;device 侧按 rightDownCausal |
| @@ -99,3 +236,9 @@ bash build.sh --run_example msa_index_score eager cust --vendor_name=custom | |||
| 99 | > - `start_loc` 为逻辑 block 索引,与属性 `init_blocks`(默认 0)、`local_blocks`(默认 1) | 236 | > - `start_loc` 为逻辑 block 索引,与属性 `init_blocks`(默认 0)、`local_blocks`(默认 1) |
| 100 | > 一起在 Maxpool 之后施加 `local_mask`。 | 237 | > 一起在 Maxpool 之后施加 `local_mask`。 |
| 101 | > - 完整公式:`score = Maxpool[(scale·)Q@Kᵀ + atten_mask] + local_mask`。 | 238 | > - 完整公式:`score = Maxpool[(scale·)Q@Kᵀ + atten_mask] + local_mask`。 |
| 239 | +> - **950 当前交付**(`op_kernel/arch35/`):计算骨架与 A2 相同(Q 驻留 × K pingpong × **8-page S GM** + MODE 0x2 握手)。Cube **原生** FP8(TilingKey 4/5/6,`hifloat8_t` / `fp8_e5m2_t` / `fp8_e4m3fn_t`,无 scale,禁止 Cast→fp16)。L0C→AIV UB(C_to_UB)尚未作为主路径。`arch22/` 冻结。 | ||
| 240 | +> - **PA key dim0 stride**(A2/A3 与 950):key 为 `IgnoreContiguous`;tiling 用 `GetInputStride` / `GetRequiredInputStride` 读首轴元素 stride,写入已有 `strideKvBlock`。TND 不允许非连续。scale 仍按逻辑 page 紧凑布局。 | ||
| 241 | +> - **950 宽 `block_table`**:C2UB score 暂存单窗 256 列;末维 `RoundUp(width,16)>256` 时按窗 flush 并补写后续 `-inf`,不能关掉 stage。 | ||
| 242 | +> - **短 decode**(A2/A3 与 950):host 按估计 M-task 数 `SetBlockDim`(单请求即 $\mathrm{CeilDiv}(T1\cdot N1,128)$),不再默认打满 AIC;多 M-tile / 大 batch 仍可占满 AIC。 | ||
| 243 | +> - torch 的 `torch_npu.hifloat8.npu()` 当前会打出非法 device id,脚本跳过;kernel / aclnn 已挂 HIFLOAT8。 | ||
| 244 | +> - 测性能须选 Health=OK 且空闲的卡。 | ||
| @@ -4,11 +4,15 @@ | |||
| 4 | 4 | ||
| 5 | ## 产品支持情况 | 5 | ## 产品支持情况 |
| 6 | 6 | ||
| 7 | -| 产品 | 是否支持 | | 7 | +<!-- npu="910b" id1 --> |
| 8 | -| --------------------------------------------------------- | :------: | | 8 | +- <term>Atlas A2 训练系列产品/Atlas A2 推理系列产品</term>:支持 |
| 9 | -| <term>Atlas A2 训练系列产品/Atlas 800I A2 推理产品</term> | √ | | 9 | +<!-- end id1 --> |
| 10 | -| <term>Atlas A3 训练系列产品/Atlas A3 推理系列产品</term> | √ | | 10 | +<!-- npu="A3" id2 --> |
| 11 | -| <term>Ascend 950PR/Ascend 950DT</term> | × | | 11 | +- <term>Atlas A3 训练系列产品/Atlas A3 推理系列产品</term>:支持 |
| 12 | +<!-- end id2 --> | ||
| 13 | +<!-- npu="950" id3 --> | ||
| 14 | +- <term>Ascend 950PR/Ascend 950DT</term>:支持 | ||
| 15 | +<!-- end id3 --> | ||
| 12 | 16 | ||
| 13 | ## 功能说明 | 17 | ## 功能说明 |
| 14 | 18 | ||
| @@ -75,62 +79,290 @@ aclnnStatus aclnnMsaIndexScore( | |||
| 75 | 79 | ||
| 76 | ## aclnnMsaIndexScoreGetWorkspaceSize | 80 | ## aclnnMsaIndexScoreGetWorkspaceSize |
| 77 | 81 | ||
| 78 | -### 参数说明 | 82 | +- **参数说明:** |
| 79 | 83 | ||
| 80 | -| 参数名 | 输入/输出 | 描述 | 使用说明 | 数据类型 | 数据格式 | 维度 | | 84 | + <table style="undefined;table-layout: fixed; width: 1600px"><colgroup> |
| 81 | -| ----------------------------- | --------- | ------------------------------------------------- | ------------------------------------------------------------ | ---------------------------- | -------- | ------------------------------------------------------------ | | 85 | + <col style="width: 200px"> |
| 82 | -| query | 输入 | 公式中的$Q_{idx}$ | 支持的shape为:TND | BFLOAT16, FLOAT16 | ND | 3($[T1, N1, D]$) | | 86 | + <col style="width: 100px"> |
| 83 | -| key | 输入 | 公式中的$K_{idx}$ | 支持的shape为:TND、BNBD、BBND | BFLOAT16, FLOAT16, INT8 | ND, NZ | 3($[T2, N2, D]$)或 4($[block\_num, N2, block\_size, D]$、$[block\_num, block\_size, N2, D]$) | | 87 | + <col style="width: 280px"> |
| 84 | -| blockTableOptional | 输入 | 表示当前传入的使用PageAttention存储的block映射表 | PageAttention场景下,blockTableOptional需为2维;第二维长度不能小于 $maxBlockNumPerSeq$ | INT32 | ND | 2($[B, S2/block\_size]$) | | 88 | + <col style="width: 420px"> |
| 85 | -| scaleOptional | 输入 | 公式中的$scale$,反量化系数 | 非量化场景传入`nullptr`;量化场景时必选。PA 为 BNB/BBN;TND 为 $[T2, N2]$(N2=1 时可 $[T2]$) | FLOAT | ND, NZ | 3($[block\_num, N2, block\_size]$、$[block\_num, block\_size, N2]$)或 2($[T2, N2]$) | | 89 | + <col style="width: 280px"> |
| 86 | -| attenMaskOptional | 输入 | 控制因果可见的mask掩码。 | 仅在sparseMode=3时使用,作为base mask控制causal可见;取值为1代表该位不参与计算,为0代表该位参与计算 | INT8 | ND | 2($[2048, 2048]$) | | 90 | + <col style="width: 100px"> |
| 87 | -| actualSeqQlenOptional | 输入 | 每个Batch中,Query的有效token数 | 当传入TND时,该入参必须传入,单调不减(前缀和) | INT32 | ND | 1($[B+1]$) | | 91 | + <col style="width: 220px"> |
| 88 | -| actualSeqKlenOptional | 输入 | 每个Batch中,Key的有效token数 | key 为 TND 时必须传入,单调不减(前缀和,$[B+1]$);PageAttention 场景下为各请求可见 $S2$($[B]$) | INT32 | ND | 1($[B]$ 或 $[B+1]$) | | 92 | + </colgroup> |
| 89 | -| startLoc | 输入 | 当前 query 所在逻辑 block 索引(非 token 前缀) | 与 `initBlocks` / `localBlocks` 一起生成 $local\_mask$ | INT32 | ND | 1($[B]$) | | 93 | + <thead> |
| 90 | -| layoutKeyOptional | 输入 | key 的数据排布 | 取值 `"TND"` / `"BBND"` / `"BNBD"`。不传或空串时默认 `"BBND"`。必须与 `key` 实际 shape 一致,不可仅凭维度推断 | CHAR* | - | - | | 94 | + <tr> |
| 91 | -| sparseMode | 输入 | 表示sparse的模式 | 为0时,代表defaultMask模式;为3时,代表rightDownCausal模式的mask,对应以右顶点为划分的下三角场景。 | INT64 | - | - | | 95 | + <th>参数名</th> |
| 92 | -| initBlocks | 输入 | $local\_mask$ 强制选中的头部 block 数 | 对逻辑 block $[0, initBlocks)$ 写入高分 $1\mathrm{e}30$。可选,默认 $0$;须 $\ge 0$ 且 $\le maxBlockNumPerSeq$ | INT64 | - | - | | 96 | + <th>输入/输出</th> |
| 93 | -| localBlocks | 输入 | $local\_mask$ 强制选中的局部窗口长度 | 窗口为 $[max(0, startLoc+1-localBlocks), startLoc]$,写入高分 $1\mathrm{e}29$(覆盖同位置 init)。可选,默认 $1$(对齐 MiniMax HF);与 Triton raw score 对齐时置 $0$ | INT64 | - | - | | 97 | + <th>描述</th> |
| 94 | -| score | 输出 | 公式中的$score$ | 逐block的重要性分数;末维为对齐后的逻辑 block 数 | FLOAT | ND | 3($[N1, T1, RoundUp(maxBlockNumPerSeq, 16)]$) | | 98 | + <th>使用说明</th> |
| 95 | -| workspaceSize | 输出 | 所需 workspace 字节数 | - | uint64_t | - | - | | 99 | + <th>数据类型</th> |
| 96 | -| executor | 输出 | 算子执行器 | - | aclOpExecutor** | - | - | | 100 | + <th>数据格式</th> |
| 101 | + <th>维度(shape)</th> | ||
| 102 | + </tr> | ||
| 103 | + </thead> | ||
| 104 | + <tbody> | ||
| 105 | + <tr> | ||
| 106 | + <td>query</td> | ||
| 107 | + <td>输入</td> | ||
| 108 | + <td>公式中的$Q_{idx}$。</td> | ||
| 109 | + <td>支持的shape为:TND。</td> | ||
| 110 | + <td>BFLOAT16、FLOAT16、HIFLOAT8、FLOAT8_E5M2、FLOAT8_E4M3FN</td> | ||
| 111 | + <td>ND</td> | ||
| 112 | + <td>3($[T1, N1, D]$)</td> | ||
| 113 | + </tr> | ||
| 114 | + <tr> | ||
| 115 | + <td>key</td> | ||
| 116 | + <td>输入</td> | ||
| 117 | + <td>公式中的$K_{idx}$。</td> | ||
| 118 | + <td>支持的shape为:TND、BNBD、BBND。A2/A3 与 Ascend 950 上 PA key 允许 dim0(物理 page)非连续,page 内其余轴须连续。TND 不允许非连续。</td> | ||
| 119 | + <td>BFLOAT16、FLOAT16、INT8、HIFLOAT8、FLOAT8_E5M2、FLOAT8_E4M3FN</td> | ||
| 120 | + <td>ND</td> | ||
| 121 | + <td>3($[T2, N2, D]$)或 4($[block\_num, N2, block\_size, D]$、$[block\_num, block\_size, N2, D]$)</td> | ||
| 122 | + </tr> | ||
| 123 | + <tr> | ||
| 124 | + <td>blockTableOptional</td> | ||
| 125 | + <td>输入</td> | ||
| 126 | + <td>表示当前传入的使用PageAttention存储的block映射表。</td> | ||
| 127 | + <td>PageAttention场景下,blockTableOptional需为2维;第二维长度不能小于 $maxBlockNumPerSeq$。</td> | ||
| 128 | + <td>INT32</td> | ||
| 129 | + <td>ND</td> | ||
| 130 | + <td>2($[B, S2/block\_size]$)</td> | ||
| 131 | + </tr> | ||
| 132 | + <tr> | ||
| 133 | + <td>scaleOptional</td> | ||
| 134 | + <td>输入</td> | ||
| 135 | + <td>公式中的$scale$,反量化系数。</td> | ||
| 136 | + <td>非量化场景传入 <code>nullptr</code>;量化场景时必选。PA 为 BNB/BBN;TND 为 $[T2, N2]$(N2=1 时可 $[T2]$)。</td> | ||
| 137 | + <td>FLOAT</td> | ||
| 138 | + <td>ND</td> | ||
| 139 | + <td>3($[block\_num, N2, block\_size]$、$[block\_num, block\_size, N2]$)或 2($[T2, N2]$)</td> | ||
| 140 | + </tr> | ||
| 141 | + <tr> | ||
| 142 | + <td>attenMaskOptional</td> | ||
| 143 | + <td>输入</td> | ||
| 144 | + <td>控制因果可见的mask掩码。</td> | ||
| 145 | + <td>仅在sparseMode=3时使用,作为base mask控制causal可见;取值为1代表该位不参与计算,为0代表该位参与计算。</td> | ||
| 146 | + <td>INT8</td> | ||
| 147 | + <td>ND</td> | ||
| 148 | + <td>2($[2048, 2048]$)</td> | ||
| 149 | + </tr> | ||
| 150 | + <tr> | ||
| 151 | + <td>actualSeqQlenOptional</td> | ||
| 152 | + <td>输入</td> | ||
| 153 | + <td>每个Batch中,Query的有效token数。</td> | ||
| 154 | + <td>当传入TND时,该入参必须传入,单调不减(前缀和)。</td> | ||
| 155 | + <td>INT32</td> | ||
| 156 | + <td>ND</td> | ||
| 157 | + <td>1($[B+1]$)</td> | ||
| 158 | + </tr> | ||
| 159 | + <tr> | ||
| 160 | + <td>actualSeqKlenOptional</td> | ||
| 161 | + <td>输入</td> | ||
| 162 | + <td>每个Batch中,Key的有效token数。</td> | ||
| 163 | + <td>key 为 TND 时必须传入,单调不减(前缀和,$[B+1]$);PageAttention 场景下为各请求可见 $S2$($[B]$)。</td> | ||
| 164 | + <td>INT32</td> | ||
| 165 | + <td>ND</td> | ||
| 166 | + <td>1($[B]$ 或 $[B+1]$)</td> | ||
| 167 | + </tr> | ||
| 168 | + <tr> | ||
| 169 | + <td>startLoc</td> | ||
| 170 | + <td>输入</td> | ||
| 171 | + <td>当前 query 所在逻辑 block 索引(非 token 前缀)。</td> | ||
| 172 | + <td>与 <code>initBlocks</code> / <code>localBlocks</code> 一起生成 $local\_mask$。</td> | ||
| 173 | + <td>INT32</td> | ||
| 174 | + <td>ND</td> | ||
| 175 | + <td>1($[B]$)</td> | ||
| 176 | + </tr> | ||
| 177 | + <tr> | ||
| 178 | + <td>layoutKeyOptional</td> | ||
| 179 | + <td>输入</td> | ||
| 180 | + <td>key 的数据排布。</td> | ||
| 181 | + <td>取值 <code>"TND"</code> / <code>"BBND"</code> / <code>"BNBD"</code>。不传或空串时默认 <code>"BBND"</code>。必须与 <code>key</code> 实际 shape 一致,不可仅凭维度推断。</td> | ||
| 182 | + <td>CHAR*</td> | ||
| 183 | + <td>-</td> | ||
| 184 | + <td>-</td> | ||
| 185 | + </tr> | ||
| 186 | + <tr> | ||
| 187 | + <td>sparseMode</td> | ||
| 188 | + <td>输入</td> | ||
| 189 | + <td>表示sparse的模式。</td> | ||
| 190 | + <td>为0时,代表defaultMask模式;为3时,代表rightDownCausal模式的mask,对应以右顶点为划分的下三角场景。</td> | ||
| 191 | + <td>INT64</td> | ||
| 192 | + <td>-</td> | ||
| 193 | + <td>-</td> | ||
| 194 | + </tr> | ||
| 195 | + <tr> | ||
| 196 | + <td>initBlocks</td> | ||
| 197 | + <td>输入</td> | ||
| 198 | + <td>$local\_mask$ 强制选中的头部 block 数。</td> | ||
| 199 | + <td>对逻辑 block $[0, initBlocks)$ 写入高分 $1\mathrm{e}30$。可选,默认 $0$;须 $\ge 0$ 且 $\le maxBlockNumPerSeq$。</td> | ||
| 200 | + <td>INT64</td> | ||
| 201 | + <td>-</td> | ||
| 202 | + <td>-</td> | ||
| 203 | + </tr> | ||
| 204 | + <tr> | ||
| 205 | + <td>localBlocks</td> | ||
| 206 | + <td>输入</td> | ||
| 207 | + <td>$local\_mask$ 强制选中的局部窗口长度。</td> | ||
| 208 | + <td>窗口为 $[max(0, startLoc+1-localBlocks), startLoc]$,写入高分 $1\mathrm{e}29$(覆盖同位置 init)。可选,默认 $1$(对齐 MiniMax HF);与 Triton raw score 对齐时置 $0$。</td> | ||
| 209 | + <td>INT64</td> | ||
| 210 | + <td>-</td> | ||
| 211 | + <td>-</td> | ||
| 212 | + </tr> | ||
| 213 | + <tr> | ||
| 214 | + <td>score</td> | ||
| 215 | + <td>输出</td> | ||
| 216 | + <td>公式中的$score$。</td> | ||
| 217 | + <td>逐block的重要性分数;末维为对齐后的逻辑 block 数。</td> | ||
| 218 | + <td>FLOAT</td> | ||
| 219 | + <td>ND</td> | ||
| 220 | + <td>3($[N1, T1, RoundUp(maxBlockNumPerSeq, 16)]$)</td> | ||
| 221 | + </tr> | ||
| 222 | + <tr> | ||
| 223 | + <td>workspaceSize</td> | ||
| 224 | + <td>输出</td> | ||
| 225 | + <td>返回需要在Device侧申请的workspace大小。</td> | ||
| 226 | + <td>-</td> | ||
| 227 | + <td>-</td> | ||
| 228 | + <td>-</td> | ||
| 229 | + <td>-</td> | ||
| 230 | + </tr> | ||
| 231 | + <tr> | ||
| 232 | + <td>executor</td> | ||
| 233 | + <td>输出</td> | ||
| 234 | + <td>返回op执行器,包含了算子计算流程。</td> | ||
| 235 | + <td>-</td> | ||
| 236 | + <td>-</td> | ||
| 237 | + <td>-</td> | ||
| 238 | + <td>-</td> | ||
| 239 | + </tr> | ||
| 240 | + </tbody> | ||
| 241 | + </table> | ||
| 97 | 242 | ||
| 98 | -### 返回值 | 243 | +- **返回值:** |
| 99 | 244 | ||
| 100 | -| 返回码 | 错误码 | 说明 | | 245 | + aclnnStatus:返回状态码,具体参见[aclnn返回码](../../../docs/zh/context/aclnn_return_code.md)。 |
| 101 | -| ----------------------- | ------ | ---------------------------------------- | | 246 | + |
| 102 | -| ACLNN_SUCCESS | 0 | 执行成功 | | 247 | + 第一段接口会完成入参校验,出现以下场景时报错: |
| 103 | -| ACLNN_ERR_PARAM_NULLPTR | 161001 | 必选入参或出参为空指针 | | 248 | + |
| 104 | -| ACLNN_ERR_PARAM_INVALID | 161002 | 数据类型、数据格式、维度或取值不满足约束 | | 249 | + <table style="undefined;table-layout: fixed;width: 1155px"><colgroup> |
| 250 | + <col style="width: 319px"> | ||
| 251 | + <col style="width: 144px"> | ||
| 252 | + <col style="width: 671px"> | ||
| 253 | + </colgroup> | ||
| 254 | + <thead> | ||
| 255 | + <tr> | ||
| 256 | + <th>返回码</th> | ||
| 257 | + <th>错误码</th> | ||
| 258 | + <th>描述</th> | ||
| 259 | + </tr> | ||
| 260 | + </thead> | ||
| 261 | + <tbody> | ||
| 262 | + <tr> | ||
| 263 | + <td>ACLNN_SUCCESS</td> | ||
| 264 | + <td>0</td> | ||
| 265 | + <td>执行成功。</td> | ||
| 266 | + </tr> | ||
| 267 | + <tr> | ||
| 268 | + <td>ACLNN_ERR_PARAM_NULLPTR</td> | ||
| 269 | + <td>161001</td> | ||
| 270 | + <td>必选入参或出参为空指针。</td> | ||
| 271 | + </tr> | ||
| 272 | + <tr> | ||
| 273 | + <td>ACLNN_ERR_PARAM_INVALID</td> | ||
| 274 | + <td>161002</td> | ||
| 275 | + <td>数据类型、数据格式、维度或取值不满足约束。</td> | ||
| 276 | + </tr> | ||
| 277 | + </tbody> | ||
| 278 | + </table> | ||
| 105 | 279 | ||
| 106 | ## aclnnMsaIndexScore | 280 | ## aclnnMsaIndexScore |
| 107 | 281 | ||
| 108 | -### 参数说明 | 282 | +- **参数说明:** |
| 109 | 283 | ||
| 110 | -| 参数名 | 输入/输出 | 描述 | | 284 | + <table style="undefined;table-layout: fixed; width: 953px"><colgroup> |
| 111 | -| ------------- | --------- | ---------------------------------- | | 285 | + <col style="width: 173px"> |
| 112 | -| workspace | 输入 | device 侧 workspace 地址 | | 286 | + <col style="width: 112px"> |
| 113 | -| workspaceSize | 输入 | workspace 字节数,由第一段接口返回 | | 287 | + <col style="width: 668px"> |
| 114 | -| executor | 输入 | 算子执行器,由第一段接口返回 | | 288 | + </colgroup> |
| 115 | -| stream | 输入 | acl stream | | 289 | + <thead> |
| 290 | + <tr> | ||
| 291 | + <th>参数名</th> | ||
| 292 | + <th>输入/输出</th> | ||
| 293 | + <th>描述</th> | ||
| 294 | + </tr> | ||
| 295 | + </thead> | ||
| 296 | + <tbody> | ||
| 297 | + <tr> | ||
| 298 | + <td>workspace</td> | ||
| 299 | + <td>输入</td> | ||
| 300 | + <td>在Device侧申请的workspace内存地址。</td> | ||
| 301 | + </tr> | ||
| 302 | + <tr> | ||
| 303 | + <td>workspaceSize</td> | ||
| 304 | + <td>输入</td> | ||
| 305 | + <td>在Device侧申请的workspace大小,由第一段接口aclnnMsaIndexScoreGetWorkspaceSize获取。</td> | ||
| 306 | + </tr> | ||
| 307 | + <tr> | ||
| 308 | + <td>executor</td> | ||
| 309 | + <td>输入</td> | ||
| 310 | + <td>op执行器,包含了算子计算流程。</td> | ||
| 311 | + </tr> | ||
| 312 | + <tr> | ||
| 313 | + <td>stream</td> | ||
| 314 | + <td>输入</td> | ||
| 315 | + <td>指定执行任务的Stream。</td> | ||
| 316 | + </tr> | ||
| 317 | + </tbody> | ||
| 318 | + </table> | ||
| 116 | 319 | ||
| 117 | -### 返回值 | 320 | +- **返回值:** |
| 118 | 321 | ||
| 119 | -| 返回码 | 错误码 | 说明 | | 322 | + aclnnStatus:返回状态码,具体参见[aclnn返回码](../../../docs/zh/context/aclnn_return_code.md)。 |
| 120 | -| ----------------------- | ------ | ---------- | | 323 | + |
| 121 | -| ACLNN_SUCCESS | 0 | 执行成功 | | 324 | + <table style="undefined;table-layout: fixed;width: 1155px"><colgroup> |
| 122 | -| ACLNN_ERR_PARAM_INVALID | 161002 | 参数不合法 | | 325 | + <col style="width: 319px"> |
| 326 | + <col style="width: 144px"> | ||
| 327 | + <col style="width: 671px"> | ||
| 328 | + </colgroup> | ||
| 329 | + <thead> | ||
| 330 | + <tr> | ||
| 331 | + <th>返回码</th> | ||
| 332 | + <th>错误码</th> | ||
| 333 | + <th>描述</th> | ||
| 334 | + </tr> | ||
| 335 | + </thead> | ||
| 336 | + <tbody> | ||
| 337 | + <tr> | ||
| 338 | + <td>ACLNN_SUCCESS</td> | ||
| 339 | + <td>0</td> | ||
| 340 | + <td>执行成功。</td> | ||
| 341 | + </tr> | ||
| 342 | + <tr> | ||
| 343 | + <td>ACLNN_ERR_PARAM_INVALID</td> | ||
| 344 | + <td>161002</td> | ||
| 345 | + <td>参数不合法。</td> | ||
| 346 | + </tr> | ||
| 347 | + </tbody> | ||
| 348 | + </table> | ||
| 123 | 349 | ||
| 124 | ## 约束说明 | 350 | ## 约束说明 |
| 125 | 351 | ||
| 126 | - 当前 $block\_size$ 值使用128。 | 352 | - 当前 $block\_size$ 值使用128。 |
| 127 | - `layoutKeyOptional` 必须显式指定 key 布局:`"BBND"`($[block\_num, block\_size, N2, D]$)、`"BNBD"`($[block\_num, N2, block\_size, D]$)或 `"TND"`($[T2, N2, D]$)。不传时默认 `"BBND"`。 | 353 | - `layoutKeyOptional` 必须显式指定 key 布局:`"BBND"`($[block\_num, block\_size, N2, D]$)、`"BNBD"`($[block\_num, N2, block\_size, D]$)或 `"TND"`($[T2, N2, D]$)。不传时默认 `"BBND"`。 |
| 128 | - PageAttention(`layoutKey` 为 `"BBND"` / `"BNBD"`)场景下,`blockTableOptional` 必须传入;TND key 场景不得传入 `blockTableOptional`,`actualSeqKlenOptional` 为 $[B+1]$ 前缀和。 | 354 | - PageAttention(`layoutKey` 为 `"BBND"` / `"BNBD"`)场景下,`blockTableOptional` 必须传入;TND key 场景不得传入 `blockTableOptional`,`actualSeqKlenOptional` 为 $[B+1]$ 前缀和。 |
| 129 | -- 非量化场景下,`key` dtype 与 `query` 相同(当前为 BFLOAT16 / FLOAT16),`scaleOptional` 必须为 `nullptr`;量化场景下仅支持 INT8,`scaleOptional` 必选,dtype 为 FLOAT:PA 为 $[block\_num, N2, block\_size]$ 或 $[block\_num, block\_size, N2]$,TND 为 $[T2, N2]$。当前不支持 FP8 与 <term>Ascend 950PR/Ascend 950DT</term>。 | 355 | +- 非量化场景下,`key` dtype 与 `query` 相同(BFLOAT16 / FLOAT16;Ascend 950 另支持 HIFLOAT8 / FLOAT8_E5M2 / FLOAT8_E4M3FN),`scaleOptional` 必须为 `nullptr`。量化场景仅支持 INT8(fp16 query),`scaleOptional` 必选,dtype 为 FLOAT:PA 为 $[block\_num, N2, block\_size]$ 或 $[block\_num, block\_size, N2]$,TND 为 $[T2, N2]$。三种 FP8 仅 950,query 与 key 必须同型。 |
| 130 | - `sparseMode` 当前仅支持 0、3: | 356 | - `sparseMode` 当前仅支持 0、3: |
| 131 | - 为 0 时,代表 defaultMask 模式,`attenMaskOptional` 传入 `nullptr`; | 357 | - 为 0 时,代表 defaultMask 模式,`attenMaskOptional` 传入 `nullptr`; |
| 132 | - 为 3 时,代表 rightDownCausal 模式,`attenMaskOptional` 必须传入,shape 为 $[2048, 2048]$,取值为 1 代表该位不参与计算,为 0 代表该位参与计算。 | 358 | - 为 3 时,代表 rightDownCausal 模式,`attenMaskOptional` 必须传入,shape 为 $[2048, 2048]$,取值为 1 代表该位不参与计算,为 0 代表该位参与计算。 |
| 133 | - `initBlocks`、`localBlocks` 必须 $\ge 0$ 且不超过逻辑 block 数(PA 为 `blockTableOptional` 第二维;TND 为 score 末维对齐宽度)。两者均为 0 时跳过 $local\_mask$。 | 359 | - `initBlocks`、`localBlocks` 必须 $\ge 0$ 且不超过逻辑 block 数(PA 为 `blockTableOptional` 第二维;TND 为 score 末维对齐宽度)。两者均为 0 时跳过 $local\_mask$。 |
| 360 | +- `q_len` / `kv_len` 允许为 0(含整 batch)。对应请求跳过 QK;空 KV 的 score 填 $-inf$;整 batch $T1=0$ 时 `SetBlockDim(1)`(对齐 FIA PR 9246)。 | ||
| 361 | +- A2/A3 与 Ascend 950:按估计 M-task 数启动 MIX(单 batch 为 $\mathrm{CeilDiv}(T1\cdot N1,128)$;多 batch 取 packed+$B$ 上界并截到 AIC 数),短 decode 不打满空核。 | ||
| 362 | +- PageAttention `key`(BBND/BNBD)允许首轴非连续(`key | gap | key | ...`),tiling 通过 `GetInputStride` 读取 dim0 元素 stride 写入 `strideKvBlock`;非首轴必须连续。TND `key` 不允许非连续。`scale` 仍按逻辑 page 紧凑布局。 | ||
| 363 | +- PageAttention `blockTable` 第二维可以大于实际 KV 逻辑 block 数;score 末维为 $\mathrm{RoundUp}(width, 16)$。Ascend 950 C2UB 路径对超过 256 列的末维按 256 列滑窗 flush,并补写后续 $-inf$。 | ||
| 364 | +- 精度自验证矩阵:39 条 fp16/bf16/int8(含 PA key dim0 stride、宽 `blockTable`、短 decode)+ 8 条 FP8。950 通过标准末行 `[PASS]: 47/47 cases passed`。A2/A3 跳过 FP8,期望 39/39(skipped 8)。容差 fp16/bf16/int8 为 $1\mathrm{e}{-3}$,FP8 为 $2\mathrm{e}{-2}$。 | ||
| 365 | +- Ascend 950 核实现位于 `op_kernel/arch35/`:当前与 A2 共用 8-page S workspace(非量化 fp16 / int8 fp32);Cube 原生三种 FP8。`--run_example` 默认 soc 为 910b,950 必须显式 `--soc=ascend950`。 | ||
| 134 | 366 | ||
| 135 | 367 | ||
| 136 | ## 调用示例 | 368 | ## 调用示例 |
| @@ -9,7 +9,7 @@ | |||
| 9 | - <term>Atlas A3 训练系列产品/Atlas A3 推理系列产品</term>:支持 | 9 | - <term>Atlas A3 训练系列产品/Atlas A3 推理系列产品</term>:支持 |
| 10 | <!-- end id2 --> | 10 | <!-- end id2 --> |
| 11 | <!-- npu="950" id3 --> | 11 | <!-- npu="950" id3 --> |
| 12 | -- <term>Ascend 950PR/Ascend 950DT</term>:不支持 | 12 | +- <term>Ascend 950PR/Ascend 950DT</term>:支持 |
| 13 | <!-- end id3 --> | 13 | <!-- end id3 --> |
| 14 | 14 | ||
| 15 | ## 功能说明 | 15 | ## 功能说明 |
| @@ -23,6 +23,12 @@ $$ | |||
| 23 | 23 | ||
| 24 | - `sparse_mode=0`:无因果;`sparse_mode=3`:rightDownCausal(须传 `[2048,2048]` `atten_mask`)。 | 24 | - `sparse_mode=0`:无因果;`sparse_mode=3`:rightDownCausal(须传 `[2048,2048]` `atten_mask`)。 |
| 25 | - `start_loc[B]`:当前 query 所在逻辑 block 索引,用于 `local_mask`。 | 25 | - `start_loc[B]`:当前 query 所在逻辑 block 索引,用于 `local_mask`。 |
| 26 | +- `init_blocks` / `local_blocks`:Maxpool 之后的强制高分块;与 Triton raw score 对齐时置 0。默认 `0` / `1`。 | ||
| 27 | +- Ascend 950 额外支持 query/key 同型 `torch.float8_e4m3fn` / `torch.float8_e5m2` / `torch_npu.hifloat8`(无 `scale`)。`hifloat8.npu()` 当前不安全,测试脚本跳过;kernel 已注册。 | ||
| 28 | +- `q_len` / `kv_len` 允许为 0(含整 batch)。对应请求跳过 QK;空 KV 的 score 为 `-inf`。 | ||
| 29 | +- A2/A3 与 Ascend 950 短 decode 按估计 M-task 启动 MIX,不打满空核。 | ||
| 30 | +- PageAttention `key` 允许 dim0(物理 page)非连续,须保持为 view(不要 `.contiguous()`);TND 不允许。 | ||
| 31 | +- PageAttention `block_table` 第二维可以大于实际 KV 逻辑 block 数(例如 vLLM 预分配宽表)。950 C2UB 对 score 末维超过 256 列按窗 flush。 | ||
| 26 | 32 | ||
| 27 | ## 函数原型 | 33 | ## 函数原型 |
| 28 | 34 | ||
| @@ -39,23 +45,116 @@ cann_ops_transformer.msa_index_score( | |||
| 39 | actual_seq_klen=None, | 45 | actual_seq_klen=None, |
| 40 | layout_key="BBND", | 46 | layout_key="BBND", |
| 41 | sparse_mode=3, | 47 | sparse_mode=3, |
| 48 | + init_blocks=0, | ||
| 49 | + local_blocks=1, | ||
| 42 | ) -> Tensor | 50 | ) -> Tensor |
| 43 | ``` | 51 | ``` |
| 44 | 52 | ||
| 45 | ## 参数说明 | 53 | ## 参数说明 |
| 46 | 54 | ||
| 47 | -| 参数名 | 可选/必选 | 描述 | dtype | shape | | 55 | +<table style="undefined;table-layout: fixed; width: 1180px"><colgroup> |
| 48 | -|--------|-----------|------|-------|-------| | 56 | +<col style="width: 160px"> |
| 49 | -| query | 必选 | $Q_{idx}$ TND | fp16/bf16 | `[T1,N1,D]` | | 57 | +<col style="width: 120px"> |
| 50 | -| key | 必选 | $K_{idx}$ PA BBND/BNBD 或 TND | 同 query 或 int8 | `[NP,P,N2,D]` / `[NP,N2,P,D]` / `[T2,N2,D]` | | 58 | +<col style="width: 360px"> |
| 51 | -| start_loc | 必选 | query 所在逻辑 block | int32 | `[B]` | | 59 | +<col style="width: 280px"> |
| 52 | -| block_table | PA 必选 | 逻辑→物理 page;TND 不传 | int32 | `[B,MB]` | | 60 | +<col style="width: 260px"> |
| 53 | -| scale | 量化必选 | 反量化 | float32 | PA `[NP,N2,P]`;TND `[T2,N2]` | | 61 | +</colgroup> |
| 54 | -| atten_mask | mode=3 必选 | 压缩下三角模板 | int8 | `[2048,2048]` | | 62 | +<thead> |
| 55 | -| actual_seq_qlen | TND query 必选 | query 前缀和 | int32 | `[B+1]` | | 63 | + <tr> |
| 56 | -| actual_seq_klen | 必选 | PA 为各请求 S2;TND 为 key 前缀和 | int32 | `[B]` / `[B+1]` | | 64 | + <th>参数名</th> |
| 57 | -| layout_key | 可选 | key 布局:`TND` / `BBND` / `BNBD`,默认 `BBND` | str | - | | 65 | + <th>可选/必选</th> |
| 58 | -| sparse_mode | 可选 | 0 / 3,默认 3 | int | - | | 66 | + <th>描述</th> |
| 67 | + <th>dtype</th> | ||
| 68 | + <th>shape</th> | ||
| 69 | + </tr> | ||
| 70 | +</thead> | ||
| 71 | +<tbody> | ||
| 72 | + <tr> | ||
| 73 | + <td>query</td> | ||
| 74 | + <td>必选</td> | ||
| 75 | + <td>$Q_{idx}$ TND</td> | ||
| 76 | + <td>fp16/bf16;950 另支持 fp8_e4m3fn / fp8_e5m2 / hifloat8</td> | ||
| 77 | + <td><code>[T1,N1,D]</code></td> | ||
| 78 | + </tr> | ||
| 79 | + <tr> | ||
| 80 | + <td>key</td> | ||
| 81 | + <td>必选</td> | ||
| 82 | + <td>$K_{idx}$ PA BBND/BNBD 或 TND。A2/A3 与 950 上 PA 允许 dim0 非连续。</td> | ||
| 83 | + <td>同 query 或 int8(int8 配 fp16 query)</td> | ||
| 84 | + <td><code>[NP,P,N2,D]</code> / <code>[NP,N2,P,D]</code> / <code>[T2,N2,D]</code></td> | ||
| 85 | + </tr> | ||
| 86 | + <tr> | ||
| 87 | + <td>start_loc</td> | ||
| 88 | + <td>必选</td> | ||
| 89 | + <td>query 所在逻辑 block</td> | ||
| 90 | + <td>int32</td> | ||
| 91 | + <td><code>[B]</code></td> | ||
| 92 | + </tr> | ||
| 93 | + <tr> | ||
| 94 | + <td>block_table</td> | ||
| 95 | + <td>PA 必选</td> | ||
| 96 | + <td>逻辑→物理 page;TND 不传</td> | ||
| 97 | + <td>int32</td> | ||
| 98 | + <td><code>[B,MB]</code></td> | ||
| 99 | + </tr> | ||
| 100 | + <tr> | ||
| 101 | + <td>scale</td> | ||
| 102 | + <td>量化必选</td> | ||
| 103 | + <td>反量化</td> | ||
| 104 | + <td>float32</td> | ||
| 105 | + <td>PA <code>[NP,N2,P]</code>;TND <code>[T2,N2]</code></td> | ||
| 106 | + </tr> | ||
| 107 | + <tr> | ||
| 108 | + <td>atten_mask</td> | ||
| 109 | + <td>mode=3 必选</td> | ||
| 110 | + <td>压缩下三角模板</td> | ||
| 111 | + <td>int8</td> | ||
| 112 | + <td><code>[2048,2048]</code></td> | ||
| 113 | + </tr> | ||
| 114 | + <tr> | ||
| 115 | + <td>actual_seq_qlen</td> | ||
| 116 | + <td>TND query 必选</td> | ||
| 117 | + <td>query 前缀和</td> | ||
| 118 | + <td>int32</td> | ||
| 119 | + <td><code>[B+1]</code></td> | ||
| 120 | + </tr> | ||
| 121 | + <tr> | ||
| 122 | + <td>actual_seq_klen</td> | ||
| 123 | + <td>必选</td> | ||
| 124 | + <td>PA 为各请求 S2;TND 为 key 前缀和</td> | ||
| 125 | + <td>int32</td> | ||
| 126 | + <td><code>[B]</code> / <code>[B+1]</code></td> | ||
| 127 | + </tr> | ||
| 128 | + <tr> | ||
| 129 | + <td>layout_key</td> | ||
| 130 | + <td>可选</td> | ||
| 131 | + <td>key 布局:<code>TND</code> / <code>BBND</code> / <code>BNBD</code>,默认 <code>BBND</code></td> | ||
| 132 | + <td>str</td> | ||
| 133 | + <td>-</td> | ||
| 134 | + </tr> | ||
| 135 | + <tr> | ||
| 136 | + <td>sparse_mode</td> | ||
| 137 | + <td>可选</td> | ||
| 138 | + <td>0 / 3,默认 3</td> | ||
| 139 | + <td>int</td> | ||
| 140 | + <td>-</td> | ||
| 141 | + </tr> | ||
| 142 | + <tr> | ||
| 143 | + <td>init_blocks</td> | ||
| 144 | + <td>可选</td> | ||
| 145 | + <td><code>local_mask</code> 头部强制块数,默认 0</td> | ||
| 146 | + <td>int</td> | ||
| 147 | + <td>-</td> | ||
| 148 | + </tr> | ||
| 149 | + <tr> | ||
| 150 | + <td>local_blocks</td> | ||
| 151 | + <td>可选</td> | ||
| 152 | + <td><code>local_mask</code> 局部窗口,默认 1</td> | ||
| 153 | + <td>int</td> | ||
| 154 | + <td>-</td> | ||
| 155 | + </tr> | ||
| 156 | +</tbody> | ||
| 157 | +</table> | ||
| 59 | 158 | ||
| 60 | ## 输出 | 159 | ## 输出 |
| 61 | 160 | ||
| @@ -13,12 +13,16 @@ | |||
| 13 | * \brief aclnnMsaIndexScore 调用示例,内置 CPU golden 做端到端精度自验证。 | 13 | * \brief aclnnMsaIndexScore 调用示例,内置 CPU golden 做端到端精度自验证。 |
| 14 | * | 14 | * |
| 15 | * 用例矩阵覆盖:Prefill 多 M-tile、prefix 非 128 对齐的边界 block、varlen 多 batch、 | 15 | * 用例矩阵覆盖:Prefill 多 M-tile、prefix 非 128 对齐的边界 block、varlen 多 batch、 |
| 16 | - * Decode(q_len=1)、投机解码(q_len>1)、长序列多 S-tile 轮转、block_table 乱序、 | 16 | + * Decode(q_len=1)、投机解码(q_len>1)、短 decode(Hq=4 / 宽 block_table)、 |
| 17 | - * 无效尾填充、bf16 / fp16 双 dtype、int8 key 前融合反量化、PA BNBD、TND packed key。 | 17 | + * 长序列多 S-tile 轮转、block_table 乱序、 |
| 18 | + * 无效尾填充、q_len/kv_len=0 的 mixed-batch pad、bf16 / fp16 双 dtype、 | ||
| 19 | + * int8 key 前融合反量化、PA BNBD、TND packed key、A2/A3 与 950 PA key dim0 非连续。 | ||
| 18 | */ | 20 | */ |
| 19 | 21 | ||
| 20 | #include <algorithm> | 22 | #include <algorithm> |
| 21 | #include <cmath> | 23 | #include <cmath> |
| 24 | +#include <cfenv> | ||
| 25 | +#include <fenv.h> | ||
| 22 | #include <cstdint> | 26 | #include <cstdint> |
| 23 | #include <cstdio> | 27 | #include <cstdio> |
| 24 | #include <cstdlib> | 28 | #include <cstdlib> |
| @@ -13,11 +13,8 @@ | |||
| 13 | * \brief MsaIndexScore 算子原型注册(对齐 docs/aclnnMsaIndexScore.md)。 | 13 | * \brief MsaIndexScore 算子原型注册(对齐 docs/aclnnMsaIndexScore.md)。 |
| 14 | * | 14 | * |
| 15 | * dtype 组合(按列表下标对齐): | 15 | * dtype 组合(按列表下标对齐): |
| 16 | - * 0: query=bf16, key=bf16 (非量化) | 16 | + * A2/A3(3 组,全 ND):0 bf16/bf16、1 fp16/fp16、2 fp16/int8 |
| 17 | - * 1: query=fp16, key=fp16 (非量化) | 17 | + * Ascend 950(6 组):0–2 同上(ND),3 hifloat8、4 fp8_e5m2、5 fp8_e4m3fn |
| 18 | - * 2: query=fp16, key=int8 (前融合 int8,scale 为反量化系数) | ||
| 19 | - * | ||
| 20 | - * A2/A3:PageAttention BBND/BNBD key 与 TND packed key;sparse_mode∈{0,3}。不支持 FP8 / Ascend 950。 | ||
| 21 | */ | 18 | */ |
| 22 | 19 | ||
| 23 | #include <cstdint> | 20 | #include <cstdint> |
| @@ -36,11 +33,12 @@ public: | |||
| 36 | .Format({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND}) | 33 | .Format({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND}) |
| 37 | .AutoContiguous(); | 34 | .AutoContiguous(); |
| 38 | // K_idx:BBND [block_num, block_size, N2, D]、BNBD [block_num, N2, block_size, D]、TND [T2, N2, D] | 35 | // K_idx:BBND [block_num, block_size, N2, D]、BNBD [block_num, N2, block_size, D]、TND [T2, N2, D] |
| 36 | + // PA key 允许 dim0(物理 page)非连续;TND 仍须连续(tiling 拒绝)。 | ||
| 39 | this->Input("key") | 37 | this->Input("key") |
| 40 | .ParamType(REQUIRED) | 38 | .ParamType(REQUIRED) |
| 41 | .DataType({ge::DT_BF16, ge::DT_FLOAT16, ge::DT_INT8}) | 39 | .DataType({ge::DT_BF16, ge::DT_FLOAT16, ge::DT_INT8}) |
| 42 | .Format({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND}) | 40 | .Format({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND}) |
| 43 | - .AutoContiguous(); | 41 | + .IgnoreContiguous(); |
| 44 | // PA 逻辑 block → 物理 page;PA 场景必须传入 | 42 | // PA 逻辑 block → 物理 page;PA 场景必须传入 |
| 45 | this->Input("block_table") | 43 | this->Input("block_table") |
| 46 | .ParamType(OPTIONAL) | 44 | .ParamType(OPTIONAL) |
| @@ -98,9 +96,63 @@ public: | |||
| 98 | .DynamicShapeSupportFlag(true) | 96 | .DynamicShapeSupportFlag(true) |
| 99 | .NeedCheckSupportFlag(false) | 97 | .NeedCheckSupportFlag(false) |
| 100 | .PrecisionReduceFlag(true); | 98 | .PrecisionReduceFlag(true); |
| 101 | - // Atlas A2(ascend910b)/ A3(ascend910_93);不注册 Ascend 950。 | ||
| 102 | this->AICore().AddConfig("ascend910b", aicoreConfig); | 99 | this->AICore().AddConfig("ascend910b", aicoreConfig); |
| 103 | this->AICore().AddConfig("ascend910_93", aicoreConfig); | 100 | this->AICore().AddConfig("ascend910_93", aicoreConfig); |
| 101 | + | ||
| 102 | + OpAICoreConfig aicoreConfig950; | ||
| 103 | + aicoreConfig950.Input("query") | ||
| 104 | + .ParamType(REQUIRED) | ||
| 105 | + .DataType({ge::DT_BF16, ge::DT_FLOAT16, ge::DT_FLOAT16, ge::DT_HIFLOAT8, ge::DT_FLOAT8_E5M2, | ||
| 106 | + ge::DT_FLOAT8_E4M3FN}) | ||
| 107 | + .Format({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND}) | ||
| 108 | + .AutoContiguous(); | ||
| 109 | + aicoreConfig950.Input("key") | ||
| 110 | + .ParamType(REQUIRED) | ||
| 111 | + .DataType( | ||
| 112 | + {ge::DT_BF16, ge::DT_FLOAT16, ge::DT_INT8, ge::DT_HIFLOAT8, ge::DT_FLOAT8_E5M2, ge::DT_FLOAT8_E4M3FN}) | ||
| 113 | + .Format({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND}) | ||
| 114 | + .IgnoreContiguous(); | ||
| 115 | + aicoreConfig950.Input("block_table") | ||
| 116 | + .ParamType(OPTIONAL) | ||
| 117 | + .DataType({ge::DT_INT32, ge::DT_INT32, ge::DT_INT32, ge::DT_INT32, ge::DT_INT32, ge::DT_INT32}) | ||
| 118 | + .Format({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND}) | ||
| 119 | + .AutoContiguous(); | ||
| 120 | + aicoreConfig950.Input("scale") | ||
| 121 | + .ParamType(OPTIONAL) | ||
| 122 | + .DataType({ge::DT_FLOAT, ge::DT_FLOAT, ge::DT_FLOAT, ge::DT_FLOAT, ge::DT_FLOAT, ge::DT_FLOAT}) | ||
| 123 | + .Format({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND}) | ||
| 124 | + .AutoContiguous(); | ||
| 125 | + aicoreConfig950.Input("atten_mask") | ||
| 126 | + .ParamType(OPTIONAL) | ||
| 127 | + .DataType({ge::DT_INT8, ge::DT_INT8, ge::DT_INT8, ge::DT_INT8, ge::DT_INT8, ge::DT_INT8}) | ||
| 128 | + .Format({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND}) | ||
| 129 | + .AutoContiguous(); | ||
| 130 | + aicoreConfig950.Input("actual_seq_qlen") | ||
| 131 | + .ParamType(OPTIONAL) | ||
| 132 | + .DataType({ge::DT_INT32, ge::DT_INT32, ge::DT_INT32, ge::DT_INT32, ge::DT_INT32, ge::DT_INT32}) | ||
| 133 | + .Format({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND}) | ||
| 134 | + .AutoContiguous(); | ||
| 135 | + aicoreConfig950.Input("actual_seq_klen") | ||
| 136 | + .ParamType(OPTIONAL) | ||
| 137 | + .DataType({ge::DT_INT32, ge::DT_INT32, ge::DT_INT32, ge::DT_INT32, ge::DT_INT32, ge::DT_INT32}) | ||
| 138 | + .Format({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND}) | ||
| 139 | + .AutoContiguous(); | ||
| 140 | + aicoreConfig950.Input("start_loc") | ||
| 141 | + .ParamType(REQUIRED) | ||
| 142 | + .DataType({ge::DT_INT32, ge::DT_INT32, ge::DT_INT32, ge::DT_INT32, ge::DT_INT32, ge::DT_INT32}) | ||
| 143 | + .Format({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND}) | ||
| 144 | + .AutoContiguous(); | ||
| 145 | + aicoreConfig950.Output("score") | ||
| 146 | + .ParamType(REQUIRED) | ||
| 147 | + .DataType({ge::DT_FLOAT, ge::DT_FLOAT, ge::DT_FLOAT, ge::DT_FLOAT, ge::DT_FLOAT, ge::DT_FLOAT}) | ||
| 148 | + .Format({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND}); | ||
| 149 | + aicoreConfig950.DynamicCompileStaticFlag(true) | ||
| 150 | + .DynamicFormatFlag(true) | ||
| 151 | + .DynamicRankSupportFlag(true) | ||
| 152 | + .DynamicShapeSupportFlag(true) | ||
| 153 | + .NeedCheckSupportFlag(false) | ||
| 154 | + .PrecisionReduceFlag(true); | ||
| 155 | + this->AICore().AddConfig("ascend950", aicoreConfig950); | ||
| 104 | } | 156 | } |
| 105 | }; | 157 | }; |
| 106 | OP_ADD(MsaIndexScore); | 158 | OP_ADD(MsaIndexScore); |
| @@ -36,7 +36,10 @@ constexpr uint32_t MSA_ATTR_LAYOUT_KEY = 0; | |||
| 36 | constexpr int64_t MSA_SCORE_STRIDE_ALIGN = 16; | 36 | constexpr int64_t MSA_SCORE_STRIDE_ALIGN = 16; |
| 37 | constexpr int64_t MSA_BLOCK_SIZE = 128; | 37 | constexpr int64_t MSA_BLOCK_SIZE = 128; |
| 38 | 38 | ||
| 39 | -inline int64_t RoundUpTo(int64_t value, int64_t align) { return (value + align - 1) / align * align; } | 39 | +inline int64_t RoundUpTo(int64_t value, int64_t align) |
| 40 | +{ | ||
| 41 | + return (value + align - 1) / align * align; | ||
| 42 | +} | ||
| 40 | } // namespace | 43 | } // namespace |
| 41 | 44 | ||
| 42 | static ge::graphStatus InferShapeMsaIndexScore(gert::InferShapeContext *context) | 45 | static ge::graphStatus InferShapeMsaIndexScore(gert::InferShapeContext *context) |
| @@ -95,7 +98,11 @@ static ge::graphStatus InferShapeMsaIndexScore(gert::InferShapeContext *context) | |||
| 95 | maxBlocks = (totalK + MSA_BLOCK_SIZE - 1) / MSA_BLOCK_SIZE; | 98 | maxBlocks = (totalK + MSA_BLOCK_SIZE - 1) / MSA_BLOCK_SIZE; |
| 96 | } | 99 | } |
| 97 | } | 100 | } |
| 98 | - OP_CHECK_IF(maxBlocks <= 0, OP_LOGE(context, "maxBlocksPerSeq must be positive."), return ge::GRAPH_FAILED); | 101 | + // 不拦截全 kv_len=0:按 1 个 dummy block 再 16 对齐,score 末维为 16; |
| 102 | + // 核内跳过 QK,把该请求的 score 填 -inf(T1=0 时输出为空张量)。 | ||
| 103 | + if (maxBlocks <= 0) { | ||
| 104 | + maxBlocks = 1; | ||
| 105 | + } | ||
| 99 | 106 | ||
| 100 | // maxpool 已经把一个 block 内的 blockSize 个 token 归约成 1 个分数, | 107 | // maxpool 已经把一个 block 内的 blockSize 个 token 归约成 1 个分数, |
| 101 | // 因此输出末维是 block 数(16 对齐)而不是 kv token 数。 | 108 | // 因此输出末维是 block 数(16 对齐)而不是 kv token 数。 |
| @@ -10,13 +10,16 @@ | |||
| 10 | 10 | ||
| 11 | /*! | 11 | /*! |
| 12 | * \file msa_index_score_tiling.cpp | 12 | * \file msa_index_score_tiling.cpp |
| 13 | - * \brief MsaIndexScore Tiling 实现(A2/A3:PA BBND/BNBD + TND packed key;sparse_mode 0/3)。 | 13 | + * \brief MsaIndexScore Tiling 实现(A2/A3 / 950:PA BBND/BNBD + TND packed key;sparse_mode 0/3; |
| 14 | + * PA key 允许 dim0 非连续,strideKvBlock 来自 GetInputStride)。 | ||
| 14 | */ | 15 | */ |
| 15 | 16 | ||
| 16 | #include "msa_index_score_tiling.h" | 17 | #include "msa_index_score_tiling.h" |
| 17 | #include "../op_kernel/msa_index_score_common.h" | 18 | #include "../op_kernel/msa_index_score_common.h" |
| 18 | 19 | ||
| 20 | +#include <limits> | ||
| 19 | #include <string> | 21 | #include <string> |
| 22 | +#include "graph/utils/type_utils.h" | ||
| 20 | 23 | ||
| 21 | using namespace ge; | 24 | using namespace ge; |
| 22 | using namespace MsaIndexScoreNs; | 25 | using namespace MsaIndexScoreNs; |
| @@ -37,7 +40,151 @@ constexpr uint32_t MSA_DIM_3 = 3; | |||
| 37 | constexpr uint32_t MSA_RANK_1D = 1; | 40 | constexpr uint32_t MSA_RANK_1D = 1; |
| 38 | constexpr uint32_t MSA_QLEN_MIN_DIM0 = 2; // prefix-sum 长度至少为 B+1 | 41 | constexpr uint32_t MSA_QLEN_MIN_DIM0 = 2; // prefix-sum 长度至少为 B+1 |
| 39 | 42 | ||
| 40 | -inline uint32_t RoundUpU32(uint32_t value, uint32_t align) { return (value + align - 1) / align * align; } | 43 | +inline bool IsFp8ComputeDtype(ge::DataType dt) |
| 44 | +{ | ||
| 45 | + return dt == ge::DT_HIFLOAT8 || dt == ge::DT_FLOAT8_E5M2 || dt == ge::DT_FLOAT8_E4M3FN; | ||
| 46 | +} | ||
| 47 | + | ||
| 48 | +inline bool IsNonQuantQueryDtype(ge::DataType dt, bool isAscend950) | ||
| 49 | +{ | ||
| 50 | + if (dt == ge::DT_BF16 || dt == ge::DT_FLOAT16) { | ||
| 51 | + return true; | ||
| 52 | + } | ||
| 53 | + return isAscend950 && IsFp8ComputeDtype(dt); | ||
| 54 | +} | ||
| 55 | + | ||
| 56 | +inline uint32_t RoundUpU32(uint32_t value, uint32_t align) | ||
| 57 | +{ | ||
| 58 | + return (value + align - 1) / align * align; | ||
| 59 | +} | ||
| 60 | + | ||
| 61 | +// MIX:按估计 M-task 数启动 AIC,避免短 decode 打满空核(scheduler.Init GetValue / MODE4 VC 空转)。 | ||
| 62 | +// Host 读不到 per-request q_len。kernel TotalTasks = Σ CeilDiv(qLen_b * Hq, MSA_ROW_TILE_M)。 | ||
| 63 | +// Σ ceil(x_i) <= ceil(Σ x_i) + B;B=1 时 packed == actual。B>1 用 packed+B 上界,避免少估把 | ||
| 64 | +// 多 tile 请求串到已启动核上。多 M-tile / 大 batch 仍截到全量 AIC。 | ||
| 65 | +inline uint32_t EstLaunchAic(const MsaIndexScoreInfo &info, uint32_t aicNum) | ||
| 66 | +{ | ||
| 67 | + if (info.totalQ == 0U || aicNum == 0U) { | ||
| 68 | + return 1U; | ||
| 69 | + } | ||
| 70 | + const uint64_t packedRows = static_cast<uint64_t>(info.totalQ) * static_cast<uint64_t>(info.numQHeads); | ||
| 71 | + const uint32_t packedM = | ||
| 72 | + static_cast<uint32_t>((packedRows + static_cast<uint64_t>(MSA_ROW_TILE_M) - 1U) / MSA_ROW_TILE_M); | ||
| 73 | + uint32_t est = packedM; | ||
| 74 | + if (info.batch > 1U) { | ||
| 75 | + const uint32_t add = packedM + info.batch; | ||
| 76 | + est = (add < packedM) ? aicNum : add; | ||
| 77 | + } | ||
| 78 | + if (est < 1U) { | ||
| 79 | + est = 1U; | ||
| 80 | + } | ||
| 81 | + if (est > aicNum) { | ||
| 82 | + est = aicNum; | ||
| 83 | + } | ||
| 84 | + return est; | ||
| 85 | +} | ||
| 86 | + | ||
| 87 | +inline uint64_t GetDefaultStride0(const gert::Shape &shape) | ||
| 88 | +{ | ||
| 89 | + uint64_t stride = 1; | ||
| 90 | + for (size_t dim = 1; dim < shape.GetDimNum(); ++dim) { | ||
| 91 | + stride *= static_cast<uint64_t>(shape.GetDim(dim)); | ||
| 92 | + } | ||
| 93 | + return stride; | ||
| 94 | +} | ||
| 95 | + | ||
| 96 | +// 逻辑 view:优先 OriginShape(aclCreateTensor 的 viewDims / torch sizes)。 | ||
| 97 | +// StorageShape 在 dim0 插空时可能是 [NP*gap, ...],不能当 numPages。 | ||
| 98 | +inline const gert::Shape &KeyLogicalShape(const gert::StorageShape *keyShape, uint32_t keyLayout) | ||
| 99 | +{ | ||
| 100 | + const size_t expectRank = (keyLayout == MSA_KEY_LAYOUT_TND) ? MSA_KEY_TND_DIM_NUM : MSA_KEY_PA_DIM_NUM; | ||
| 101 | + const gert::Shape &origin = keyShape->GetOriginShape(); | ||
| 102 | + if (origin.GetDimNum() == expectRank) { | ||
| 103 | + return origin; | ||
| 104 | + } | ||
| 105 | + return keyShape->GetStorageShape(); | ||
| 106 | +} | ||
| 107 | + | ||
| 108 | +// aclnn TensorV2 优先 GetRequiredInputStride; | ||
| 109 | +// 文档接口 GetInputStride 作为兼容回退。 | ||
| 110 | +inline const gert::Stride *GetKeyStrideDesc(gert::TilingContext *context) | ||
| 111 | +{ | ||
| 112 | + const gert::Stride *stride = context->GetRequiredInputStride(MSA_IDX_KEY); | ||
| 113 | + if (stride != nullptr) { | ||
| 114 | + return stride; | ||
| 115 | + } | ||
| 116 | + return context->GetInputStride(MSA_IDX_KEY); | ||
| 117 | +} | ||
| 118 | + | ||
| 119 | +inline bool ValidateKeyInnerAxesContiguous(gert::TilingContext *context, const gert::Stride *stride, | ||
| 120 | + const gert::Shape &shape) | ||
| 121 | +{ | ||
| 122 | + if (stride == nullptr || stride->GetDimNum() != shape.GetDimNum()) { | ||
| 123 | + return true; | ||
| 124 | + } | ||
| 125 | + uint64_t expectedStride = 1; | ||
| 126 | + for (int64_t i = static_cast<int64_t>(shape.GetDimNum()) - 1; i >= 1; --i) { | ||
| 127 | + const int64_t dimSize = shape.GetDim(static_cast<size_t>(i)); | ||
| 128 | + if (dimSize <= 1) { | ||
| 129 | + // size-1 轴不参与寻址;PyTorch 对其 stride 不做连续约束(BNBD 且 N2=1 时常见)。 | ||
| 130 | + continue; | ||
| 131 | + } | ||
| 132 | + const uint64_t actualStride = static_cast<uint64_t>(stride->GetStride(static_cast<size_t>(i))); | ||
| 133 | + if (actualStride != expectedStride) { | ||
| 134 | + OP_LOGE(context, | ||
| 135 | + "key dim%ld must be contiguous, actual stride=%lu, expected=%lu. " | ||
| 136 | + "Only dim0 (PA page axis) may be non-contiguous.", | ||
| 137 | + i, actualStride, expectedStride); | ||
| 138 | + return false; | ||
| 139 | + } | ||
| 140 | + expectedStride *= static_cast<uint64_t>(dimSize); | ||
| 141 | + } | ||
| 142 | + return true; | ||
| 143 | +} | ||
| 144 | + | ||
| 145 | +// PA:strideKvBlock 取 key dim0 元素 stride。连续输入写入值与 shape 推算相同。 | ||
| 146 | +inline ge::graphStatus ResolveStrideKvBlock(gert::TilingContext *context, const MsaIndexScoreInfo &info, | ||
| 147 | + uint32_t defaultStrideKvBlock, uint32_t &strideKvBlock) | ||
| 148 | +{ | ||
| 149 | + strideKvBlock = defaultStrideKvBlock; | ||
| 150 | + | ||
| 151 | + const gert::StorageShape *keyShape = context->GetInputShape(MSA_IDX_KEY); | ||
| 152 | + OP_CHECK_NULL_WITH_CONTEXT(context, keyShape); | ||
| 153 | + const gert::Shape &kc = KeyLogicalShape(keyShape, info.keyLayout); | ||
| 154 | + if (kc.GetShapeSize() == 0) { | ||
| 155 | + return ge::GRAPH_SUCCESS; | ||
| 156 | + } | ||
| 157 | + | ||
| 158 | + const uint64_t defaultStride0 = GetDefaultStride0(kc); | ||
| 159 | + const gert::Stride *stride = GetKeyStrideDesc(context); | ||
| 160 | + if (stride == nullptr || stride->GetDimNum() != kc.GetDimNum()) { | ||
| 161 | + OP_LOGD(context->GetNodeName(), "key has no stride desc, treat as contiguous, strideKvBlock=%u.", | ||
| 162 | + strideKvBlock); | ||
| 163 | + return ge::GRAPH_SUCCESS; | ||
| 164 | + } | ||
| 165 | + OP_CHECK_IF(!ValidateKeyInnerAxesContiguous(context, stride, kc), | ||
| 166 | + OP_LOGE(context, "key inner axes must stay contiguous."), return ge::GRAPH_FAILED); | ||
| 167 | + | ||
| 168 | + const int64_t actualStride0 = stride->GetStride(MSA_DIM_0); | ||
| 169 | + if (info.keyLayout == MSA_KEY_LAYOUT_TND) { | ||
| 170 | + OP_CHECK_IF(actualStride0 < 0 || static_cast<uint64_t>(actualStride0) != defaultStride0, | ||
| 171 | + OP_LOGE(context, "TND key dim0 must be contiguous, actual=%ld expected=%lu.", actualStride0, | ||
| 172 | + defaultStride0), | ||
| 173 | + return ge::GRAPH_FAILED); | ||
| 174 | + strideKvBlock = 0; | ||
| 175 | + return ge::GRAPH_SUCCESS; | ||
| 176 | + } | ||
| 177 | + | ||
| 178 | + OP_CHECK_IF(actualStride0 < 0 || static_cast<uint64_t>(actualStride0) < defaultStride0, | ||
| 179 | + OP_LOGE(context, "PA key dim0 stride must be >= %lu, got %ld.", defaultStride0, actualStride0), | ||
| 180 | + return ge::GRAPH_FAILED); | ||
| 181 | + OP_CHECK_IF(static_cast<uint64_t>(actualStride0) > static_cast<uint64_t>(std::numeric_limits<uint32_t>::max()), | ||
| 182 | + OP_LOGE(context, "PA key dim0 stride %ld exceeds uint32 strideKvBlock.", actualStride0), | ||
| 183 | + return ge::GRAPH_FAILED); | ||
| 184 | + strideKvBlock = static_cast<uint32_t>(actualStride0); | ||
| 185 | + OP_LOGI(context->GetNodeName(), "PA key strideKvBlock=%u (contiguous default=%lu).", strideKvBlock, defaultStride0); | ||
| 186 | + return ge::GRAPH_SUCCESS; | ||
| 187 | +} | ||
| 41 | 188 | ||
| 42 | ge::graphStatus ParseLayoutKeyAttr(gert::TilingContext *context, const std::string &layoutKey, uint32_t &keyLayout) | 189 | ge::graphStatus ParseLayoutKeyAttr(gert::TilingContext *context, const std::string &layoutKey, uint32_t &keyLayout) |
| 43 | { | 190 | { |
| @@ -59,6 +206,8 @@ ge::graphStatus ParseAndCheck(gert::TilingContext *context, MsaIndexScoreInfo &i | |||
| 59 | { | 206 | { |
| 60 | info.platformInfo = context->GetPlatformInfo(); | 207 | info.platformInfo = context->GetPlatformInfo(); |
| 61 | OP_CHECK_IF(info.platformInfo == nullptr, OP_LOGE(context, "GetPlatformInfo is nullptr."), return ge::GRAPH_FAILED); | 208 | OP_CHECK_IF(info.platformInfo == nullptr, OP_LOGE(context, "GetPlatformInfo is nullptr."), return ge::GRAPH_FAILED); |
| 209 | + auto ascendcPlatformEarly = platform_ascendc::PlatformAscendC(info.platformInfo); | ||
| 210 | + info.isAscend950 = (ascendcPlatformEarly.GetSocVersion() == platform_ascendc::SocVersion::ASCEND950); | ||
| 62 | 211 | ||
| 63 | const gert::StorageShape *queryShape = context->GetInputShape(MSA_IDX_QUERY); | 212 | const gert::StorageShape *queryShape = context->GetInputShape(MSA_IDX_QUERY); |
| 64 | OP_CHECK_NULL_WITH_CONTEXT(context, queryShape); | 213 | OP_CHECK_NULL_WITH_CONTEXT(context, queryShape); |
| @@ -75,10 +224,15 @@ ge::graphStatus ParseAndCheck(gert::TilingContext *context, MsaIndexScoreInfo &i | |||
| 75 | OP_CHECK_NULL_WITH_CONTEXT(context, startLocShape); | 224 | OP_CHECK_NULL_WITH_CONTEXT(context, startLocShape); |
| 76 | 225 | ||
| 77 | const gert::Shape &q = queryShape->GetStorageShape(); | 226 | const gert::Shape &q = queryShape->GetStorageShape(); |
| 78 | - const gert::Shape &kc = keyShape->GetStorageShape(); | ||
| 79 | const gert::Shape &qlen = actualSeqQlenShape->GetStorageShape(); | 227 | const gert::Shape &qlen = actualSeqQlenShape->GetStorageShape(); |
| 80 | const gert::Shape &klen = actualSeqKlenShape->GetStorageShape(); | 228 | const gert::Shape &klen = actualSeqKlenShape->GetStorageShape(); |
| 81 | 229 | ||
| 230 | + const auto *keyDesc = context->GetInputDesc(MSA_IDX_KEY); | ||
| 231 | + OP_CHECK_NULL_WITH_CONTEXT(context, keyDesc); | ||
| 232 | + info.keyDtype = keyDesc->GetDataType(); | ||
| 233 | + OP_CHECK_IF(static_cast<ge::Format>(ge::GetPrimaryFormat(keyDesc->GetStorageFormat())) == ge::FORMAT_FRACTAL_NZ, | ||
| 234 | + OP_LOGE(context, "FRACTAL_NZ key is not supported."), return ge::GRAPH_FAILED); | ||
| 235 | + | ||
| 82 | OP_CHECK_IF(q.GetDimNum() != MSA_QUERY_DIM_NUM, | 236 | OP_CHECK_IF(q.GetDimNum() != MSA_QUERY_DIM_NUM, |
| 83 | OP_LOGE(context, "query must be TND(3 dims), got %zu.", q.GetDimNum()), return ge::GRAPH_FAILED); | 237 | OP_LOGE(context, "query must be TND(3 dims), got %zu.", q.GetDimNum()), return ge::GRAPH_FAILED); |
| 84 | OP_CHECK_IF(qlen.GetDimNum() != MSA_RANK_1D || qlen.GetDim(MSA_DIM_0) < MSA_QLEN_MIN_DIM0, | 238 | OP_CHECK_IF(qlen.GetDimNum() != MSA_RANK_1D || qlen.GetDim(MSA_DIM_0) < MSA_QLEN_MIN_DIM0, |
| @@ -96,6 +250,7 @@ ge::graphStatus ParseAndCheck(gert::TilingContext *context, MsaIndexScoreInfo &i | |||
| 96 | if (ParseLayoutKeyAttr(context, layoutKey, info.keyLayout) != ge::GRAPH_SUCCESS) { | 250 | if (ParseLayoutKeyAttr(context, layoutKey, info.keyLayout) != ge::GRAPH_SUCCESS) { |
| 97 | return ge::GRAPH_FAILED; | 251 | return ge::GRAPH_FAILED; |
| 98 | } | 252 | } |
| 253 | + const gert::Shape &kc = KeyLogicalShape(keyShape, info.keyLayout); | ||
| 99 | 254 | ||
| 100 | if (info.keyLayout == MSA_KEY_LAYOUT_TND) { | 255 | if (info.keyLayout == MSA_KEY_LAYOUT_TND) { |
| 101 | info.totalK = static_cast<uint32_t>(kc.GetDim(MSA_DIM_0)); | 256 | info.totalK = static_cast<uint32_t>(kc.GetDim(MSA_DIM_0)); |
| @@ -114,10 +269,6 @@ ge::graphStatus ParseAndCheck(gert::TilingContext *context, MsaIndexScoreInfo &i | |||
| 114 | OP_CHECK_IF(klen.GetDimNum() != MSA_RANK_1D || static_cast<uint32_t>(klen.GetDim(MSA_DIM_0)) != info.batch + 1, | 269 | OP_CHECK_IF(klen.GetDimNum() != MSA_RANK_1D || static_cast<uint32_t>(klen.GetDim(MSA_DIM_0)) != info.batch + 1, |
| 115 | OP_LOGE(context, "TND actual_seq_klen must be [B+1] prefix-sum."), return ge::GRAPH_FAILED); | 270 | OP_LOGE(context, "TND actual_seq_klen must be [B+1] prefix-sum."), return ge::GRAPH_FAILED); |
| 116 | } else { | 271 | } else { |
| 117 | - OP_CHECK_IF(kc.GetDimNum() != MSA_KEY_PA_DIM_NUM, | ||
| 118 | - OP_LOGE(context, "layout_key=%s requires key rank 4, got %zu.", | ||
| 119 | - info.keyLayout == MSA_KEY_LAYOUT_BNBD ? "BNBD" : "BBND", kc.GetDimNum()), | ||
| 120 | - return ge::GRAPH_FAILED); | ||
| 121 | OP_CHECK_IF(blockTableShape == nullptr, OP_LOGE(context, "PageAttention (BBND/BNBD) requires block_table."), | 272 | OP_CHECK_IF(blockTableShape == nullptr, OP_LOGE(context, "PageAttention (BBND/BNBD) requires block_table."), |
| 122 | return ge::GRAPH_FAILED); | 273 | return ge::GRAPH_FAILED); |
| 123 | const gert::Shape &bt = blockTableShape->GetStorageShape(); | 274 | const gert::Shape &bt = blockTableShape->GetStorageShape(); |
| @@ -129,6 +280,10 @@ ge::graphStatus ParseAndCheck(gert::TilingContext *context, MsaIndexScoreInfo &i | |||
| 129 | bt.GetDim(MSA_DIM_0), info.batch), | 280 | bt.GetDim(MSA_DIM_0), info.batch), |
| 130 | return ge::GRAPH_FAILED); | 281 | return ge::GRAPH_FAILED); |
| 131 | info.maxBlocksPerBatch = static_cast<uint32_t>(bt.GetDim(MSA_DIM_1)); | 282 | info.maxBlocksPerBatch = static_cast<uint32_t>(bt.GetDim(MSA_DIM_1)); |
| 283 | + OP_CHECK_IF(kc.GetDimNum() != MSA_KEY_PA_DIM_NUM, | ||
| 284 | + OP_LOGE(context, "layout_key=%s requires key rank 4, got %zu.", | ||
| 285 | + info.keyLayout == MSA_KEY_LAYOUT_BNBD ? "BNBD" : "BBND", kc.GetDimNum()), | ||
| 286 | + return ge::GRAPH_FAILED); | ||
| 132 | info.numPages = static_cast<uint32_t>(kc.GetDim(MSA_DIM_0)); | 287 | info.numPages = static_cast<uint32_t>(kc.GetDim(MSA_DIM_0)); |
| 133 | if (info.keyLayout == MSA_KEY_LAYOUT_BBND) { | 288 | if (info.keyLayout == MSA_KEY_LAYOUT_BBND) { |
| 134 | info.blockSize = static_cast<uint32_t>(kc.GetDim(MSA_DIM_1)); | 289 | info.blockSize = static_cast<uint32_t>(kc.GetDim(MSA_DIM_1)); |
| @@ -176,8 +331,11 @@ ge::graphStatus ParseAndCheck(gert::TilingContext *context, MsaIndexScoreInfo &i | |||
| 176 | OP_CHECK_IF(info.numKvHeads == 0 || (info.numQHeads % info.numKvHeads) != 0, | 331 | OP_CHECK_IF(info.numKvHeads == 0 || (info.numQHeads % info.numKvHeads) != 0, |
| 177 | OP_LOGE(context, "numQHeads(%u) must be divisible by numKvHeads(%u).", info.numQHeads, info.numKvHeads), | 332 | OP_LOGE(context, "numQHeads(%u) must be divisible by numKvHeads(%u).", info.numQHeads, info.numKvHeads), |
| 178 | return ge::GRAPH_FAILED); | 333 | return ge::GRAPH_FAILED); |
| 179 | - OP_CHECK_IF(info.batch == 0 || info.totalQ == 0 || info.maxBlocksPerBatch == 0, | 334 | + OP_CHECK_IF(info.batch == 0, OP_LOGE(context, "batch must be positive."), return ge::GRAPH_FAILED); |
| 180 | - OP_LOGE(context, "batch/totalQ/maxBlocksPerBatch must be positive."), return ge::GRAPH_FAILED); | 335 | + // q_len/kv_len 全 0 跳过计算。InferShape 已把空 KV 的 score 末维钳到 16。 |
| 336 | + OP_CHECK_IF(info.maxBlocksPerBatch == 0, | ||
| 337 | + OP_LOGE(context, "maxBlocksPerBatch must be positive (empty kv uses aligned score stride)."), | ||
| 338 | + return ge::GRAPH_FAILED); | ||
| 181 | OP_CHECK_IF(info.keyLayout != MSA_KEY_LAYOUT_TND && info.numPages == 0, | 339 | OP_CHECK_IF(info.keyLayout != MSA_KEY_LAYOUT_TND && info.numPages == 0, |
| 182 | OP_LOGE(context, "PA numPages must be positive."), return ge::GRAPH_FAILED); | 340 | OP_LOGE(context, "PA numPages must be positive."), return ge::GRAPH_FAILED); |
| 183 | OP_CHECK_IF(static_cast<uint32_t>(startLocShape->GetStorageShape().GetDim(MSA_DIM_0)) != info.batch, | 341 | OP_CHECK_IF(static_cast<uint32_t>(startLocShape->GetStorageShape().GetDim(MSA_DIM_0)) != info.batch, |
| @@ -186,19 +344,20 @@ ge::graphStatus ParseAndCheck(gert::TilingContext *context, MsaIndexScoreInfo &i | |||
| 186 | const auto *queryDesc = context->GetInputDesc(MSA_IDX_QUERY); | 344 | const auto *queryDesc = context->GetInputDesc(MSA_IDX_QUERY); |
| 187 | OP_CHECK_NULL_WITH_CONTEXT(context, queryDesc); | 345 | OP_CHECK_NULL_WITH_CONTEXT(context, queryDesc); |
| 188 | info.queryDtype = queryDesc->GetDataType(); | 346 | info.queryDtype = queryDesc->GetDataType(); |
| 189 | - OP_CHECK_IF(info.queryDtype != ge::DT_BF16 && info.queryDtype != ge::DT_FLOAT16, | 347 | + OP_CHECK_IF(!IsNonQuantQueryDtype(info.queryDtype, info.isAscend950), |
| 190 | - OP_LOGE(context, "query dtype must be bf16 or fp16 on A2/A3."), return ge::GRAPH_FAILED); | 348 | + OP_LOGE(context, "query dtype must be bf16 or fp16 on A2/A3; Ascend 950 also allows hifloat8 / " |
| 349 | + "float8_e5m2 / float8_e4m3fn."), | ||
| 350 | + return ge::GRAPH_FAILED); | ||
| 191 | 351 | ||
| 192 | - const auto *keyDesc = context->GetInputDesc(MSA_IDX_KEY); | ||
| 193 | - OP_CHECK_NULL_WITH_CONTEXT(context, keyDesc); | ||
| 194 | - info.keyDtype = keyDesc->GetDataType(); | ||
| 195 | info.isQuant = (info.keyDtype == ge::DT_INT8); | 352 | info.isQuant = (info.keyDtype == ge::DT_INT8); |
| 196 | if (info.isQuant) { | 353 | if (info.isQuant) { |
| 197 | - OP_CHECK_IF(info.queryDtype != ge::DT_FLOAT16, OP_LOGE(context, "int8 key currently requires fp16 query."), | 354 | + OP_CHECK_IF(info.queryDtype != ge::DT_FLOAT16, OP_LOGE(context, "ND int8 key currently requires fp16 query."), |
| 198 | return ge::GRAPH_FAILED); | 355 | return ge::GRAPH_FAILED); |
| 199 | } else { | 356 | } else { |
| 200 | OP_CHECK_IF(info.keyDtype != info.queryDtype, OP_LOGE(context, "non-quant key dtype must match query dtype."), | 357 | OP_CHECK_IF(info.keyDtype != info.queryDtype, OP_LOGE(context, "non-quant key dtype must match query dtype."), |
| 201 | return ge::GRAPH_FAILED); | 358 | return ge::GRAPH_FAILED); |
| 359 | + OP_CHECK_IF(IsFp8ComputeDtype(info.queryDtype) && !info.isAscend950, | ||
| 360 | + OP_LOGE(context, "FP8 query/key is only supported on Ascend 950."), return ge::GRAPH_FAILED); | ||
| 202 | } | 361 | } |
| 203 | 362 | ||
| 204 | const gert::StorageShape *scaleShape = context->GetOptionalInputShape(MSA_IDX_SCALE); | 363 | const gert::StorageShape *scaleShape = context->GetOptionalInputShape(MSA_IDX_SCALE); |
| @@ -282,14 +441,30 @@ ge::graphStatus DoTiling(gert::TilingContext *context, const MsaIndexScoreInfo & | |||
| 282 | OP_CHECK_IF(aicNum == 0, OP_LOGE(context, "aic core num is 0."), return ge::GRAPH_FAILED); | 441 | OP_CHECK_IF(aicNum == 0, OP_LOGE(context, "aic core num is 0."), return ge::GRAPH_FAILED); |
| 283 | 442 | ||
| 284 | const uint32_t scoreBlockStride = RoundUpU32(info.maxBlocksPerBatch, MSA_SCORE_STRIDE_ALIGN); | 443 | const uint32_t scoreBlockStride = RoundUpU32(info.maxBlocksPerBatch, MSA_SCORE_STRIDE_ALIGN); |
| 285 | - // S workspace:非量化路径元素为 fp16(AIC fixpipe F322F16 直接写出),int8 路径 fp32。 | 444 | + // 950 C_to_UB:S 不进 GM;int8/TND 尾页仍要一页 K scratch。A2 与 950 回退(MSA_A5_USE_C2UB=0)走 8-stage S。 |
| 286 | - const uint32_t sWsBytes = | 445 | + const uint32_t sElemBytes = info.isQuant ? sizeof(float) : sizeof(uint16_t); |
| 287 | - aicNum * MSA_WORKSPACE_STAGES * MSA_STILE_ELEM_NUM * (info.isQuant ? sizeof(float) : sizeof(uint16_t)); | 446 | + const bool c2ub = info.isAscend950 && (MSA_A5_USE_C2UB != 0); |
| 447 | + const uint32_t sWsBytes = c2ub ? 0U : (aicNum * MSA_WORKSPACE_STAGES * MSA_STILE_ELEM_NUM * sElemBytes); | ||
| 288 | const bool useKScratch = info.isQuant || (info.keyLayout == MSA_KEY_LAYOUT_TND); | 448 | const bool useKScratch = info.isQuant || (info.keyLayout == MSA_KEY_LAYOUT_TND); |
| 289 | - const uint32_t kScratchBytes = useKScratch ? (aicNum * MSA_K_SCRATCH_ELEM_NUM * sizeof(uint16_t)) : 0U; | 449 | + uint32_t scratchElemBytes = sizeof(uint16_t); |
| 290 | - // K scratch 基址以 4B 为单位表达(kernel 内按 float* 折算),S 变 fp16 后偏移减半。 | 450 | + if (info.isAscend950 && !info.isQuant && IsFp8ComputeDtype(info.queryDtype)) { |
| 451 | + scratchElemBytes = 1U; | ||
| 452 | + } | ||
| 453 | + uint32_t kScratchElems = c2ub ? MSA_A5_K_SCRATCH_ELEM_NUM : MSA_K_SCRATCH_ELEM_NUM; | ||
| 454 | + // A2/A3 int8 四槽 K scratch;950 C2UB / 非量化 TND 尾页保持单槽。 | ||
| 455 | + if (!info.isAscend950 && info.isQuant) { | ||
| 456 | + kScratchElems *= MSA_K_SCRATCH_STAGES_A2; | ||
| 457 | + } | ||
| 458 | + const uint32_t kScratchBytes = useKScratch ? (aicNum * kScratchElems * scratchElemBytes) : 0U; | ||
| 291 | const uint32_t kScratchOffsetElems = sWsBytes / sizeof(float); | 459 | const uint32_t kScratchOffsetElems = sWsBytes / sizeof(float); |
| 292 | 460 | ||
| 461 | + uint32_t launchAic = aicNum; | ||
| 462 | + if (info.totalQ == 0U) { | ||
| 463 | + launchAic = 1U; | ||
| 464 | + } else { | ||
| 465 | + launchAic = EstLaunchAic(info, aicNum); | ||
| 466 | + } | ||
| 467 | + | ||
| 293 | MsaIndexScoreTilingData tilingData; | 468 | MsaIndexScoreTilingData tilingData; |
| 294 | tilingData.set_batch(info.batch); | 469 | tilingData.set_batch(info.batch); |
| 295 | tilingData.set_totalQ(info.totalQ); | 470 | tilingData.set_totalQ(info.totalQ); |
| @@ -300,7 +475,7 @@ ge::graphStatus DoTiling(gert::TilingContext *context, const MsaIndexScoreInfo & | |||
| 300 | tilingData.set_blockSize(info.blockSize); | 475 | tilingData.set_blockSize(info.blockSize); |
| 301 | tilingData.set_maxBlocksPerBatch(info.maxBlocksPerBatch); | 476 | tilingData.set_maxBlocksPerBatch(info.maxBlocksPerBatch); |
| 302 | tilingData.set_scoreBlockStride(scoreBlockStride); | 477 | tilingData.set_scoreBlockStride(scoreBlockStride); |
| 303 | - tilingData.set_usedCoreNum(aicNum); | 478 | + tilingData.set_usedCoreNum(launchAic); |
| 304 | tilingData.set_isQuant(info.isQuant ? 1U : 0U); | 479 | tilingData.set_isQuant(info.isQuant ? 1U : 0U); |
| 305 | tilingData.set_sparseMode(info.sparseMode); | 480 | tilingData.set_sparseMode(info.sparseMode); |
| 306 | tilingData.set_initBlocks(info.initBlocks); | 481 | tilingData.set_initBlocks(info.initBlocks); |
| @@ -311,22 +486,28 @@ ge::graphStatus DoTiling(gert::TilingContext *context, const MsaIndexScoreInfo & | |||
| 311 | 486 | ||
| 312 | tilingData.set_strideQt(info.numQHeads * info.headDim); | 487 | tilingData.set_strideQt(info.numQHeads * info.headDim); |
| 313 | tilingData.set_strideQn(info.headDim); | 488 | tilingData.set_strideQn(info.headDim); |
| 489 | + uint32_t defaultStrideKvBlock = 0; | ||
| 314 | if (info.keyLayout == MSA_KEY_LAYOUT_TND) { | 490 | if (info.keyLayout == MSA_KEY_LAYOUT_TND) { |
| 315 | - tilingData.set_strideKvBlock(0); | 491 | + defaultStrideKvBlock = 0; |
| 316 | tilingData.set_strideKvToken(info.numKvHeads * info.headDim); | 492 | tilingData.set_strideKvToken(info.numKvHeads * info.headDim); |
| 317 | tilingData.set_strideScalePage(info.numKvHeads); | 493 | tilingData.set_strideScalePage(info.numKvHeads); |
| 318 | tilingData.set_strideScaleHead(1); | 494 | tilingData.set_strideScaleHead(1); |
| 319 | } else if (info.keyLayout == MSA_KEY_LAYOUT_BNBD) { | 495 | } else if (info.keyLayout == MSA_KEY_LAYOUT_BNBD) { |
| 320 | - tilingData.set_strideKvBlock(info.numKvHeads * info.blockSize * info.headDim); | 496 | + defaultStrideKvBlock = info.numKvHeads * info.blockSize * info.headDim; |
| 321 | tilingData.set_strideKvToken(info.headDim); | 497 | tilingData.set_strideKvToken(info.headDim); |
| 322 | tilingData.set_strideScalePage(info.numKvHeads * info.blockSize); | 498 | tilingData.set_strideScalePage(info.numKvHeads * info.blockSize); |
| 323 | tilingData.set_strideScaleHead(info.blockSize); | 499 | tilingData.set_strideScaleHead(info.blockSize); |
| 324 | } else { | 500 | } else { |
| 325 | - tilingData.set_strideKvBlock(info.blockSize * info.numKvHeads * info.headDim); | 501 | + defaultStrideKvBlock = info.blockSize * info.numKvHeads * info.headDim; |
| 326 | tilingData.set_strideKvToken(info.numKvHeads * info.headDim); | 502 | tilingData.set_strideKvToken(info.numKvHeads * info.headDim); |
| 327 | tilingData.set_strideScalePage(info.numKvHeads * info.blockSize); | 503 | tilingData.set_strideScalePage(info.numKvHeads * info.blockSize); |
| 328 | tilingData.set_strideScaleHead(info.blockSize); | 504 | tilingData.set_strideScaleHead(info.blockSize); |
| 329 | } | 505 | } |
| 506 | + uint32_t strideKvBlock = defaultStrideKvBlock; | ||
| 507 | + if (ResolveStrideKvBlock(context, info, defaultStrideKvBlock, strideKvBlock) != ge::GRAPH_SUCCESS) { | ||
| 508 | + return ge::GRAPH_FAILED; | ||
| 509 | + } | ||
| 510 | + tilingData.set_strideKvBlock(strideKvBlock); | ||
| 330 | tilingData.set_strideOutHead(info.totalQ * scoreBlockStride); | 511 | tilingData.set_strideOutHead(info.totalQ * scoreBlockStride); |
| 331 | tilingData.set_strideOutToken(scoreBlockStride); | 512 | tilingData.set_strideOutToken(scoreBlockStride); |
| 332 | tilingData.set_kScratchOffsetElems(kScratchOffsetElems); | 513 | tilingData.set_kScratchOffsetElems(kScratchOffsetElems); |
| @@ -335,22 +516,45 @@ ge::graphStatus DoTiling(gert::TilingContext *context, const MsaIndexScoreInfo & | |||
| 335 | context->GetRawTilingData()->SetDataSize(tilingData.GetDataSize()); | 516 | context->GetRawTilingData()->SetDataSize(tilingData.GetDataSize()); |
| 336 | 517 | ||
| 337 | // MIX 1AIC:2AIV:CalcTschBlockDim 的 sliceNum 按 AIV 计数,内部再 / (aiv/aic)。 | 518 | // MIX 1AIC:2AIV:CalcTschBlockDim 的 sliceNum 按 AIV 计数,内部再 / (aiv/aic)。 |
| 338 | - // 传入 aicNum 会再除一次得到 blockDim=aic/2(910B3: 20→10),只能打一半 Cube。 | 519 | + // 传入 aicNum 会再除一次得到 blockDim=aic/2,只能打一半 Cube。 |
| 339 | - // 与 LightningIndexer 等 MIX 算子一致:sliceNum = aivNum → 910B3 blockDim=20。 | 520 | + // 整 batch q_len=0:queryS==0 → BlockDim=1,避免 totalTaskNum=0。 |
| 340 | - context->SetBlockDim(ascendcPlatform.CalcTschBlockDim(aivNum, aicNum, aivNum)); | 521 | + // sliceNum = launchAic * 2:短 decode 只起实际 M-task 对应的 MIX; |
| 522 | + // 多 M-tile / 大 batch 仍打满 AIC。 | ||
| 523 | + if (info.totalQ == 0U) { | ||
| 524 | + context->SetBlockDim(1); | ||
| 525 | + } else { | ||
| 526 | + uint32_t sliceAiv = launchAic * MSA_AIV_PER_AIC; | ||
| 527 | + if (sliceAiv > aivNum) { | ||
| 528 | + sliceAiv = aivNum; | ||
| 529 | + } | ||
| 530 | + context->SetBlockDim(ascendcPlatform.CalcTschBlockDim(sliceAiv, aicNum, aivNum)); | ||
| 531 | + } | ||
| 341 | 532 | ||
| 342 | size_t *workspaces = context->GetWorkspaceSizes(1); | 533 | size_t *workspaces = context->GetWorkspaceSizes(1); |
| 343 | OP_CHECK_NULL_WITH_CONTEXT(context, workspaces); | 534 | OP_CHECK_NULL_WITH_CONTEXT(context, workspaces); |
| 344 | workspaces[0] = | 535 | workspaces[0] = |
| 345 | ascendcPlatform.GetLibApiWorkSpaceSize() + static_cast<size_t>(sWsBytes) + static_cast<size_t>(kScratchBytes); | 536 | ascendcPlatform.GetLibApiWorkSpaceSize() + static_cast<size_t>(sWsBytes) + static_cast<size_t>(kScratchBytes); |
| 537 | + // 非量化 PA 无 S GM / K scratch 时 user workspace 为 0,GetUserWorkspace 可能返回 nullptr。 | ||
| 538 | + if (info.isAscend950 && sWsBytes == 0U && kScratchBytes == 0U) { | ||
| 539 | + workspaces[0] += 64U; | ||
| 540 | + } | ||
| 346 | 541 | ||
| 347 | uint64_t tilingKey = MSA_TILING_KEY_FP16; | 542 | uint64_t tilingKey = MSA_TILING_KEY_FP16; |
| 348 | if (info.isQuant) { | 543 | if (info.isQuant) { |
| 349 | tilingKey = MSA_TILING_KEY_FP16_INT8; | 544 | tilingKey = MSA_TILING_KEY_FP16_INT8; |
| 545 | + } else if (info.queryDtype == ge::DT_HIFLOAT8) { | ||
| 546 | + tilingKey = MSA_TILING_KEY_HIFLOAT8; | ||
| 547 | + } else if (info.queryDtype == ge::DT_FLOAT8_E5M2) { | ||
| 548 | + tilingKey = MSA_TILING_KEY_FP8_E5M2; | ||
| 549 | + } else if (info.queryDtype == ge::DT_FLOAT8_E4M3FN) { | ||
| 550 | + tilingKey = MSA_TILING_KEY_FP8_E4M3FN; | ||
| 350 | } else { | 551 | } else { |
| 351 | tilingKey = (info.queryDtype == ge::DT_BF16) ? MSA_TILING_KEY_BF16 : MSA_TILING_KEY_FP16; | 552 | tilingKey = (info.queryDtype == ge::DT_BF16) ? MSA_TILING_KEY_BF16 : MSA_TILING_KEY_FP16; |
| 352 | } | 553 | } |
| 353 | context->SetTilingKey(tilingKey); | 554 | context->SetTilingKey(tilingKey); |
| 555 | + OP_LOGI(context->GetNodeName(), | ||
| 556 | + "MsaIndexScore tilingKey=%lu headDim=%u layout=%u strideKvBlock=%u launchAic=%u aicNum=%u", tilingKey, | ||
| 557 | + info.headDim, info.keyLayout, strideKvBlock, launchAic, aicNum); | ||
| 354 | return ge::GRAPH_SUCCESS; | 558 | return ge::GRAPH_SUCCESS; |
| 355 | } | 559 | } |
| 356 | } // namespace | 560 | } // namespace |
| @@ -65,7 +65,7 @@ TILING_DATA_FIELD_DEF(uint32_t, strideOutHead) | |||
| 65 | TILING_DATA_FIELD_DEF(uint32_t, strideOutToken) | 65 | TILING_DATA_FIELD_DEF(uint32_t, strideOutToken) |
| 66 | TILING_DATA_FIELD_DEF(uint32_t, kScratchOffsetElems) | 66 | TILING_DATA_FIELD_DEF(uint32_t, kScratchOffsetElems) |
| 67 | TILING_DATA_FIELD_DEF(uint32_t, keyLayout) // MSA_KEY_LAYOUT_BBND / BNBD / TND | 67 | TILING_DATA_FIELD_DEF(uint32_t, keyLayout) // MSA_KEY_LAYOUT_BBND / BNBD / TND |
| 68 | -TILING_DATA_FIELD_DEF(uint32_t, totalK) // TND:key 第 0 维 T2;PA:0 | 68 | +TILING_DATA_FIELD_DEF(uint32_t, totalK) // TND:key 第 0 维 T2; PA:0 |
| 69 | END_TILING_DATA_DEF | 69 | END_TILING_DATA_DEF |
| 70 | REGISTER_TILING_DATA_CLASS(MsaIndexScore, MsaIndexScoreTilingData) | 70 | REGISTER_TILING_DATA_CLASS(MsaIndexScore, MsaIndexScoreTilingData) |
| 71 | 71 | ||
| @@ -87,6 +87,7 @@ struct MsaIndexScoreInfo { | |||
| 87 | uint32_t keyLayout = 0; | 87 | uint32_t keyLayout = 0; |
| 88 | uint32_t totalK = 0; | 88 | uint32_t totalK = 0; |
| 89 | bool isQuant = false; | 89 | bool isQuant = false; |
| 90 | + bool isAscend950 = false; | ||
| 90 | ge::DataType queryDtype = ge::DT_BF16; | 91 | ge::DataType queryDtype = ge::DT_BF16; |
| 91 | ge::DataType keyDtype = ge::DT_BF16; | 92 | ge::DataType keyDtype = ge::DT_BF16; |
| 92 | }; | 93 | }; |
| @@ -64,9 +64,8 @@ public: | |||
| 64 | using L1BAlignHelper = Catlass::Gemm::helper::L1AlignHelper<ElementB, LayoutB>; | 64 | using L1BAlignHelper = Catlass::Gemm::helper::L1AlignHelper<ElementB, LayoutB>; |
| 65 | 65 | ||
| 66 | // K (B) 的 L1 流水级数。非量化 3 级:fixpipe 减半(fp16 S)后 MTE2 的逐页延迟 | 66 | // K (B) 的 L1 流水级数。非量化 3 级:fixpipe 减半(fp16 S)后 MTE2 的逐页延迟 |
| 67 | - // (~390ns,L2 命中)会成为新的关键路径,加深一级让 MTE2 提前两页预取。 | 67 | + // 会成为新的关键路径,加深一级让 MTE2 提前两页预取。 |
| 68 | - // int8 保持 2 级:K 源是每 stile 复用的 per-core scratch,AIV 下一 stile 的 cast | 68 | + // A2/A3 int8 双槽 scratch 后同样 3 级:AIC 消费 slot[i] 时 AIV 只写 slot[i^1]。 |
| 69 | - // 会重写该区,更深的预取与 cast 重写产生竞态(实测 STAGES=3 下 int8 崩溃)。 | ||
| 70 | // L1 占用:A 32KB + 3×32KB B = 128KB / 512KB。 | 69 | // L1 占用:A 32KB + 3×32KB B = 128KB / 512KB。 |
| 71 | static constexpr uint32_t STAGES = STAGES_IN; | 70 | static constexpr uint32_t STAGES = STAGES_IN; |
| 72 | // 非量化路径开 unit flag:mmad 与 fixpipe 的依赖交给硬件互锁,省掉每页 | 71 | // 非量化路径开 unit flag:mmad 与 fixpipe 的依赖交给硬件互锁,省掉每页 |
| @@ -11,11 +11,11 @@ | |||
| 11 | /*! | 11 | /*! |
| 12 | * \file msa_index_score_kernel.h | 12 | * \file msa_index_score_kernel.h |
| 13 | * \brief MsaIndexScore Atlas A2/A3 核函数:AIC 做 paged QKᵀ,AIV 做(可选反量化)+ mask + 分段 RowMax。 | 13 | * \brief MsaIndexScore Atlas A2/A3 核函数:AIC 做 paged QKᵀ,AIV 做(可选反量化)+ mask + 分段 RowMax。 |
| 14 | - * 不支持 Ascend 950 / FP8。 | ||
| 15 | * | 14 | * |
| 16 | * 非量化:query/key 同为 bf16|fp16,score 侧不乘 scale。 | 15 | * 非量化:query/key 同为 bf16|fp16,score 侧不乘 scale。 |
| 17 | - * int8 量化:key 为 int8;AIV 将 page DataCopy+Cast 到 fp 暂存后通知 AIC 做 Mmad; | 16 | + * int8 量化:key 为 int8;AIV 将 page DataCopy+Cast 到四槽 fp 暂存后通知 AIC 做 Mmad; |
| 18 | - * AIV 在 mask 前按 scale[NP,N_kv,P] 对 S 做列乘完成 per-token 反量化。 | 17 | + * gather(st+4) 叠在 cube(st+1..st+3) 上;两 AIV 对分 8 page(每页仍串行 MTE2→V→MTE3)。 |
| 18 | + * 同 AIV 上 MTE2∥MTE3 / 多 page 并发 MTE2 会写坏多 page int8。S 侧仍为 fp32 反量化。 | ||
| 19 | */ | 19 | */ |
| 20 | 20 | ||
| 21 | #ifndef MSA_INDEX_SCORE_KERNEL_H | 21 | #ifndef MSA_INDEX_SCORE_KERNEL_H |
| @@ -36,7 +36,7 @@ | |||
| 36 | #include "../msa_index_score_common.h" | 36 | #include "../msa_index_score_common.h" |
| 37 | #include "msa_block_mmad.h" | 37 | #include "msa_block_mmad.h" |
| 38 | #include "msa_index_score_task.h" | 38 | #include "msa_index_score_task.h" |
| 39 | -#include "msa_index_score_epilogue.h" | 39 | +#include "msa_seg_row_max_epilogue.h" |
| 40 | 40 | ||
| 41 | namespace MsaIndexScoreNs { | 41 | namespace MsaIndexScoreNs { |
| 42 | 42 | ||
| @@ -57,20 +57,23 @@ public: | |||
| 57 | using ElementS = std::conditional_t<IS_QUANT, float, half>; | 57 | using ElementS = std::conditional_t<IS_QUANT, float, half>; |
| 58 | using SType = Catlass::Gemm::GemmType<ElementS, LayoutS>; | 58 | using SType = Catlass::Gemm::GemmType<ElementS, LayoutS>; |
| 59 | 59 | ||
| 60 | - // int8 的 K 源是每 stile 复用的 per-core scratch,保持 2 级 L1B 流水(3 级实测竞态崩溃); | 60 | + // int8:三槽 K scratch 后与非量化同样 3 级 L1B + unit flag(单槽时 3 级会与下一 stile |
| 61 | - // 同样出于该 scratch 的时序保守性,unit flag 只在非量化路径开启。 | 61 | + // 的 cast 重写竞态,950 arch35 仍保持 2 级 / 关 unit flag)。 |
| 62 | - using BlockMmad = | 62 | + using BlockMmad = MsaBlockMmad<QType, KType, SType, MSA_L1B_STAGES_FP16, true>; |
| 63 | - MsaBlockMmad<QType, KType, SType, IS_QUANT ? MSA_L1B_STAGES_INT8 : MSA_L1B_STAGES_FP16, !IS_QUANT>; | ||
| 64 | 63 | ||
| 65 | static constexpr uint32_t REVERSE_DEPTH = MSA_WORKSPACE_STAGES - 1; | 64 | static constexpr uint32_t REVERSE_DEPTH = MSA_WORKSPACE_STAGES - 1; |
| 66 | static constexpr uint32_t STILE_WIDTH = MSA_BLOCKS_PER_STILE * MSA_BLOCK_SIZE; | 65 | static constexpr uint32_t STILE_WIDTH = MSA_BLOCKS_PER_STILE * MSA_BLOCK_SIZE; |
| 67 | static constexpr bool IS_QUANT_V = IS_QUANT; | 66 | static constexpr bool IS_QUANT_V = IS_QUANT; |
| 68 | - // 单 page cast 暂存:int8 + half 共用 epilogue 前半段 UB(S 尚未载入)。 | 67 | + static_assert(MSA_K_SCRATCH_STAGES_A2 == 4, "A2 int8 K scratch handshake assumes 4 slots"); |
| 69 | - static constexpr uint32_t UB_OFF_CAST_I8 = 0; | 68 | + // int8:cast 放 epilogue 尾部。EVENT_ID2 每页成对 MTE2_V / V_MTE3 / MTE3_MTE2。 |
| 69 | + // 非量化 TND 仍占用 epilogue S 区(gather 在 WaitS 之前,S 尚未载入)。 | ||
| 70 | + static constexpr uint32_t UB_OFF_CAST_I8 = IS_QUANT ? MsaSegRowMaxEpilogue<true>::UB_OFF_TAIL : 0; | ||
| 70 | static constexpr uint32_t UB_SIZE_CAST_I8 = MSA_BLOCK_SIZE * MSA_K_TILE * sizeof(int8_t); | 71 | static constexpr uint32_t UB_SIZE_CAST_I8 = MSA_BLOCK_SIZE * MSA_K_TILE * sizeof(int8_t); |
| 71 | static constexpr uint32_t UB_OFF_CAST_FP = UB_OFF_CAST_I8 + UB_SIZE_CAST_I8; | 72 | static constexpr uint32_t UB_OFF_CAST_FP = UB_OFF_CAST_I8 + UB_SIZE_CAST_I8; |
| 72 | static constexpr uint32_t UB_SIZE_CAST_FP = MSA_BLOCK_SIZE * MSA_K_TILE * sizeof(half); | 73 | static constexpr uint32_t UB_SIZE_CAST_FP = MSA_BLOCK_SIZE * MSA_K_TILE * sizeof(half); |
| 73 | static_assert(UB_OFF_CAST_FP + UB_SIZE_CAST_FP <= ArchTag::UB_SIZE, "cast UB out of bounds"); | 74 | static_assert(UB_OFF_CAST_FP + UB_SIZE_CAST_FP <= ArchTag::UB_SIZE, "cast UB out of bounds"); |
| 75 | + static_assert(!IS_QUANT || UB_OFF_CAST_I8 >= MsaSegRowMaxEpilogue<true>::UB_TOTAL, | ||
| 76 | + "int8 cast UB overlaps epilogue"); | ||
| 74 | 77 | ||
| 75 | __aicore__ inline MsaIndexScoreKernel() {} | 78 | __aicore__ inline MsaIndexScoreKernel() {} |
| 76 | 79 | ||
| @@ -120,12 +123,16 @@ private: | |||
| 120 | Catlass::Arch::Resource<ArchTag> resource; | 123 | Catlass::Arch::Resource<ArchTag> resource; |
| 121 | BlockMmad blockMmad(resource); | 124 | BlockMmad blockMmad(resource); |
| 122 | Catlass::Arch::CrossCoreFlagWithReverse<REVERSE_DEPTH> flagSReady{MSA_FLAG_S_READY, MSA_FLAG_S_READY_REVERSE}; | 125 | Catlass::Arch::CrossCoreFlagWithReverse<REVERSE_DEPTH> flagSReady{MSA_FLAG_S_READY, MSA_FLAG_S_READY_REVERSE}; |
| 123 | - Catlass::Arch::CrossCoreFlag flagKReady{MSA_FLAG_K_READY}; | 126 | + Catlass::Arch::CrossCoreFlag flagK0{MSA_FLAG_K_READY}; |
| 127 | + Catlass::Arch::CrossCoreFlag flagK1{MSA_FLAG_K_READY_REVERSE}; | ||
| 128 | + Catlass::Arch::CrossCoreFlag flagK2{MSA_FLAG_K_READY_2}; | ||
| 129 | + Catlass::Arch::CrossCoreFlag flagK3{MSA_FLAG_K_READY_3}; | ||
| 124 | 130 | ||
| 125 | const uint32_t coreIdx = AscendC::GetBlockIdx(); | 131 | const uint32_t coreIdx = AscendC::GetBlockIdx(); |
| 126 | const uint32_t coreNum = AscendC::GetBlockNum(); | 132 | const uint32_t coreNum = AscendC::GetBlockNum(); |
| 127 | const uint64_t coreWsBase = static_cast<uint64_t>(coreIdx) * MSA_WORKSPACE_STAGES * MSA_STILE_ELEM_NUM; | 133 | const uint64_t coreWsBase = static_cast<uint64_t>(coreIdx) * MSA_WORKSPACE_STAGES * MSA_STILE_ELEM_NUM; |
| 128 | - const uint64_t coreKScratch = static_cast<uint64_t>(coreIdx) * MSA_K_SCRATCH_ELEM_NUM; | 134 | + const uint32_t kStages = IS_QUANT_V ? MSA_K_SCRATCH_STAGES_A2 : 1U; |
| 135 | + const uint64_t coreKScratch = static_cast<uint64_t>(coreIdx) * kStages * MSA_K_SCRATCH_ELEM_NUM; | ||
| 129 | const uint32_t totalTasks = scheduler_.TotalTasks(); | 136 | const uint32_t totalTasks = scheduler_.TotalTasks(); |
| 130 | 137 | ||
| 131 | uint32_t tileSeq = 0; | 138 | uint32_t tileSeq = 0; |
| @@ -138,12 +145,13 @@ private: | |||
| 138 | const bool needKScratch = StileNeedsKScratch(task, st * MSA_BLOCKS_PER_STILE); | 145 | const bool needKScratch = StileNeedsKScratch(task, st * MSA_BLOCKS_PER_STILE); |
| 139 | if (needKScratch) { | 146 | if (needKScratch) { |
| 140 | // MIX 1AIC:2AIV:AIV→AIC 的 0x2 flag 需两个 AIV 都 Set 后才放行。 | 147 | // MIX 1AIC:2AIV:AIV→AIC 的 0x2 flag 需两个 AIV 都 Set 后才放行。 |
| 141 | - Catlass::Arch::CrossCoreWaitFlag(flagKReady); | 148 | + // 绑 PIPE_MTE2,避免 PIPE_ALL 把上一 stile 的 cube/fixpipe 冲掉。 |
| 142 | - AscendC::PipeBarrier<PIPE_ALL>(); | 149 | + WaitKScratchReady(flagK0, flagK1, flagK2, flagK3, st); |
| 143 | } | 150 | } |
| 144 | const uint64_t sBase = | 151 | const uint64_t sBase = |
| 145 | coreWsBase + static_cast<uint64_t>(tileSeq % MSA_WORKSPACE_STAGES) * MSA_STILE_ELEM_NUM; | 152 | coreWsBase + static_cast<uint64_t>(tileSeq % MSA_WORKSPACE_STAGES) * MSA_STILE_ELEM_NUM; |
| 146 | - ComputeSTile(blockMmad, task, st * MSA_BLOCKS_PER_STILE, sBase, coreKScratch, needLoadQ); | 153 | + ComputeSTile(blockMmad, task, st * MSA_BLOCKS_PER_STILE, sBase, KScratchSlot(coreKScratch, st), |
| 154 | + needLoadQ); | ||
| 147 | needLoadQ = false; | 155 | needLoadQ = false; |
| 148 | Catlass::Arch::CrossCoreSetFlagWithReverse<0x2, PIPE_FIX>(flagSReady); | 156 | Catlass::Arch::CrossCoreSetFlagWithReverse<0x2, PIPE_FIX>(flagSReady); |
| 149 | ++tileSeq; | 157 | ++tileSeq; |
| @@ -152,6 +160,37 @@ private: | |||
| 152 | AscendC::PipeBarrier<PIPE_ALL>(); | 160 | AscendC::PipeBarrier<PIPE_ALL>(); |
| 153 | } | 161 | } |
| 154 | 162 | ||
| 163 | + __aicore__ inline uint64_t KScratchSlot(uint64_t coreKScratch, uint32_t st) const | ||
| 164 | + { | ||
| 165 | + if constexpr (IS_QUANT_V) { | ||
| 166 | + return coreKScratch + static_cast<uint64_t>(st % MSA_K_SCRATCH_STAGES_A2) * MSA_K_SCRATCH_ELEM_NUM; | ||
| 167 | + } | ||
| 168 | + return coreKScratch; | ||
| 169 | + } | ||
| 170 | + | ||
| 171 | + __aicore__ inline void WaitKScratchReady(Catlass::Arch::CrossCoreFlag &flagK0, Catlass::Arch::CrossCoreFlag &flagK1, | ||
| 172 | + Catlass::Arch::CrossCoreFlag &flagK2, Catlass::Arch::CrossCoreFlag &flagK3, | ||
| 173 | + uint32_t st) | ||
| 174 | + { | ||
| 175 | + if constexpr (IS_QUANT_V) { | ||
| 176 | + const uint32_t slot = st % MSA_K_SCRATCH_STAGES_A2; | ||
| 177 | + if (slot == 0U) { | ||
| 178 | + Catlass::Arch::CrossCoreWaitFlag<0x2, PIPE_MTE2>(flagK0); | ||
| 179 | + } else if (slot == 1U) { | ||
| 180 | + Catlass::Arch::CrossCoreWaitFlag<0x2, PIPE_MTE2>(flagK1); | ||
| 181 | + } else if (slot == 2U) { | ||
| 182 | + Catlass::Arch::CrossCoreWaitFlag<0x2, PIPE_MTE2>(flagK2); | ||
| 183 | + } else { | ||
| 184 | + Catlass::Arch::CrossCoreWaitFlag<0x2, PIPE_MTE2>(flagK3); | ||
| 185 | + } | ||
| 186 | + } else { | ||
| 187 | + (void)flagK1; | ||
| 188 | + (void)flagK2; | ||
| 189 | + (void)flagK3; | ||
| 190 | + Catlass::Arch::CrossCoreWaitFlag<0x2, PIPE_MTE2>(flagK0); | ||
| 191 | + } | ||
| 192 | + } | ||
| 193 | + | ||
| 155 | __aicore__ inline bool KeyBlockNeedsScratch(const MsaTask &task, uint32_t blk) const | 194 | __aicore__ inline bool KeyBlockNeedsScratch(const MsaTask &task, uint32_t blk) const |
| 156 | { | 195 | { |
| 157 | if constexpr (IS_QUANT_V) { | 196 | if constexpr (IS_QUANT_V) { |
| @@ -229,20 +268,25 @@ private: | |||
| 229 | AscendC::DataCopyPad(ub, gm, params, pad); | 268 | AscendC::DataCopyPad(ub, gm, params, pad); |
| 230 | } | 269 | } |
| 231 | 270 | ||
| 232 | - /// AIV:把本 S-tile 可见 block 的 K 收进 per-core scratch(int8 cast 或 TND fp copy+尾填充)。 | 271 | + /// int8 gather:单 i8 + 单 fp16,每页 MTE2→V→MTE3 串行。subIdx/subBlockNum 对分 page。 |
| 233 | - __aicore__ inline void GatherKeySTileToScratch(Catlass::Arch::Resource<ArchTag> &resource, const MsaTask &task, | 272 | + __aicore__ inline void GatherKeySTileQuantPiped(Catlass::Arch::Resource<ArchTag> &resource, const MsaTask &task, |
| 234 | - uint32_t blkBase, uint64_t kScratchBase) | 273 | + uint32_t blkBase, uint64_t kScratchBase, uint32_t subIdx, |
| 274 | + uint32_t subBlockNum) | ||
| 235 | { | 275 | { |
| 236 | AscendC::LocalTensor<int8_t> ubI8 = resource.ubBuf.template GetBufferByByte<int8_t>(UB_OFF_CAST_I8); | 276 | AscendC::LocalTensor<int8_t> ubI8 = resource.ubBuf.template GetBufferByByte<int8_t>(UB_OFF_CAST_I8); |
| 237 | AscendC::LocalTensor<half> ubHalf = resource.ubBuf.template GetBufferByByte<half>(UB_OFF_CAST_FP); | 277 | AscendC::LocalTensor<half> ubHalf = resource.ubBuf.template GetBufferByByte<half>(UB_OFF_CAST_FP); |
| 238 | const uint32_t headDim = tiling_->headDim; | 278 | const uint32_t headDim = tiling_->headDim; |
| 239 | const uint32_t nElem = MSA_BLOCK_SIZE * headDim; | 279 | const uint32_t nElem = MSA_BLOCK_SIZE * headDim; |
| 240 | const uint32_t pageStride = MSA_BLOCK_SIZE * MSA_K_TILE; | 280 | const uint32_t pageStride = MSA_BLOCK_SIZE * MSA_K_TILE; |
| 281 | + const uint32_t split = (subBlockNum == 0U) ? 1U : subBlockNum; | ||
| 241 | 282 | ||
| 242 | for (uint32_t j = 0; j < MSA_BLOCKS_PER_STILE; ++j) { | 283 | for (uint32_t j = 0; j < MSA_BLOCKS_PER_STILE; ++j) { |
| 284 | + if ((j % split) != subIdx) { | ||
| 285 | + continue; | ||
| 286 | + } | ||
| 243 | const uint32_t blk = blkBase + j; | 287 | const uint32_t blk = blkBase + j; |
| 244 | if (blk >= task.visibleEndBlk) { | 288 | if (blk >= task.visibleEndBlk) { |
| 245 | - break; | 289 | + continue; |
| 246 | } | 290 | } |
| 247 | if (!KeyBlockNeedsScratch(task, blk)) { | 291 | if (!KeyBlockNeedsScratch(task, blk)) { |
| 248 | continue; | 292 | continue; |
| @@ -252,53 +296,84 @@ private: | |||
| 252 | const uint32_t nValidTok = KeyBlockValidTokens(task, blk); | 296 | const uint32_t nValidTok = KeyBlockValidTokens(task, blk); |
| 253 | const uint32_t nValid = nValidTok * headDim; | 297 | const uint32_t nValid = nValidTok * headDim; |
| 254 | 298 | ||
| 255 | - if constexpr (IS_QUANT_V) { | 299 | + uint32_t copyN = nElem; |
| 256 | - // 与已通过的 PA int8 路径对齐:整页 DataCopy+Cast(nElem),EVENT_ID0。 | 300 | + if (tiling_->keyLayout == MSA_KEY_LAYOUT_TND) { |
| 257 | - // TND 仅在尾页会越过 T2 时改为部分拷贝;中间整页禁止走 CopyGmToUbPartial。 | 301 | + const uint32_t tokenStart = task.cuKStart + blk * tiling_->blockSize; |
| 258 | - uint32_t copyN = nElem; | 302 | + const uint32_t packedEnd = tokenStart + MSA_BLOCK_SIZE; |
| 259 | - if (tiling_->keyLayout == MSA_KEY_LAYOUT_TND) { | 303 | + if (tiling_->totalK > 0 && packedEnd > tiling_->totalK) { |
| 260 | - const uint32_t tokenStart = task.cuKStart + blk * tiling_->blockSize; | 304 | + copyN = nValid; |
| 261 | - const uint32_t packedEnd = tokenStart + MSA_BLOCK_SIZE; | ||
| 262 | - if (tiling_->totalK > 0 && packedEnd > tiling_->totalK) { | ||
| 263 | - copyN = nValid; | ||
| 264 | - } | ||
| 265 | } | 305 | } |
| 266 | - if (copyN < nElem) { | ||
| 267 | - AscendC::Duplicate(ubHalf, static_cast<half>(0), nElem); | ||
| 268 | - AscendC::PipeBarrier<PIPE_V>(); | ||
| 269 | - AscendC::SetFlag<AscendC::HardEvent::V_MTE2>(EVENT_ID0); | ||
| 270 | - AscendC::WaitFlag<AscendC::HardEvent::V_MTE2>(EVENT_ID0); | ||
| 271 | - } | ||
| 272 | - if (copyN > 0) { | ||
| 273 | - if (copyN == nElem) { | ||
| 274 | - AscendC::DataCopy(ubI8, gKeyInt8_[kOffset], nElem); | ||
| 275 | - } else { | ||
| 276 | - CopyGmToUbPartial(ubI8, gKeyInt8_[kOffset], copyN); | ||
| 277 | - } | ||
| 278 | - AscendC::SetFlag<AscendC::HardEvent::MTE2_V>(EVENT_ID0); | ||
| 279 | - AscendC::WaitFlag<AscendC::HardEvent::MTE2_V>(EVENT_ID0); | ||
| 280 | - AscendC::Cast(ubHalf, ubI8, AscendC::RoundMode::CAST_NONE, copyN); | ||
| 281 | - } | ||
| 282 | - AscendC::SetFlag<AscendC::HardEvent::V_MTE3>(EVENT_ID0); | ||
| 283 | - AscendC::WaitFlag<AscendC::HardEvent::V_MTE3>(EVENT_ID0); | ||
| 284 | - AscendC::DataCopy(gKeyScratch_[scratchOff], ubHalf, nElem); | ||
| 285 | - } else { | ||
| 286 | - AscendC::LocalTensor<ElementQ> ubK = resource.ubBuf.template GetBufferByByte<ElementQ>(UB_OFF_CAST_FP); | ||
| 287 | - if (nValid < nElem) { | ||
| 288 | - AscendC::Duplicate(ubK, static_cast<ElementQ>(0), nElem); | ||
| 289 | - AscendC::PipeBarrier<PIPE_V>(); | ||
| 290 | - AscendC::SetFlag<AscendC::HardEvent::V_MTE2>(EVENT_ID2); | ||
| 291 | - AscendC::WaitFlag<AscendC::HardEvent::V_MTE2>(EVENT_ID2); | ||
| 292 | - } | ||
| 293 | - if (nValid > 0) { | ||
| 294 | - CopyGmToUbPartial(ubK, gKeyFp_[kOffset], nValid); | ||
| 295 | - AscendC::SetFlag<AscendC::HardEvent::MTE2_V>(EVENT_ID2); | ||
| 296 | - AscendC::WaitFlag<AscendC::HardEvent::MTE2_V>(EVENT_ID2); | ||
| 297 | - } | ||
| 298 | - AscendC::SetFlag<AscendC::HardEvent::V_MTE3>(EVENT_ID2); | ||
| 299 | - AscendC::WaitFlag<AscendC::HardEvent::V_MTE3>(EVENT_ID2); | ||
| 300 | - AscendC::DataCopy(gKeyScratch_[scratchOff], ubK, nElem); | ||
| 301 | } | 306 | } |
| 307 | + if (copyN < nElem) { | ||
| 308 | + AscendC::Duplicate(ubHalf, static_cast<half>(0), nElem); | ||
| 309 | + AscendC::PipeBarrier<PIPE_V>(); | ||
| 310 | + AscendC::SetFlag<AscendC::HardEvent::V_MTE2>(EVENT_ID2); | ||
| 311 | + AscendC::WaitFlag<AscendC::HardEvent::V_MTE2>(EVENT_ID2); | ||
| 312 | + } | ||
| 313 | + if (copyN > 0) { | ||
| 314 | + if (copyN == nElem) { | ||
| 315 | + AscendC::DataCopy(ubI8, gKeyInt8_[kOffset], nElem); | ||
| 316 | + } else { | ||
| 317 | + CopyGmToUbPartial(ubI8, gKeyInt8_[kOffset], copyN); | ||
| 318 | + } | ||
| 319 | + AscendC::SetFlag<AscendC::HardEvent::MTE2_V>(EVENT_ID2); | ||
| 320 | + AscendC::WaitFlag<AscendC::HardEvent::MTE2_V>(EVENT_ID2); | ||
| 321 | + AscendC::Cast(ubHalf, ubI8, AscendC::RoundMode::CAST_NONE, copyN); | ||
| 322 | + } | ||
| 323 | + AscendC::SetFlag<AscendC::HardEvent::V_MTE3>(EVENT_ID2); | ||
| 324 | + AscendC::WaitFlag<AscendC::HardEvent::V_MTE3>(EVENT_ID2); | ||
| 325 | + AscendC::DataCopy(gKeyScratch_[scratchOff], ubHalf, nElem); | ||
| 326 | + AscendC::SetFlag<AscendC::HardEvent::MTE3_MTE2>(EVENT_ID2); | ||
| 327 | + AscendC::WaitFlag<AscendC::HardEvent::MTE3_MTE2>(EVENT_ID2); | ||
| 328 | + } | ||
| 329 | + } | ||
| 330 | + | ||
| 331 | + /// AIV:把本 S-tile 可见 block 的 K 收进 per-core scratch(int8 cast 或 TND fp copy+尾填充)。 | ||
| 332 | + /// int8:两 AIV 按 j%subBlockNum 对分 page;非量化仍由调用方只让 AIV0 进来。 | ||
| 333 | + __aicore__ inline void GatherKeySTileToScratch(Catlass::Arch::Resource<ArchTag> &resource, const MsaTask &task, | ||
| 334 | + uint32_t blkBase, uint64_t kScratchBase, uint32_t subIdx, | ||
| 335 | + uint32_t subBlockNum) | ||
| 336 | + { | ||
| 337 | + if constexpr (IS_QUANT_V) { | ||
| 338 | + GatherKeySTileQuantPiped(resource, task, blkBase, kScratchBase, subIdx, subBlockNum); | ||
| 339 | + return; | ||
| 340 | + } | ||
| 341 | + AscendC::LocalTensor<ElementQ> ubK = resource.ubBuf.template GetBufferByByte<ElementQ>(UB_OFF_CAST_FP); | ||
| 342 | + const uint32_t headDim = tiling_->headDim; | ||
| 343 | + const uint32_t nElem = MSA_BLOCK_SIZE * headDim; | ||
| 344 | + const uint32_t pageStride = MSA_BLOCK_SIZE * MSA_K_TILE; | ||
| 345 | + const uint32_t split = (subBlockNum == 0U) ? 1U : subBlockNum; | ||
| 346 | + | ||
| 347 | + for (uint32_t j = 0; j < MSA_BLOCKS_PER_STILE; ++j) { | ||
| 348 | + if ((j % split) != subIdx) { | ||
| 349 | + continue; | ||
| 350 | + } | ||
| 351 | + const uint32_t blk = blkBase + j; | ||
| 352 | + if (blk >= task.visibleEndBlk) { | ||
| 353 | + continue; | ||
| 354 | + } | ||
| 355 | + if (!KeyBlockNeedsScratch(task, blk)) { | ||
| 356 | + continue; | ||
| 357 | + } | ||
| 358 | + const uint64_t kOffset = KeyBlockGmOffset(task, blk); | ||
| 359 | + const uint64_t scratchOff = kScratchBase + static_cast<uint64_t>(j) * pageStride; | ||
| 360 | + const uint32_t nValidTok = KeyBlockValidTokens(task, blk); | ||
| 361 | + const uint32_t nValid = nValidTok * headDim; | ||
| 362 | + | ||
| 363 | + if (nValid < nElem) { | ||
| 364 | + AscendC::Duplicate(ubK, static_cast<ElementQ>(0), nElem); | ||
| 365 | + AscendC::PipeBarrier<PIPE_V>(); | ||
| 366 | + AscendC::SetFlag<AscendC::HardEvent::V_MTE2>(EVENT_ID2); | ||
| 367 | + AscendC::WaitFlag<AscendC::HardEvent::V_MTE2>(EVENT_ID2); | ||
| 368 | + } | ||
| 369 | + if (nValid > 0) { | ||
| 370 | + CopyGmToUbPartial(ubK, gKeyFp_[kOffset], nValid); | ||
| 371 | + AscendC::SetFlag<AscendC::HardEvent::MTE2_V>(EVENT_ID2); | ||
| 372 | + AscendC::WaitFlag<AscendC::HardEvent::MTE2_V>(EVENT_ID2); | ||
| 373 | + } | ||
| 374 | + AscendC::SetFlag<AscendC::HardEvent::V_MTE3>(EVENT_ID2); | ||
| 375 | + AscendC::WaitFlag<AscendC::HardEvent::V_MTE3>(EVENT_ID2); | ||
| 376 | + AscendC::DataCopy(gKeyScratch_[scratchOff], ubK, nElem); | ||
| 302 | AscendC::SetFlag<AscendC::HardEvent::MTE3_MTE2>(EVENT_ID0); | 377 | AscendC::SetFlag<AscendC::HardEvent::MTE3_MTE2>(EVENT_ID0); |
| 303 | AscendC::WaitFlag<AscendC::HardEvent::MTE3_MTE2>(EVENT_ID0); | 378 | AscendC::WaitFlag<AscendC::HardEvent::MTE3_MTE2>(EVENT_ID0); |
| 304 | AscendC::PipeBarrier<PIPE_ALL>(); | 379 | AscendC::PipeBarrier<PIPE_ALL>(); |
| @@ -306,6 +381,46 @@ private: | |||
| 306 | AscendC::PipeBarrier<PIPE_ALL>(); | 381 | AscendC::PipeBarrier<PIPE_ALL>(); |
| 307 | } | 382 | } |
| 308 | 383 | ||
| 384 | + __aicore__ inline void NotifyKScratchReady(Catlass::Arch::CrossCoreFlag &flagK0, | ||
| 385 | + Catlass::Arch::CrossCoreFlag &flagK1, | ||
| 386 | + Catlass::Arch::CrossCoreFlag &flagK2, | ||
| 387 | + Catlass::Arch::CrossCoreFlag &flagK3, uint32_t st) | ||
| 388 | + { | ||
| 389 | + if constexpr (IS_QUANT_V) { | ||
| 390 | + const uint32_t slot = st % MSA_K_SCRATCH_STAGES_A2; | ||
| 391 | + if (slot == 0U) { | ||
| 392 | + Catlass::Arch::CrossCoreSetFlag<0x2, PIPE_MTE3>(flagK0); | ||
| 393 | + } else if (slot == 1U) { | ||
| 394 | + Catlass::Arch::CrossCoreSetFlag<0x2, PIPE_MTE3>(flagK1); | ||
| 395 | + } else if (slot == 2U) { | ||
| 396 | + Catlass::Arch::CrossCoreSetFlag<0x2, PIPE_MTE3>(flagK2); | ||
| 397 | + } else { | ||
| 398 | + Catlass::Arch::CrossCoreSetFlag<0x2, PIPE_MTE3>(flagK3); | ||
| 399 | + } | ||
| 400 | + } else { | ||
| 401 | + (void)flagK1; | ||
| 402 | + (void)flagK2; | ||
| 403 | + (void)flagK3; | ||
| 404 | + Catlass::Arch::CrossCoreSetFlag<0x2, PIPE_MTE3>(flagK0); | ||
| 405 | + } | ||
| 406 | + } | ||
| 407 | + | ||
| 408 | + __aicore__ inline void IssueKGatherAndNotify(Catlass::Arch::Resource<ArchTag> &resource, const MsaTask &task, | ||
| 409 | + uint32_t st, uint64_t coreKScratch, uint32_t subIdx, | ||
| 410 | + uint32_t subBlockNum, Catlass::Arch::CrossCoreFlag &flagK0, | ||
| 411 | + Catlass::Arch::CrossCoreFlag &flagK1, | ||
| 412 | + Catlass::Arch::CrossCoreFlag &flagK2, | ||
| 413 | + Catlass::Arch::CrossCoreFlag &flagK3) | ||
| 414 | + { | ||
| 415 | + if constexpr (IS_QUANT_V) { | ||
| 416 | + GatherKeySTileToScratch(resource, task, st * MSA_BLOCKS_PER_STILE, KScratchSlot(coreKScratch, st), subIdx, | ||
| 417 | + subBlockNum); | ||
| 418 | + } else if (subIdx == 0U) { | ||
| 419 | + GatherKeySTileToScratch(resource, task, st * MSA_BLOCKS_PER_STILE, KScratchSlot(coreKScratch, st), 0U, 1U); | ||
| 420 | + } | ||
| 421 | + NotifyKScratchReady(flagK0, flagK1, flagK2, flagK3, st); | ||
| 422 | + } | ||
| 423 | + | ||
| 309 | __aicore__ inline void ComputeSTile(BlockMmad &blockMmad, const MsaTask &task, uint32_t blkBase, uint64_t sBase, | 424 | __aicore__ inline void ComputeSTile(BlockMmad &blockMmad, const MsaTask &task, uint32_t blkBase, uint64_t sBase, |
| 310 | uint64_t kScratchBase, bool needLoadL1) | 425 | uint64_t kScratchBase, bool needLoadL1) |
| 311 | { | 426 | { |
| @@ -345,14 +460,18 @@ private: | |||
| 345 | tiling_->maxBlocksPerBatch, tiling_->strideScalePage, tiling_->strideScaleHead, IS_QUANT_V, | 460 | tiling_->maxBlocksPerBatch, tiling_->strideScalePage, tiling_->strideScaleHead, IS_QUANT_V, |
| 346 | tiling_->keyLayout, tiling_->totalK, gScore_, gScale_, gBlockTable_); | 461 | tiling_->keyLayout, tiling_->totalK, gScore_, gScale_, gBlockTable_); |
| 347 | Catlass::Arch::CrossCoreFlagWithReverse<REVERSE_DEPTH> flagSReady{MSA_FLAG_S_READY, MSA_FLAG_S_READY_REVERSE}; | 462 | Catlass::Arch::CrossCoreFlagWithReverse<REVERSE_DEPTH> flagSReady{MSA_FLAG_S_READY, MSA_FLAG_S_READY_REVERSE}; |
| 348 | - Catlass::Arch::CrossCoreFlag flagKReady{MSA_FLAG_K_READY}; | 463 | + Catlass::Arch::CrossCoreFlag flagK0{MSA_FLAG_K_READY}; |
| 464 | + Catlass::Arch::CrossCoreFlag flagK1{MSA_FLAG_K_READY_REVERSE}; | ||
| 465 | + Catlass::Arch::CrossCoreFlag flagK2{MSA_FLAG_K_READY_2}; | ||
| 466 | + Catlass::Arch::CrossCoreFlag flagK3{MSA_FLAG_K_READY_3}; | ||
| 349 | 467 | ||
| 350 | const uint32_t subBlockNum = AscendC::GetSubBlockNum(); | 468 | const uint32_t subBlockNum = AscendC::GetSubBlockNum(); |
| 351 | const uint32_t subIdx = AscendC::GetSubBlockIdx(); | 469 | const uint32_t subIdx = AscendC::GetSubBlockIdx(); |
| 352 | const uint32_t coreIdx = AscendC::GetBlockIdx() / subBlockNum; | 470 | const uint32_t coreIdx = AscendC::GetBlockIdx() / subBlockNum; |
| 353 | const uint32_t coreNum = AscendC::GetBlockNum(); | 471 | const uint32_t coreNum = AscendC::GetBlockNum(); |
| 354 | const uint64_t coreWsBase = static_cast<uint64_t>(coreIdx) * MSA_WORKSPACE_STAGES * MSA_STILE_ELEM_NUM; | 472 | const uint64_t coreWsBase = static_cast<uint64_t>(coreIdx) * MSA_WORKSPACE_STAGES * MSA_STILE_ELEM_NUM; |
| 355 | - const uint64_t coreKScratch = static_cast<uint64_t>(coreIdx) * MSA_K_SCRATCH_ELEM_NUM; | 473 | + const uint32_t kStages = IS_QUANT_V ? MSA_K_SCRATCH_STAGES_A2 : 1U; |
| 474 | + const uint64_t coreKScratch = static_cast<uint64_t>(coreIdx) * kStages * MSA_K_SCRATCH_ELEM_NUM; | ||
| 356 | const uint32_t totalTasks = scheduler_.TotalTasks(); | 475 | const uint32_t totalTasks = scheduler_.TotalTasks(); |
| 357 | 476 | ||
| 358 | uint32_t tileSeq = 0; | 477 | uint32_t tileSeq = 0; |
| @@ -360,20 +479,41 @@ private: | |||
| 360 | for (uint32_t taskIdx = coreIdx; taskIdx < totalTasks; taskIdx += coreNum) { | 479 | for (uint32_t taskIdx = coreIdx; taskIdx < totalTasks; taskIdx += coreNum) { |
| 361 | scheduler_.Decode(taskIdx, task); | 480 | scheduler_.Decode(taskIdx, task); |
| 362 | epilogue.BeginTask(task, subIdx, subBlockNum); | 481 | epilogue.BeginTask(task, subIdx, subBlockNum); |
| 363 | - for (uint32_t st = 0; st < task.numComputeSTiles; ++st) { | 482 | + if constexpr (IS_QUANT_V) { |
| 364 | - if (StileNeedsKScratch(task, st * MSA_BLOCKS_PER_STILE)) { | 483 | + // 四槽:先填 0/1/2/3。WaitS(st) 后 slot[st%4] 空闲,gather(st+4) 叠在 |
| 365 | - // AIV0 写 scratch;AIV0/AIV1 barrier 后双方都 Set,满足 1:2 MIX 握手。 | 484 | + // cube(st+1..st+3) 上。两 AIV 对分 page,每页串行 MTE2→V→MTE3。 |
| 366 | - if (subIdx == 0) { | 485 | + const uint32_t nSt = task.numComputeSTiles; |
| 367 | - GatherKeySTileToScratch(resource, task, st * MSA_BLOCKS_PER_STILE, coreKScratch); | 486 | + const uint32_t nPref = (nSt < MSA_K_SCRATCH_STAGES_A2) ? nSt : MSA_K_SCRATCH_STAGES_A2; |
| 487 | + for (uint32_t p = 0; p < nPref; ++p) { | ||
| 488 | + if (StileNeedsKScratch(task, p * MSA_BLOCKS_PER_STILE)) { | ||
| 489 | + IssueKGatherAndNotify(resource, task, p, coreKScratch, subIdx, subBlockNum, flagK0, flagK1, | ||
| 490 | + flagK2, flagK3); | ||
| 368 | } | 491 | } |
| 369 | - Catlass::Arch::CrossCoreBarrier<0x1, PIPE_MTE3>(); | ||
| 370 | - Catlass::Arch::CrossCoreSetFlag<0x2, PIPE_MTE3>(flagKReady); | ||
| 371 | } | 492 | } |
| 372 | - Catlass::Arch::CrossCoreWaitFlagWithReverse<0x2, PIPE_MTE3>(flagSReady); | 493 | + for (uint32_t st = 0; st < nSt; ++st) { |
| 373 | - const uint64_t sBase = | 494 | + Catlass::Arch::CrossCoreWaitFlagWithReverse<0x2, PIPE_MTE3>(flagSReady); |
| 374 | - coreWsBase + static_cast<uint64_t>(tileSeq % MSA_WORKSPACE_STAGES) * MSA_STILE_ELEM_NUM; | 495 | + const uint32_t stPref = st + MSA_K_SCRATCH_STAGES_A2; |
| 375 | - epilogue.ProcessSTile(gWorkspace_[sBase], task, st * MSA_BLOCKS_PER_STILE); | 496 | + const uint64_t sBase = |
| 376 | - ++tileSeq; | 497 | + coreWsBase + static_cast<uint64_t>(tileSeq % MSA_WORKSPACE_STAGES) * MSA_STILE_ELEM_NUM; |
| 498 | + if (stPref < nSt && StileNeedsKScratch(task, stPref * MSA_BLOCKS_PER_STILE)) { | ||
| 499 | + IssueKGatherAndNotify(resource, task, stPref, coreKScratch, subIdx, subBlockNum, flagK0, flagK1, | ||
| 500 | + flagK2, flagK3); | ||
| 501 | + } | ||
| 502 | + epilogue.ProcessSTile(gWorkspace_[sBase], task, st * MSA_BLOCKS_PER_STILE); | ||
| 503 | + ++tileSeq; | ||
| 504 | + } | ||
| 505 | + } else { | ||
| 506 | + for (uint32_t st = 0; st < task.numComputeSTiles; ++st) { | ||
| 507 | + if (StileNeedsKScratch(task, st * MSA_BLOCKS_PER_STILE)) { | ||
| 508 | + IssueKGatherAndNotify(resource, task, st, coreKScratch, subIdx, subBlockNum, flagK0, flagK1, | ||
| 509 | + flagK2, flagK3); | ||
| 510 | + } | ||
| 511 | + Catlass::Arch::CrossCoreWaitFlagWithReverse<0x2, PIPE_MTE3>(flagSReady); | ||
| 512 | + const uint64_t sBase = | ||
| 513 | + coreWsBase + static_cast<uint64_t>(tileSeq % MSA_WORKSPACE_STAGES) * MSA_STILE_ELEM_NUM; | ||
| 514 | + epilogue.ProcessSTile(gWorkspace_[sBase], task, st * MSA_BLOCKS_PER_STILE); | ||
| 515 | + ++tileSeq; | ||
| 516 | + } | ||
| 377 | } | 517 | } |
| 378 | // 因果不可见尾:不握手,直接写 -inf(与 AIC 跳过这些 tile 对齐)。 | 518 | // 因果不可见尾:不握手,直接写 -inf(与 AIC 跳过这些 tile 对齐)。 |
| 379 | for (uint32_t st = task.numComputeSTiles; st < task.numSTiles; ++st) { | 519 | for (uint32_t st = task.numComputeSTiles; st < task.numSTiles; ++st) { |
| @@ -33,6 +33,17 @@ __aicore__ inline uint32_t MsaCeilDiv(uint32_t a, uint32_t b) | |||
| 33 | return (a + b - 1) / b; | 33 | return (a + b - 1) / b; |
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | +// C2UB 非量化 F322F16 单目标:两次 Fixpipe 的 mSize 切分(每段偶数),AIV 必须相同。 | ||
| 37 | +__aicore__ inline void MsaC2UbHalfSplit(uint32_t mActual, uint32_t &m0, uint32_t &m1) | ||
| 38 | +{ | ||
| 39 | + const uint32_t mEven = (mActual + 1U) & ~1U; | ||
| 40 | + m0 = mEven / 2U; | ||
| 41 | + if ((m0 & 1U) != 0U) { | ||
| 42 | + m0 += 1U; | ||
| 43 | + } | ||
| 44 | + m1 = mEven - m0; | ||
| 45 | +} | ||
| 46 | + | ||
| 36 | __aicore__ inline uint32_t MsaMinU32(uint32_t a, uint32_t b) | 47 | __aicore__ inline uint32_t MsaMinU32(uint32_t a, uint32_t b) |
| 37 | { | 48 | { |
| 38 | return a < b ? a : b; | 49 | return a < b ? a : b; |
| @@ -144,7 +155,7 @@ public: | |||
| 144 | task.mStart = (taskIdx - taskBase_) * MSA_ROW_TILE_M; | 155 | task.mStart = (taskIdx - taskBase_) * MSA_ROW_TILE_M; |
| 145 | task.mActual = MsaMinU32(MSA_ROW_TILE_M, curRows_ - task.mStart); | 156 | task.mActual = MsaMinU32(MSA_ROW_TILE_M, curRows_ - task.mStart); |
| 146 | // Cube L0A fractal 为 MSA_M_ALIGN 行。整请求不足对齐宽度时保持原样(L0 已覆盖); | 157 | // Cube L0A fractal 为 MSA_M_ALIGN 行。整请求不足对齐宽度时保持原样(L0 已覆盖); |
| 147 | - // 否则把末尾短 tile 向前重叠到 MSA_M_ALIGN 行,避免 mActual∈(0,16) 的 int8 路径出错。 | 158 | + // 否则把末尾短 tile 向前重叠到 MSA_M_ALIGN 行,避免 mActual ∈ (0,16) 的 int8 路径出错。 |
| 148 | if (task.mActual > 0U && task.mActual < MSA_M_ALIGN && task.mStart >= MSA_M_ALIGN) { | 159 | if (task.mActual > 0U && task.mActual < MSA_M_ALIGN && task.mStart >= MSA_M_ALIGN) { |
| 149 | task.mStart -= (MSA_M_ALIGN - task.mActual); | 160 | task.mStart -= (MSA_M_ALIGN - task.mActual); |
| 150 | task.mActual = MSA_M_ALIGN; | 161 | task.mActual = MSA_M_ALIGN; |
Rattention/msa_index_score/op_kernel/arch22/msa_index_score_epilogue.h→attention/msa_index_score/op_kernel/arch22/msa_seg_row_max_epilogue.h+48-14
| @@ -9,7 +9,7 @@ | |||
| 9 | */ | 9 | */ |
| 10 | 10 | ||
| 11 | /*! | 11 | /*! |
| 12 | - * \file msa_index_score_epilogue.h | 12 | + * \file msa_seg_row_max_epilogue.h |
| 13 | * \brief AIV 侧 Epilogue:可选反量化列乘 -> atten_mask -> 分段 RowMax -> local_mask -> 写回。 | 13 | * \brief AIV 侧 Epilogue:可选反量化列乘 -> atten_mask -> 分段 RowMax -> local_mask -> 写回。 |
| 14 | * | 14 | * |
| 15 | * 数值路径: | 15 | * 数值路径: |
| @@ -18,8 +18,8 @@ | |||
| 18 | * local_mask 由 start_loc(query 所在逻辑 block)+ init/local_blocks 生成强制 +∞。 | 18 | * local_mask 由 start_loc(query 所在逻辑 block)+ init/local_blocks 生成强制 +∞。 |
| 19 | */ | 19 | */ |
| 20 | 20 | ||
| 21 | -#ifndef MSA_INDEX_SCORE_EPILOGUE_H | 21 | +#ifndef MSA_SEG_ROW_MAX_EPILOGUE_H |
| 22 | -#define MSA_INDEX_SCORE_EPILOGUE_H | 22 | +#define MSA_SEG_ROW_MAX_EPILOGUE_H |
| 23 | 23 | ||
| 24 | #include "kernel_operator.h" | 24 | #include "kernel_operator.h" |
| 25 | 25 | ||
| @@ -40,8 +40,9 @@ public: | |||
| 40 | 40 | ||
| 41 | // score 暂存容量:1 AIC : 2 AIV 下每个 subcore 最多拿到半个 M-tile 的行; | 41 | // score 暂存容量:1 AIC : 2 AIV 下每个 subcore 最多拿到半个 M-tile 的行; |
| 42 | // 一次 flush 覆盖 MSA_STAGE_BLOCKS 个 block(= 每行 1KB 连续写回)。 | 42 | // 一次 flush 覆盖 MSA_STAGE_BLOCKS 个 block(= 每行 1KB 连续写回)。 |
| 43 | + // int8:缩到 160,给 UB 尾部腾出整页 cast(48KB);非量化保持 256。 | ||
| 43 | static constexpr uint32_t MSA_STAGE_ROWS = MSA_ROW_TILE_M / MSA_AIV_PER_AIC; | 44 | static constexpr uint32_t MSA_STAGE_ROWS = MSA_ROW_TILE_M / MSA_AIV_PER_AIC; |
| 44 | - static constexpr uint32_t MSA_STAGE_BLOCKS = 256; | 45 | + static constexpr uint32_t MSA_STAGE_BLOCKS = IS_QUANT ? 160 : 256; |
| 45 | static_assert(MSA_STAGE_BLOCKS % MSA_BLOCKS_PER_STILE == 0, "stage window must hold whole S-tiles"); | 46 | static_assert(MSA_STAGE_BLOCKS % MSA_BLOCKS_PER_STILE == 0, "stage window must hold whole S-tiles"); |
| 46 | 47 | ||
| 47 | // UB 手工布局(字节偏移),catlass 不提供 UB allocator。 | 48 | // UB 手工布局(字节偏移),catlass 不提供 UB allocator。 |
| @@ -59,18 +60,19 @@ public: | |||
| 59 | // 非量化:WholeReduceMax 的 fp16 输出(每 pass MSA_REDUCE_ROWS 个 half)。 | 60 | // 非量化:WholeReduceMax 的 fp16 输出(每 pass MSA_REDUCE_ROWS 个 half)。 |
| 60 | static constexpr uint32_t UB_OFF_RED16 = UB_OFF_DEQ + UB_SIZE_DEQ; | 61 | static constexpr uint32_t UB_OFF_RED16 = UB_OFF_DEQ + UB_SIZE_DEQ; |
| 61 | static constexpr uint32_t UB_SIZE_RED16 = MSA_REDUCE_ROWS * sizeof(half); | 62 | static constexpr uint32_t UB_SIZE_RED16 = MSA_REDUCE_ROWS * sizeof(half); |
| 62 | - // score 暂存:本 subcore 的行 × 一次 flush 覆盖的 block 数。逐 pass 只写 32B 到 GM | 63 | + // score 暂存:本 subcore 的行 × 一次 flush 覆盖的 block 数。 |
| 63 | - // 会把 MTE3 压到 ~9GB/s;改为在 UB 内按行累积、整行(≥1KB)一次写回。 | 64 | + // 在 UB 内按行累积、整行(≥1KB)一次写回。 |
| 64 | static constexpr uint32_t UB_OFF_STAGE = | 65 | static constexpr uint32_t UB_OFF_STAGE = |
| 65 | ((UB_OFF_RED16 + UB_SIZE_RED16 + MSA_UB_ALIGN_BYTES - 1U) / MSA_UB_ALIGN_BYTES) * MSA_UB_ALIGN_BYTES; | 66 | ((UB_OFF_RED16 + UB_SIZE_RED16 + MSA_UB_ALIGN_BYTES - 1U) / MSA_UB_ALIGN_BYTES) * MSA_UB_ALIGN_BYTES; |
| 66 | static constexpr uint32_t UB_SIZE_STAGE = MSA_STAGE_ROWS * MSA_STAGE_BLOCKS * sizeof(float); | 67 | static constexpr uint32_t UB_SIZE_STAGE = MSA_STAGE_ROWS * MSA_STAGE_BLOCKS * sizeof(float); |
| 67 | // 非量化:S16(fp16 载入缓冲)复用量化路径的 fp32 S 区(64KB = 两级 32KB 乒乓)。 | 68 | // 非量化:S16(fp16 载入缓冲)复用量化路径的 fp32 S 区(64KB = 两级 32KB 乒乓)。 |
| 68 | - // v0.7 PipeUtilization:aiv_mte2_wait_ratio≈0.84、aiv_mte2_ratio≈0.33、aiv_vec_ratio≈0.57, | ||
| 69 | // 说明 GM→UB 与归约串行;两级缓冲让下一 pass 的 MTE2 叠在当前 pass 的 V 上。 | 69 | // 说明 GM→UB 与归约串行;两级缓冲让下一 pass 的 MTE2 叠在当前 pass 的 V 上。 |
| 70 | static constexpr uint32_t UB_OFF_S16 = UB_OFF_S; | 70 | static constexpr uint32_t UB_OFF_S16 = UB_OFF_S; |
| 71 | static constexpr uint32_t UB_SIZE_S16 = MSA_ROWS_PER_PASS * MSA_BLOCKS_PER_STILE * MSA_BLOCK_SIZE * sizeof(half); | 71 | static constexpr uint32_t UB_SIZE_S16 = MSA_ROWS_PER_PASS * MSA_BLOCKS_PER_STILE * MSA_BLOCK_SIZE * sizeof(half); |
| 72 | static constexpr uint32_t S16_STAGES = 2; | 72 | static constexpr uint32_t S16_STAGES = 2; |
| 73 | static constexpr uint32_t UB_TOTAL = UB_OFF_STAGE + UB_SIZE_STAGE; | 73 | static constexpr uint32_t UB_TOTAL = UB_OFF_STAGE + UB_SIZE_STAGE; |
| 74 | + static constexpr uint32_t UB_OFF_TAIL = | ||
| 75 | + ((UB_TOTAL + MSA_UB_ALIGN_BYTES - 1U) / MSA_UB_ALIGN_BYTES) * MSA_UB_ALIGN_BYTES; | ||
| 74 | static_assert(UB_TOTAL <= ArchTag::UB_SIZE, "MsaSegRowMaxEpilogue UB out of bounds"); | 76 | static_assert(UB_TOTAL <= ArchTag::UB_SIZE, "MsaSegRowMaxEpilogue UB out of bounds"); |
| 75 | static_assert(UB_OFF_S16 + S16_STAGES * UB_SIZE_S16 <= UB_OFF_T64, "S16 ping-pong overlaps T64"); | 77 | static_assert(UB_OFF_S16 + S16_STAGES * UB_SIZE_S16 <= UB_OFF_T64, "S16 ping-pong overlaps T64"); |
| 76 | 78 | ||
| @@ -149,6 +151,42 @@ public: | |||
| 149 | } | 151 | } |
| 150 | } | 152 | } |
| 151 | 153 | ||
| 154 | + __aicore__ inline uint32_t SubRows() const | ||
| 155 | + { | ||
| 156 | + return mSub_; | ||
| 157 | + } | ||
| 158 | + | ||
| 159 | + __aicore__ inline uint32_t SubOff() const | ||
| 160 | + { | ||
| 161 | + return mOff_; | ||
| 162 | + } | ||
| 163 | + | ||
| 164 | + __aicore__ inline void BeginSTile(uint32_t blkBase) | ||
| 165 | + { | ||
| 166 | + if (mSub_ == 0U) { | ||
| 167 | + return; | ||
| 168 | + } | ||
| 169 | + if (stageOn_ && (blkBase >= stageBlkBase_ + MSA_STAGE_BLOCKS)) { | ||
| 170 | + FlushStage(); | ||
| 171 | + stageBlkBase_ = blkBase; | ||
| 172 | + stageBlkEnd_ = blkBase; | ||
| 173 | + } | ||
| 174 | + } | ||
| 175 | + | ||
| 176 | + __aicore__ inline void FinishSTile(uint32_t blkBase) | ||
| 177 | + { | ||
| 178 | + if (mSub_ == 0U) { | ||
| 179 | + return; | ||
| 180 | + } | ||
| 181 | + stageBlkEnd_ = blkBase + MSA_BLOCKS_PER_STILE; | ||
| 182 | + } | ||
| 183 | + | ||
| 184 | + __aicore__ inline void ProcessOnePass(const AscendC::GlobalTensor<ElementS> &gS, const MsaTask &task, | ||
| 185 | + uint32_t blkBase, uint32_t rowOff, uint32_t rows) | ||
| 186 | + { | ||
| 187 | + ProcessPass(gS, task, blkBase, rowOff, rows); | ||
| 188 | + } | ||
| 189 | + | ||
| 152 | /// 处理一个 S tile(MSA_BLOCKS_PER_STILE 个 sparse block)中属于本 subcore 的行。 | 190 | /// 处理一个 S tile(MSA_BLOCKS_PER_STILE 个 sparse block)中属于本 subcore 的行。 |
| 153 | __aicore__ inline void ProcessSTile(const AscendC::GlobalTensor<ElementS> &gS, const MsaTask &task, | 191 | __aicore__ inline void ProcessSTile(const AscendC::GlobalTensor<ElementS> &gS, const MsaTask &task, |
| 154 | uint32_t blkBase) | 192 | uint32_t blkBase) |
| @@ -158,11 +196,7 @@ public: | |||
| 158 | } | 196 | } |
| 159 | const uint32_t mOff = mOff_; | 197 | const uint32_t mOff = mOff_; |
| 160 | const uint32_t mSub = mSub_; | 198 | const uint32_t mSub = mSub_; |
| 161 | - if (stageOn_ && (blkBase >= stageBlkBase_ + MSA_STAGE_BLOCKS)) { | 199 | + BeginSTile(blkBase); |
| 162 | - FlushStage(); | ||
| 163 | - stageBlkBase_ = blkBase; | ||
| 164 | - stageBlkEnd_ = blkBase; | ||
| 165 | - } | ||
| 166 | 200 | ||
| 167 | if constexpr (IS_QUANT) { | 201 | if constexpr (IS_QUANT) { |
| 168 | for (uint32_t p0 = 0; p0 < mSub; p0 += MSA_ROWS_PER_PASS) { | 202 | for (uint32_t p0 = 0; p0 < mSub; p0 += MSA_ROWS_PER_PASS) { |
| @@ -202,7 +236,7 @@ public: | |||
| 202 | ReleaseS16(prevStage); | 236 | ReleaseS16(prevStage); |
| 203 | } | 237 | } |
| 204 | } | 238 | } |
| 205 | - stageBlkEnd_ = blkBase + MSA_BLOCKS_PER_STILE; | 239 | + FinishSTile(blkBase); |
| 206 | } | 240 | } |
| 207 | 241 | ||
| 208 | private: | 242 | private: |
| @@ -668,4 +702,4 @@ private: | |||
| 668 | 702 | ||
| 669 | } // namespace MsaIndexScoreNs | 703 | } // namespace MsaIndexScoreNs |
| 670 | 704 | ||
| 671 | -#endif // MSA_INDEX_SCORE_EPILOGUE_H | 705 | +#endif // MSA_SEG_ROW_MAX_EPILOGUE_H |
| @@ -0,0 +1,380 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | +/*! | ||
| 12 | + * \file msa_block_mmad.h | ||
| 13 | + * \brief Atlas A5 BlockMmad:Q 驻留 + K pingpong;C_to_UB 时 L0C Fixpipe dual-dst → AIV UB。 | ||
| 14 | + * | ||
| 15 | + * 基于 Catlass FullLoadA:同一 M-tile 只搬一次 Q;相邻 K page 的 FIXPIPE 与 Cube 重叠。 | ||
| 16 | + * Atlas A5:计算路径照搬 A2;L0C=256KB,两块 128x128 fp32 C 各 64KB。 | ||
| 17 | + * C_to_UB:int8 dual-dst 写 fp32。非量化 dual-dst 禁止 quantPre!=NoQuant(CANN 断言); | ||
| 18 | + * 改为两次 dualDstCtl=0 + subBlockId 的 F322F16,写出 packed [M/2,128] fp16(对齐 FA)。 | ||
| 19 | + */ | ||
| 20 | + | ||
| 21 | +#ifndef MSA_BLOCK_MMAD_ARCH35_H | ||
| 22 | +#define MSA_BLOCK_MMAD_ARCH35_H | ||
| 23 | + | ||
| 24 | +#include <type_traits> | ||
| 25 | + | ||
| 26 | +#include "catlass/msa_catlass.hpp" | ||
| 27 | +#include "catlass/arch/msa_arch.hpp" | ||
| 28 | +#include "catlass/arch/msa_resource.hpp" | ||
| 29 | +#include "catlass/msa_coord.hpp" | ||
| 30 | +#include "catlass/msa_matrix_coord.hpp" | ||
| 31 | +#include "catlass/msa_gemm_coord.hpp" | ||
| 32 | +#include "catlass/layout/msa_layout.hpp" | ||
| 33 | +#include "catlass/gemm/msa_gemm_helper.hpp" | ||
| 34 | +#include "catlass/gemm/tile/msa_tile_copy.hpp" | ||
| 35 | +#include "catlass/gemm/tile/msa_tile_mmad.hpp" | ||
| 36 | + | ||
| 37 | +#include "../msa_index_score_common.h" | ||
| 38 | +#include "../arch22/msa_index_score_task.h" | ||
| 39 | + | ||
| 40 | +namespace MsaIndexScoreNs { | ||
| 41 | + | ||
| 42 | +template <class AType_, class BType_, class CType_, uint32_t STAGES_IN = MSA_L1B_STAGES_FP16, | ||
| 43 | + bool USE_UNIT_FLAG_ = false> | ||
| 44 | +class MsaBlockMmad { | ||
| 45 | +public: | ||
| 46 | + using ArchTag = Catlass::Arch::AtlasA5; | ||
| 47 | + using ElementA = typename AType_::Element; | ||
| 48 | + using LayoutA = typename AType_::Layout; | ||
| 49 | + using ElementB = typename BType_::Element; | ||
| 50 | + using LayoutB = typename BType_::Layout; | ||
| 51 | + using ElementC = typename CType_::Element; | ||
| 52 | + using LayoutC = typename CType_::Layout; | ||
| 53 | + using BiasType = void; | ||
| 54 | + using TileCopy = Catlass::Gemm::Tile::TileCopy<ArchTag, AType_, BType_, CType_, BiasType>; | ||
| 55 | + using TileMmad = Catlass::Gemm::Tile::TileMmad<ArchTag, AType_, BType_, BiasType>; | ||
| 56 | + using CopyGmToL1A = typename TileCopy::CopyGmToL1A; | ||
| 57 | + using CopyGmToL1B = typename TileCopy::CopyGmToL1B; | ||
| 58 | + using CopyL1ToL0A = typename TileCopy::CopyL1ToL0A; | ||
| 59 | + using CopyL1ToL0B = typename TileCopy::CopyL1ToL0B; | ||
| 60 | + using CopyL0CToGm = typename TileCopy::CopyL0CToGm; | ||
| 61 | + using ElementAccumulator = | ||
| 62 | + typename Catlass::Gemm::helper::ElementAccumulatorSelector<ElementA, ElementB>::ElementAccumulator; | ||
| 63 | + using LayoutAInL1 = typename CopyL1ToL0A::LayoutSrc; | ||
| 64 | + using LayoutBInL1 = typename CopyL1ToL0B::LayoutSrc; | ||
| 65 | + using LayoutAInL0 = typename CopyL1ToL0A::LayoutDst; | ||
| 66 | + using LayoutBInL0 = typename CopyL1ToL0B::LayoutDst; | ||
| 67 | + using LayoutCInL0 = Catlass::layout::zN; | ||
| 68 | + using L1AAlignHelper = Catlass::Gemm::helper::L1AlignHelper<ElementA, LayoutA>; | ||
| 69 | + using L1BAlignHelper = Catlass::Gemm::helper::L1AlignHelper<ElementB, LayoutB>; | ||
| 70 | + | ||
| 71 | + // K (B) 的 L1 流水级数。非量化 3 级:fixpipe 减半(fp16 S)后 MTE2 的逐页延迟 | ||
| 72 | + // (~390ns,L2 命中)会成为新的关键路径,加深一级让 MTE2 提前两页预取。 | ||
| 73 | + // int8 保持 2 级:K 源是每 stile 复用的 per-core scratch,AIV 下一 stile 的 cast | ||
| 74 | + // 会重写该区,更深的预取与 cast 重写产生竞态(实测 STAGES=3 下 int8 崩溃)。 | ||
| 75 | + // L1 占用:A 32KB + 3×32KB B = 128KB / 512KB。 | ||
| 76 | + static constexpr uint32_t STAGES = STAGES_IN; | ||
| 77 | + // 非量化路径开 unit flag:mmad 与 fixpipe 的依赖交给硬件互锁,省掉每页 | ||
| 78 | + // M_FIX/FIX_M 四次标量 set/wait。标量因此能在 cube 完成前就发出下一页的 | ||
| 79 | + // MTE2/MTE1,把 GM→L1 的搬运藏到 cube/fixpipe 后面。 | ||
| 80 | + static constexpr bool USE_UNIT_FLAG = USE_UNIT_FLAG_; | ||
| 81 | + static constexpr uint32_t L0C_STAGES = 2; | ||
| 82 | + static constexpr uint32_t L1_M = MSA_ROW_TILE_M; | ||
| 83 | + static constexpr uint32_t L1_N = MSA_BLOCK_SIZE; | ||
| 84 | + static constexpr uint32_t L1_K = MSA_K_TILE; | ||
| 85 | + static constexpr uint32_t L1A_SIZE = L1_M * L1_K * sizeof(ElementA); | ||
| 86 | + static constexpr uint32_t L1B_SIZE = L1_N * L1_K * sizeof(ElementB); | ||
| 87 | + // L0A/L0B 各仅 64KB,B tile 32KB → 乒乓固定 2 级(32KB/级);与 L1B STAGES 解耦。 | ||
| 88 | + static constexpr uint32_t L0A_STAGES = 2; | ||
| 89 | + static constexpr uint32_t L0A_RESIDENT_ID = 0; | ||
| 90 | + static constexpr uint32_t L0A_FALLBACK_ID = 1; | ||
| 91 | + static constexpr uint32_t L0B_STAGES = 2; | ||
| 92 | + static constexpr uint32_t L0A_PINGPONG_BUF_SIZE = ArchTag::L0A_SIZE / L0A_STAGES; | ||
| 93 | + static constexpr uint32_t L0B_PINGPONG_BUF_SIZE = ArchTag::L0B_SIZE / L0B_STAGES; | ||
| 94 | + static constexpr uint32_t L0C_TILE_SIZE = L1_M * L1_N * sizeof(ElementAccumulator); | ||
| 95 | + | ||
| 96 | + static_assert(std::is_same_v<LayoutC, Catlass::layout::RowMajor>, "LayoutC must be RowMajor (fixpipe nz2nd)"); | ||
| 97 | + // A2 的 L0A/L0B 各仅 64KB:A/B tile 32KB,乒乓最多 2 级(32KB/级)。 | ||
| 98 | + static_assert(L0A_PINGPONG_BUF_SIZE >= L1A_SIZE, "L0A pingpong buf smaller than A tile"); | ||
| 99 | + static_assert(L0B_PINGPONG_BUF_SIZE >= L1B_SIZE, "L0B pingpong buf smaller than B tile"); | ||
| 100 | + static_assert(L0C_TILE_SIZE * L0C_STAGES <= ArchTag::L0C_SIZE, "L0C pingpong exceeds L0C"); | ||
| 101 | + static_assert(L1A_SIZE + L1B_SIZE * STAGES <= ArchTag::L1_SIZE, "L1 A+B exceeds L1"); | ||
| 102 | + | ||
| 103 | + __aicore__ inline MsaBlockMmad(Catlass::Arch::Resource<ArchTag> &resource) | ||
| 104 | + { | ||
| 105 | + l1ATensor_ = resource.l1Buf.template GetBufferByByte<ElementA>(0); | ||
| 106 | + const uint32_t l1BOffset = L1A_SIZE; | ||
| 107 | + for (uint32_t i = 0; i < STAGES; ++i) { | ||
| 108 | + l1BTensorList_[i] = resource.l1Buf.template GetBufferByByte<ElementB>(l1BOffset + L1B_SIZE * i); | ||
| 109 | + l1BEventList_[i] = static_cast<int32_t>(i + STAGES); | ||
| 110 | + AscendC::SetFlag<AscendC::HardEvent::MTE1_MTE2>(l1BEventList_[i]); | ||
| 111 | + } | ||
| 112 | + // L0A 槽 0 专供常驻 Q:不预置 M_MTE1,改用 LoadA 内「Set+Wait 成对」自平衡, | ||
| 113 | + // 避免计数型 flag 让 MTE1 用构造时的余额提前放行、覆盖仍被 mmad 读取的 Q。 | ||
| 114 | + // 槽 1 供 kTileCount>1 的回退路径(每 k-tile 重搬 A),沿用预置 + 析构等待。 | ||
| 115 | + for (uint32_t i = 0; i < L0A_STAGES; ++i) { | ||
| 116 | + l0ATensorList_[i] = resource.l0ABuf.template GetBufferByByte<ElementA>(L0A_PINGPONG_BUF_SIZE * i); | ||
| 117 | + l0AEventList_[i] = static_cast<int32_t>(i); | ||
| 118 | + } | ||
| 119 | + AscendC::SetFlag<AscendC::HardEvent::M_MTE1>(l0AEventList_[L0A_FALLBACK_ID]); | ||
| 120 | + for (uint32_t i = 0; i < L0B_STAGES; ++i) { | ||
| 121 | + l0BTensorList_[i] = resource.l0BBuf.template GetBufferByByte<ElementB>(L0B_PINGPONG_BUF_SIZE * i); | ||
| 122 | + l0BEventList_[i] = static_cast<int32_t>(i + STAGES + L0A_STAGES); | ||
| 123 | + AscendC::SetFlag<AscendC::HardEvent::M_MTE1>(l0BEventList_[i]); | ||
| 124 | + } | ||
| 125 | + for (uint32_t i = 0; i < L0C_STAGES; ++i) { | ||
| 126 | + l0CTensorList_[i] = resource.l0CBuf.template GetBufferByByte<ElementAccumulator>(L0C_TILE_SIZE * i); | ||
| 127 | + l0CEventList_[i] = static_cast<int32_t>(i); | ||
| 128 | + AscendC::SetFlag<AscendC::HardEvent::FIX_M>(l0CEventList_[i]); | ||
| 129 | + } | ||
| 130 | + } | ||
| 131 | + | ||
| 132 | + __aicore__ inline ~MsaBlockMmad() | ||
| 133 | + { | ||
| 134 | + for (uint32_t i = 0; i < STAGES; ++i) { | ||
| 135 | + AscendC::WaitFlag<AscendC::HardEvent::MTE1_MTE2>(l1BEventList_[i]); | ||
| 136 | + } | ||
| 137 | + AscendC::WaitFlag<AscendC::HardEvent::M_MTE1>(l0AEventList_[L0A_FALLBACK_ID]); | ||
| 138 | + for (uint32_t i = 0; i < L0B_STAGES; ++i) { | ||
| 139 | + AscendC::WaitFlag<AscendC::HardEvent::M_MTE1>(l0BEventList_[i]); | ||
| 140 | + } | ||
| 141 | + for (uint32_t i = 0; i < L0C_STAGES; ++i) { | ||
| 142 | + AscendC::WaitFlag<AscendC::HardEvent::FIX_M>(l0CEventList_[i]); | ||
| 143 | + } | ||
| 144 | + } | ||
| 145 | + | ||
| 146 | + __aicore__ inline void operator()(const AscendC::GlobalTensor<ElementA> &gmA, const LayoutA &layoutA, | ||
| 147 | + const AscendC::GlobalTensor<ElementB> &gmB, const LayoutB &layoutB, | ||
| 148 | + const AscendC::GlobalTensor<ElementC> &gmC, const LayoutC &layoutC, | ||
| 149 | + const Catlass::GemmCoord &actualShape, bool needLoadL1) | ||
| 150 | + { | ||
| 151 | + const uint32_t mRound = RoundUp<L1AAlignHelper::M_ALIGNED>(actualShape.m()); | ||
| 152 | + const uint32_t nRound = RoundUp<L1BAlignHelper::N_ALIGNED>(actualShape.n()); | ||
| 153 | + auto layoutAInL1 = LayoutAInL1::template MakeLayout<ElementA>(L1_M, actualShape.k()); | ||
| 154 | + auto layoutBInL1 = LayoutBInL1::template MakeLayout<ElementB>(L1_K, L1_N); | ||
| 155 | + auto layoutInL0C = LayoutCInL0::MakeLayoutInL0C(Catlass::MakeCoord(mRound, nRound)); | ||
| 156 | + uint32_t kActual = Min(actualShape.k(), L1_K); | ||
| 157 | + | ||
| 158 | + const uint32_t kTileCount = CeilDiv<L1_K>(actualShape.k()); | ||
| 159 | + const uint32_t mPartLoop = CeilDiv<L1_M>(mRound); | ||
| 160 | + const uint32_t nPartLoop = CeilDiv<L1_N>(nRound); | ||
| 161 | + // headDim <= L1_K(实际场景恒成立)时整块 Q 一次进 L0A,可跨 page 复用; | ||
| 162 | + // 否则退回逐 page 重搬 A 的通用路径。 | ||
| 163 | + const bool qResident = (kTileCount == 1U) && (mPartLoop == 1U) && (CeilDiv<L1_K>(kActual) == 1U); | ||
| 164 | + | ||
| 165 | + if (needLoadL1) { | ||
| 166 | + auto layoutTileA = layoutA.GetTileLayout(Catlass::MakeCoord(actualShape.m(), actualShape.k())); | ||
| 167 | + AscendC::SetFlag<AscendC::HardEvent::MTE1_MTE2>(l1AEvent_); | ||
| 168 | + AscendC::WaitFlag<AscendC::HardEvent::MTE1_MTE2>(l1AEvent_); | ||
| 169 | + copyGmToL1A_(l1ATensor_, gmA, layoutAInL1, layoutTileA); | ||
| 170 | + AscendC::SetFlag<AscendC::HardEvent::MTE2_MTE1>(l1AEvent_); | ||
| 171 | + AscendC::WaitFlag<AscendC::HardEvent::MTE2_MTE1>(l1AEvent_); | ||
| 172 | + if (qResident) { | ||
| 173 | + // 本 M-tile 的 Q 一次搬进 L0A 并驻留:省掉每页 32KB 的 L1→L0A | ||
| 174 | + // 搬运与两次标量同步。先让 MTE1 等上一 M-tile 的 mmad 读完。 | ||
| 175 | + AscendC::SetFlag<AscendC::HardEvent::M_MTE1>(l0AEventList_[L0A_RESIDENT_ID]); | ||
| 176 | + AscendC::WaitFlag<AscendC::HardEvent::M_MTE1>(l0AEventList_[L0A_RESIDENT_ID]); | ||
| 177 | + LayoutAInL0 layoutAInL0 = LayoutAInL0::template MakeLayout<ElementA>(mRound, kActual); | ||
| 178 | + copyL1ToL0A_(l0ATensorList_[L0A_RESIDENT_ID], l1ATensor_, layoutAInL0, layoutAInL1); | ||
| 179 | + AscendC::SetFlag<AscendC::HardEvent::MTE1_M>(l0AEventList_[L0A_RESIDENT_ID]); | ||
| 180 | + AscendC::WaitFlag<AscendC::HardEvent::MTE1_M>(l0AEventList_[L0A_RESIDENT_ID]); | ||
| 181 | + } | ||
| 182 | + } | ||
| 183 | + | ||
| 184 | + AscendC::WaitFlag<AscendC::HardEvent::MTE1_MTE2>(l1BEventList_[l1ListId_]); | ||
| 185 | + auto layoutTileB = layoutB.GetTileLayout(Catlass::MakeCoord(kActual, actualShape.n())); | ||
| 186 | + copyGmToL1B_(l1BTensorList_[l1ListId_], gmB, layoutBInL1, layoutTileB); | ||
| 187 | + AscendC::SetFlag<AscendC::HardEvent::MTE2_MTE1>(l1BEventList_[l1ListId_]); | ||
| 188 | + | ||
| 189 | + // 等本槽位上一轮 FIXPIPE 完成后再写 L0C,从而与另一槽位的 FIXPIPE 重叠。 | ||
| 190 | + // unit flag 下该依赖由硬件维护,省掉这次标量等待。 | ||
| 191 | + if constexpr (!USE_UNIT_FLAG) { | ||
| 192 | + AscendC::WaitFlag<AscendC::HardEvent::FIX_M>(l0CEventList_[l0CListId_]); | ||
| 193 | + } | ||
| 194 | + | ||
| 195 | + for (uint32_t kLoopIdx = 0; kLoopIdx < kTileCount; ++kLoopIdx) { | ||
| 196 | + const uint32_t l1ListIdNext = (l1ListId_ + 1 < STAGES) ? (l1ListId_ + 1) : 0; | ||
| 197 | + uint32_t kActualNext = 0; | ||
| 198 | + if (kLoopIdx < kTileCount - 1) { | ||
| 199 | + const uint32_t kLoopIdxNext = kLoopIdx + 1; | ||
| 200 | + kActualNext = (kLoopIdxNext < kTileCount - 1) ? L1_K : (actualShape.k() - kLoopIdxNext * L1_K); | ||
| 201 | + Catlass::MatrixCoord gmTileBOffset{kLoopIdxNext * L1_K, 0}; | ||
| 202 | + auto gmTileB = gmB[layoutB.GetOffset(gmTileBOffset)]; | ||
| 203 | + AscendC::WaitFlag<AscendC::HardEvent::MTE1_MTE2>(l1BEventList_[l1ListIdNext]); | ||
| 204 | + layoutTileB = layoutB.GetTileLayout(Catlass::MakeCoord(kActualNext, actualShape.n())); | ||
| 205 | + copyGmToL1B_(l1BTensorList_[l1ListIdNext], gmTileB, layoutBInL1, layoutTileB); | ||
| 206 | + AscendC::SetFlag<AscendC::HardEvent::MTE2_MTE1>(l1BEventList_[l1ListIdNext]); | ||
| 207 | + } | ||
| 208 | + | ||
| 209 | + auto l1ATensor = l1ATensor_; | ||
| 210 | + auto l1BTensor = l1BTensorList_[l1ListId_]; | ||
| 211 | + const uint32_t kPartLoop = CeilDiv<L1_K>(kActual); | ||
| 212 | + | ||
| 213 | + for (uint32_t mPartIdx = 0; mPartIdx < mPartLoop; ++mPartIdx) { | ||
| 214 | + const uint32_t mPartActual = (mPartIdx < mPartLoop - 1) ? L1_M : (mRound - mPartIdx * L1_M); | ||
| 215 | + for (uint32_t kPartIdx = 0; kPartIdx < kPartLoop; ++kPartIdx) { | ||
| 216 | + const uint32_t kPartActual = (kPartIdx < kPartLoop - 1) ? L1_K : (kActual - kPartIdx * L1_K); | ||
| 217 | + auto l0ATile = l0ATensorList_[qResident ? L0A_RESIDENT_ID : L0A_FALLBACK_ID]; | ||
| 218 | + if (!qResident) { | ||
| 219 | + LayoutAInL0 layoutAInL0 = LayoutAInL0::template MakeLayout<ElementA>(mPartActual, kPartActual); | ||
| 220 | + Catlass::MatrixCoord l1AOffset{mPartIdx * L1_M, kPartIdx * L1_K + kLoopIdx * L1_K}; | ||
| 221 | + auto l1ATile = l1ATensor[layoutAInL1.GetOffset(l1AOffset)]; | ||
| 222 | + AscendC::WaitFlag<AscendC::HardEvent::M_MTE1>(l0AEventList_[L0A_FALLBACK_ID]); | ||
| 223 | + copyL1ToL0A_(l0ATile, l1ATile, layoutAInL0, layoutAInL1); | ||
| 224 | + } | ||
| 225 | + | ||
| 226 | + for (uint32_t nPartIdx = 0; nPartIdx < nPartLoop; ++nPartIdx) { | ||
| 227 | + const uint32_t nPartActual = (nPartIdx < nPartLoop - 1) ? L1_N : (nRound - nPartIdx * L1_N); | ||
| 228 | + auto l0BTile = l0BTensorList_[l0BListId_]; | ||
| 229 | + LayoutBInL0 layoutBInL0 = LayoutBInL0::template MakeLayout<ElementB>(kPartActual, nPartActual); | ||
| 230 | + Catlass::MatrixCoord l1BOffset{kPartIdx * L1_K, nPartIdx * L1_N}; | ||
| 231 | + auto l1BTile = l1BTensor[layoutBInL1.GetOffset(l1BOffset)]; | ||
| 232 | + AscendC::WaitFlag<AscendC::HardEvent::M_MTE1>(l0BEventList_[l0BListId_]); | ||
| 233 | + if ((kPartIdx == 0) && (nPartIdx == 0)) { | ||
| 234 | + AscendC::WaitFlag<AscendC::HardEvent::MTE2_MTE1>(l1BEventList_[l1ListId_]); | ||
| 235 | + } | ||
| 236 | + copyL1ToL0B_(l0BTile, l1BTile, layoutBInL0, layoutBInL1); | ||
| 237 | + if ((kPartIdx == kPartLoop - 1) && (nPartIdx == nPartLoop - 1)) { | ||
| 238 | + AscendC::SetFlag<AscendC::HardEvent::MTE1_MTE2>(l1BEventList_[l1ListId_]); | ||
| 239 | + } | ||
| 240 | + AscendC::SetFlag<AscendC::HardEvent::MTE1_M>(EVENT_ID0); | ||
| 241 | + Catlass::MatrixCoord l0COffset{mPartIdx * L1_M, nPartIdx * L1_N}; | ||
| 242 | + auto l0CTile = l0CTensorList_[l0CListId_][layoutInL0C.GetOffset(l0COffset)]; | ||
| 243 | + AscendC::WaitFlag<AscendC::HardEvent::MTE1_M>(EVENT_ID0); | ||
| 244 | + const bool initC = ((kLoopIdx == 0) && (kPartIdx == 0)); | ||
| 245 | + uint8_t unitFlag = 0b00; | ||
| 246 | + if constexpr (USE_UNIT_FLAG) { | ||
| 247 | + const bool last = (kLoopIdx == kTileCount - 1) && (mPartIdx == mPartLoop - 1) && | ||
| 248 | + (kPartIdx == kPartLoop - 1) && (nPartIdx == nPartLoop - 1); | ||
| 249 | + unitFlag = last ? 0b11 : 0b10; | ||
| 250 | + } | ||
| 251 | + tileMmad_(l0CTile, l0ATile, l0BTile, mPartActual, nPartActual, kPartActual, initC, unitFlag); | ||
| 252 | + AscendC::SetFlag<AscendC::HardEvent::M_MTE1>(l0BEventList_[l0BListId_]); | ||
| 253 | + l0BListId_ = (l0BListId_ + 1 < L0B_STAGES) ? (l0BListId_ + 1) : 0; | ||
| 254 | + } | ||
| 255 | + if (!qResident) { | ||
| 256 | + AscendC::SetFlag<AscendC::HardEvent::M_MTE1>(l0AEventList_[L0A_FALLBACK_ID]); | ||
| 257 | + } | ||
| 258 | + } | ||
| 259 | + } | ||
| 260 | + l1ListId_ = l1ListIdNext; | ||
| 261 | + kActual = kActualNext; | ||
| 262 | + } | ||
| 263 | + | ||
| 264 | + LayoutC layoutBlock = layoutC.GetTileLayout(actualShape.GetCoordMN()); | ||
| 265 | + if (storeToUb_) { | ||
| 266 | + StoreToUb(actualShape); | ||
| 267 | + } else if constexpr (USE_UNIT_FLAG) { | ||
| 268 | + copyL0CToGm_(gmC, l0CTensorList_[l0CListId_], layoutBlock, layoutInL0C, 0b11); | ||
| 269 | + } else { | ||
| 270 | + AscendC::SetFlag<AscendC::HardEvent::M_FIX>(l0CEventList_[l0CListId_]); | ||
| 271 | + AscendC::WaitFlag<AscendC::HardEvent::M_FIX>(l0CEventList_[l0CListId_]); | ||
| 272 | + copyL0CToGm_(gmC, l0CTensorList_[l0CListId_], layoutBlock, layoutInL0C); | ||
| 273 | + AscendC::SetFlag<AscendC::HardEvent::FIX_M>(l0CEventList_[l0CListId_]); | ||
| 274 | + } | ||
| 275 | + l0CListId_ = (l0CListId_ + 1 < L0C_STAGES) ? (l0CListId_ + 1) : 0; | ||
| 276 | + } | ||
| 277 | + | ||
| 278 | + /// L0C → 配对 AIV UB(dualDstCtl=1,按 M 对半)。ubC 已是 pingpong slot 起点。 | ||
| 279 | + __aicore__ inline void ComputeToUb(const AscendC::GlobalTensor<ElementA> &gmA, const LayoutA &layoutA, | ||
| 280 | + const AscendC::GlobalTensor<ElementB> &gmB, const LayoutB &layoutB, | ||
| 281 | + const AscendC::LocalTensor<float> &ubC, const Catlass::GemmCoord &actualShape, | ||
| 282 | + bool needLoadL1) | ||
| 283 | + { | ||
| 284 | + storeToUb_ = true; | ||
| 285 | + ubC_ = ubC; | ||
| 286 | + AscendC::GlobalTensor<ElementC> dummyGm; | ||
| 287 | + const LayoutC dummyLayout(actualShape.m(), actualShape.n(), actualShape.n()); | ||
| 288 | + operator()(gmA, layoutA, gmB, layoutB, dummyGm, dummyLayout, actualShape, needLoadL1); | ||
| 289 | + storeToUb_ = false; | ||
| 290 | + } | ||
| 291 | + | ||
| 292 | +private: | ||
| 293 | + static constexpr AscendC::FixpipeConfig MSA_CFG_ROW_MAJOR_UB = {AscendC::CO2Layout::ROW_MAJOR, true}; | ||
| 294 | + | ||
| 295 | + __aicore__ inline void StoreToUb(const Catlass::GemmCoord &actualShape) | ||
| 296 | + { | ||
| 297 | + const uint32_t mEven = (actualShape.m() + 1U) & ~1U; | ||
| 298 | + const uint32_t nAlign = (actualShape.n() + 7U) & ~7U; | ||
| 299 | + const uint32_t mRound = RoundUp<L1AAlignHelper::M_ALIGNED>(actualShape.m()); | ||
| 300 | + if constexpr (!USE_UNIT_FLAG) { | ||
| 301 | + AscendC::SetFlag<AscendC::HardEvent::M_FIX>(l0CEventList_[l0CListId_]); | ||
| 302 | + AscendC::WaitFlag<AscendC::HardEvent::M_FIX>(l0CEventList_[l0CListId_]); | ||
| 303 | + } | ||
| 304 | + if constexpr (sizeof(ElementC) == 2) { | ||
| 305 | + uint32_t m0 = 0; | ||
| 306 | + uint32_t m1 = 0; | ||
| 307 | + MsaC2UbHalfSplit(actualShape.m(), m0, m1); | ||
| 308 | + auto ubH = ubC_.template ReinterpretCast<half>(); | ||
| 309 | + AscendC::FixpipeParamsC310<AscendC::CO2Layout::ROW_MAJOR> fp; | ||
| 310 | + fp.srcStride = mRound; | ||
| 311 | + fp.dualDstCtl = 0; | ||
| 312 | + fp.quantPre = QuantMode_t::F322F16; | ||
| 313 | + fp.nSize = nAlign; | ||
| 314 | + fp.dstStride = nAlign; | ||
| 315 | + fp.params.ndNum = 1; | ||
| 316 | + fp.params.srcNdStride = 0; | ||
| 317 | + fp.params.dstNdStride = 0; | ||
| 318 | + if (m0 > 0U) { | ||
| 319 | + fp.mSize = m0; | ||
| 320 | + fp.subBlockId = false; | ||
| 321 | + AscendC::Fixpipe<half, float, MSA_CFG_ROW_MAJOR_UB>(ubH, l0CTensorList_[l0CListId_], fp); | ||
| 322 | + } | ||
| 323 | + if (m1 > 0U) { | ||
| 324 | + fp.mSize = m1; | ||
| 325 | + fp.subBlockId = true; | ||
| 326 | + AscendC::Fixpipe<half, float, MSA_CFG_ROW_MAJOR_UB>(ubH, l0CTensorList_[l0CListId_][m0 * 16U], fp); | ||
| 327 | + } | ||
| 328 | + } else { | ||
| 329 | + // 950 L0C→UB:nSize 上限 64。ndNum=2 用 panel 布局(QLI):每 AIV 先 [M/2,64] | ||
| 330 | + // 再紧接第二块 [M/2,64],总 32KB ping。 | ||
| 331 | + constexpr uint32_t N_PER_ND = 64; | ||
| 332 | + const uint32_t rowsPerAiv = mEven / MSA_AIV_PER_AIC; | ||
| 333 | + AscendC::FixpipeParamsC310<AscendC::CO2Layout::ROW_MAJOR> fp; | ||
| 334 | + fp.mSize = mEven; | ||
| 335 | + fp.srcStride = mRound; | ||
| 336 | + fp.dstStride = N_PER_ND; | ||
| 337 | + fp.dualDstCtl = 1; | ||
| 338 | + fp.quantPre = QuantMode_t::NoQuant; | ||
| 339 | + fp.nSize = N_PER_ND; | ||
| 340 | + fp.params.ndNum = (nAlign > N_PER_ND) ? (nAlign / N_PER_ND) : 1; | ||
| 341 | + if (fp.params.ndNum > 1) { | ||
| 342 | + fp.params.srcNdStride = ((mEven + 15U) / 16U) * N_PER_ND; | ||
| 343 | + fp.params.dstNdStride = rowsPerAiv * N_PER_ND; | ||
| 344 | + } else { | ||
| 345 | + fp.nSize = nAlign; | ||
| 346 | + fp.params.srcNdStride = 0; | ||
| 347 | + fp.params.dstNdStride = 0; | ||
| 348 | + } | ||
| 349 | + AscendC::Fixpipe<float, float, MSA_CFG_ROW_MAJOR_UB>(ubC_, l0CTensorList_[l0CListId_], fp); | ||
| 350 | + } | ||
| 351 | + if constexpr (!USE_UNIT_FLAG) { | ||
| 352 | + AscendC::SetFlag<AscendC::HardEvent::FIX_M>(l0CEventList_[l0CListId_]); | ||
| 353 | + } | ||
| 354 | + } | ||
| 355 | + AscendC::LocalTensor<ElementA> l1ATensor_; | ||
| 356 | + AscendC::LocalTensor<ElementB> l1BTensorList_[STAGES]; | ||
| 357 | + AscendC::LocalTensor<ElementA> l0ATensorList_[STAGES]; | ||
| 358 | + AscendC::LocalTensor<ElementB> l0BTensorList_[STAGES]; | ||
| 359 | + AscendC::LocalTensor<ElementAccumulator> l0CTensorList_[L0C_STAGES]; | ||
| 360 | + int32_t l1AEvent_{0}; | ||
| 361 | + int32_t l1BEventList_[STAGES]{}; | ||
| 362 | + int32_t l0AEventList_[STAGES]{}; | ||
| 363 | + int32_t l0BEventList_[STAGES]{}; | ||
| 364 | + int32_t l0CEventList_[L0C_STAGES]{}; | ||
| 365 | + uint32_t l1ListId_{0}; | ||
| 366 | + uint32_t l0BListId_{0}; | ||
| 367 | + uint32_t l0CListId_{0}; | ||
| 368 | + TileMmad tileMmad_; | ||
| 369 | + CopyGmToL1A copyGmToL1A_; | ||
| 370 | + CopyGmToL1B copyGmToL1B_; | ||
| 371 | + CopyL1ToL0A copyL1ToL0A_; | ||
| 372 | + CopyL1ToL0B copyL1ToL0B_; | ||
| 373 | + CopyL0CToGm copyL0CToGm_; | ||
| 374 | + bool storeToUb_{false}; | ||
| 375 | + AscendC::LocalTensor<float> ubC_; | ||
| 376 | +}; | ||
| 377 | + | ||
| 378 | +} // namespace MsaIndexScoreNs | ||
| 379 | + | ||
| 380 | +#endif // MSA_BLOCK_MMAD_ARCH35_H | ||
| @@ -0,0 +1,530 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | +/*! | ||
| 12 | + * \file msa_index_score_kernel.h | ||
| 13 | + * \brief MsaIndexScore Ascend 950:计算路径照搬 A2 核函数:AIC 做 paged QKᵀ,AIV 做(可选反量化)+ mask + 分段 RowMax。 | ||
| 14 | + * 第一阶段:计算/握手照搬 A2,ArchTag=AtlasA5。 | ||
| 15 | + * | ||
| 16 | + * 非量化:query/key 同为 bf16|fp16|fp8,score 侧不乘 scale。 | ||
| 17 | + * int8 量化:key 为 int8;AIV 将 page DataCopy+Cast 到 fp 暂存后通知 AIC 做 Mmad; | ||
| 18 | + * AIV 在 mask 前按 scale[NP,N_kv,P] 对 S 做列乘完成 per-token 反量化。 | ||
| 19 | + * | ||
| 20 | + * MIX 编译:`if ASCEND_IS_AIC` 拦不住模板实例化。AIV 再实例化 FP8 Cube | ||
| 21 | + *(Mmad / LoadData2DParamsV2)会让 bisheng 栈砸(exit 134)。Cube 路径仅在 | ||
| 22 | + * `__DAV_C310_CUBE__` / `__DAV_310R6_CUBE__` 下编译。 | ||
| 23 | + */ | ||
| 24 | + | ||
| 25 | +#ifndef MSA_INDEX_SCORE_KERNEL_ARCH35_H | ||
| 26 | +#define MSA_INDEX_SCORE_KERNEL_H | ||
| 27 | + | ||
| 28 | +#include "kernel_operator.h" | ||
| 29 | + | ||
| 30 | +#include "catlass/msa_catlass.hpp" | ||
| 31 | +#include "catlass/arch/msa_arch.hpp" | ||
| 32 | +#include "catlass/arch/msa_resource.hpp" | ||
| 33 | +#include "catlass/arch/msa_cross_core_sync.hpp" | ||
| 34 | +#include "catlass/msa_coord.hpp" | ||
| 35 | +#include "catlass/msa_gemm_coord.hpp" | ||
| 36 | +#include "catlass/msa_matrix_coord.hpp" | ||
| 37 | +#include "catlass/layout/msa_layout.hpp" | ||
| 38 | +#include "catlass/gemm/msa_gemm_type.hpp" | ||
| 39 | + | ||
| 40 | +#include "../msa_index_score_common.h" | ||
| 41 | +#if defined(__DAV_C310_CUBE__) || defined(__DAV_310R6_CUBE__) | ||
| 42 | +#include "msa_block_mmad.h" | ||
| 43 | +#endif | ||
| 44 | +#include "../arch22/msa_index_score_task.h" | ||
| 45 | +#include "msa_seg_row_max_epilogue.h" | ||
| 46 | + | ||
| 47 | +namespace MsaIndexScoreNs { | ||
| 48 | + | ||
| 49 | +template <class ElementQ_, bool IS_QUANT> | ||
| 50 | +class MsaIndexScoreKernel { | ||
| 51 | +public: | ||
| 52 | + using ElementQ = ElementQ_; | ||
| 53 | + using ArchTag = Catlass::Arch::AtlasA5; | ||
| 54 | + | ||
| 55 | + using LayoutQ = Catlass::layout::RowMajor; | ||
| 56 | + using LayoutK = Catlass::layout::ColumnMajor; | ||
| 57 | + using LayoutS = Catlass::layout::RowMajor; | ||
| 58 | + | ||
| 59 | + using QType = Catlass::Gemm::GemmType<ElementQ, LayoutQ>; | ||
| 60 | + using KType = Catlass::Gemm::GemmType<ElementQ, LayoutK>; | ||
| 61 | + // S workspace 元素:非量化 fp16(fixpipe F322F16 直接写,写读流量减半),int8 fp32 | ||
| 62 | + // (S 幅值大、1e-3 容差下 fp16 精度余量不足 15%,保持 fp32 反量化)。 | ||
| 63 | + using ElementS = std::conditional_t<IS_QUANT, float, half>; | ||
| 64 | + using SType = Catlass::Gemm::GemmType<ElementS, LayoutS>; | ||
| 65 | + | ||
| 66 | +#if defined(__DAV_C310_CUBE__) || defined(__DAV_310R6_CUBE__) | ||
| 67 | + // int8 的 K 源是每 stile 复用的 per-core scratch,保持 2 级 L1B 流水(3 级实测竞态崩溃); | ||
| 68 | + // 同样出于该 scratch 的时序保守性,unit flag 只在非量化路径开启。 | ||
| 69 | + using BlockMmad = MsaBlockMmad<QType, KType, SType, IS_QUANT ? MSA_L1B_STAGES_INT8 : MSA_L1B_STAGES_FP16, false>; | ||
| 70 | +#endif | ||
| 71 | + | ||
| 72 | + static constexpr uint32_t REVERSE_DEPTH = MSA_WORKSPACE_STAGES - 1; | ||
| 73 | + static constexpr uint32_t STILE_WIDTH = MSA_BLOCKS_PER_STILE * MSA_BLOCK_SIZE; | ||
| 74 | + static constexpr bool IS_QUANT_V = IS_QUANT; | ||
| 75 | + static constexpr bool USE_C2UB = (MSA_A5_USE_C2UB != 0); | ||
| 76 | + static constexpr uint32_t K_SCRATCH_ELEMS = USE_C2UB ? MSA_A5_K_SCRATCH_ELEM_NUM : MSA_K_SCRATCH_ELEM_NUM; | ||
| 77 | + // C_to_UB:cast 必须在 epilogue stage 之后。放在 ping 64KB 处会盖住 T64/deq/stage, | ||
| 78 | + // 下一页 gather 会把尚未 Flush 的 score 清掉(TND/int8 多 page:1e29→0、fill 变 0)。 | ||
| 79 | + static constexpr uint32_t UB_OFF_CAST_I8 = | ||
| 80 | + USE_C2UB ? (((MsaSegRowMaxEpilogue<IS_QUANT_V>::UB_TOTAL + MSA_UB_ALIGN_BYTES - 1U) / MSA_UB_ALIGN_BYTES) * | ||
| 81 | + MSA_UB_ALIGN_BYTES) : | ||
| 82 | + 0; | ||
| 83 | + static constexpr uint32_t UB_SIZE_CAST_I8 = MSA_BLOCK_SIZE * MSA_K_TILE * sizeof(int8_t); | ||
| 84 | + static constexpr uint32_t UB_OFF_CAST_FP = UB_OFF_CAST_I8 + UB_SIZE_CAST_I8; | ||
| 85 | + static constexpr uint32_t UB_SIZE_CAST_FP = MSA_BLOCK_SIZE * MSA_K_TILE * sizeof(half); | ||
| 86 | + static_assert(UB_OFF_CAST_FP + UB_SIZE_CAST_FP <= ArchTag::UB_SIZE, "int8 cast UB out of bounds"); | ||
| 87 | + | ||
| 88 | + __aicore__ inline MsaIndexScoreKernel() {} | ||
| 89 | + | ||
| 90 | + __aicore__ inline void Init(GM_ADDR query, GM_ADDR key, GM_ADDR blockTable, GM_ADDR scale, GM_ADDR actualSeqQlen, | ||
| 91 | + GM_ADDR actualSeqKlen, GM_ADDR startLoc, GM_ADDR score, GM_ADDR workspace, | ||
| 92 | + const MsaIndexScoreTilingData *__restrict tiling) | ||
| 93 | + { | ||
| 94 | + tiling_ = tiling; | ||
| 95 | + gQuery_.SetGlobalBuffer(reinterpret_cast<__gm__ ElementQ *>(query)); | ||
| 96 | + if constexpr (IS_QUANT_V) { | ||
| 97 | + gKeyInt8_.SetGlobalBuffer(reinterpret_cast<__gm__ int8_t *>(key)); | ||
| 98 | + } else { | ||
| 99 | + gKeyFp_.SetGlobalBuffer(reinterpret_cast<__gm__ ElementQ *>(key)); | ||
| 100 | + } | ||
| @@ -25,6 +25,17 @@ struct AtlasA2 { | |||
| 25 | static constexpr uint32_t L0C_SIZE = 128 * 1024; | 25 | static constexpr uint32_t L0C_SIZE = 128 * 1024; |
| 26 | }; | 26 | }; |
| 27 | 27 | ||
| 28 | +/// Ascend 950PR/DT(dav-3510)。容量对齐 950 仓 AtlasA5 / BSA arch。 | ||
| 29 | +struct AtlasA5 { | ||
| 30 | + static constexpr uint32_t BIAS_SIZE = 4 * 1024; | ||
| 31 | + static constexpr uint32_t FIXBUF_SIZE = 16 * 1024; | ||
| 32 | + static constexpr uint32_t UB_SIZE = 256 * 1024; | ||
| 33 | + static constexpr uint32_t L1_SIZE = 512 * 1024; | ||
| 34 | + static constexpr uint32_t L0A_SIZE = 64 * 1024; | ||
| 35 | + static constexpr uint32_t L0B_SIZE = 64 * 1024; | ||
| 36 | + static constexpr uint32_t L0C_SIZE = 256 * 1024; | ||
| 37 | +}; | ||
| 38 | + | ||
| 28 | template <AscendC::TPosition POS> | 39 | template <AscendC::TPosition POS> |
| 29 | using PositionType = std::integral_constant<AscendC::TPosition, POS>; | 40 | using PositionType = std::integral_constant<AscendC::TPosition, POS>; |
| 30 | 41 | ||
| @@ -101,6 +101,12 @@ void CrossCoreWaitFlag(CrossCoreFlag &flag) | |||
| 101 | AscendC::CrossCoreWaitFlag(flag.id); | 101 | AscendC::CrossCoreWaitFlag(flag.id); |
| 102 | } | 102 | } |
| 103 | 103 | ||
| 104 | +template <uint8_t MODE, pipe_t PIPE> | ||
| 105 | +CATLASS_DEVICE void CrossCoreWaitFlag(CrossCoreFlag &flag) | ||
| 106 | +{ | ||
| 107 | + AscendC::CrossCoreWaitFlag<MODE, PIPE>(flag.id); | ||
| 108 | +} | ||
| 109 | + | ||
| 104 | template <uint8_t MODE, pipe_t PIPE, uint32_t REVERSE_DEPTH> | 110 | template <uint8_t MODE, pipe_t PIPE, uint32_t REVERSE_DEPTH> |
| 105 | CATLASS_DEVICE void CrossCoreSetFlagWithReverse(CrossCoreFlagWithReverse<REVERSE_DEPTH> &flag) | 111 | CATLASS_DEVICE void CrossCoreSetFlagWithReverse(CrossCoreFlagWithReverse<REVERSE_DEPTH> &flag) |
| 106 | { | 112 | { |
| @@ -45,6 +45,9 @@ public: | |||
| 45 | CATLASS_DEVICE | 45 | CATLASS_DEVICE |
| 46 | LocalTensorBuffer() | 46 | LocalTensorBuffer() |
| 47 | { | 47 | { |
| 48 | + if ASCEND_IS_AIV { | ||
| 49 | + return; | ||
| 50 | + } | ||
| 48 | AscendC::TBuf<AscendC::TPosition::A1> tbufA1; | 51 | AscendC::TBuf<AscendC::TPosition::A1> tbufA1; |
| 49 | GetTPipePtr()->InitBuffer(tbufA1, ArchTag::L1_SIZE); | 52 | GetTPipePtr()->InitBuffer(tbufA1, ArchTag::L1_SIZE); |
| 50 | tensor = tbufA1.Get<uint8_t>(); | 53 | tensor = tbufA1.Get<uint8_t>(); |
| @@ -62,6 +65,9 @@ public: | |||
| 62 | CATLASS_DEVICE | 65 | CATLASS_DEVICE |
| 63 | LocalTensorBuffer() | 66 | LocalTensorBuffer() |
| 64 | { | 67 | { |
| 68 | + if ASCEND_IS_AIV { | ||
| 69 | + return; | ||
| 70 | + } | ||
| 65 | AscendC::TBuf<AscendC::TPosition::A2> tbufA2; | 71 | AscendC::TBuf<AscendC::TPosition::A2> tbufA2; |
| 66 | GetTPipePtr()->InitBuffer(tbufA2, ArchTag::L0A_SIZE); | 72 | GetTPipePtr()->InitBuffer(tbufA2, ArchTag::L0A_SIZE); |
| 67 | tensor = tbufA2.Get<uint8_t>(); | 73 | tensor = tbufA2.Get<uint8_t>(); |
| @@ -79,6 +85,9 @@ public: | |||
| 79 | CATLASS_DEVICE | 85 | CATLASS_DEVICE |
| 80 | LocalTensorBuffer() | 86 | LocalTensorBuffer() |
| 81 | { | 87 | { |
| 88 | + if ASCEND_IS_AIV { | ||
| 89 | + return; | ||
| 90 | + } | ||
| 82 | AscendC::TBuf<AscendC::TPosition::B1> tbufB1; | 91 | AscendC::TBuf<AscendC::TPosition::B1> tbufB1; |
| 83 | GetTPipePtr()->InitBuffer(tbufB1, ArchTag::L1_SIZE); | 92 | GetTPipePtr()->InitBuffer(tbufB1, ArchTag::L1_SIZE); |
| 84 | tensor = tbufB1.Get<uint8_t>(); | 93 | tensor = tbufB1.Get<uint8_t>(); |
| @@ -96,6 +105,9 @@ public: | |||
| 96 | CATLASS_DEVICE | 105 | CATLASS_DEVICE |
| 97 | LocalTensorBuffer() | 106 | LocalTensorBuffer() |
| 98 | { | 107 | { |
| 108 | + if ASCEND_IS_AIV { | ||
| 109 | + return; | ||
| 110 | + } | ||
| 99 | AscendC::TBuf<AscendC::TPosition::B2> tbufB2; | 111 | AscendC::TBuf<AscendC::TPosition::B2> tbufB2; |
| 100 | GetTPipePtr()->InitBuffer(tbufB2, ArchTag::L0B_SIZE); | 112 | GetTPipePtr()->InitBuffer(tbufB2, ArchTag::L0B_SIZE); |
| 101 | tensor = tbufB2.Get<uint8_t>(); | 113 | tensor = tbufB2.Get<uint8_t>(); |
| @@ -114,6 +126,9 @@ public: | |||
| 114 | CATLASS_DEVICE | 126 | CATLASS_DEVICE |
| 115 | LocalTensorBuffer() | 127 | LocalTensorBuffer() |
| 116 | { | 128 | { |
| 129 | + if ASCEND_IS_AIV { | ||
| 130 | + return; | ||
| 131 | + } | ||
| 117 | AscendC::TBuf<AscendC::TPosition::C1> tbufC1; | 132 | AscendC::TBuf<AscendC::TPosition::C1> tbufC1; |
| 118 | GetTPipePtr()->InitBuffer(tbufC1, ArchTag::L1_SIZE); | 133 | GetTPipePtr()->InitBuffer(tbufC1, ArchTag::L1_SIZE); |
| 119 | tensor = tbufC1.Get<uint8_t>(); | 134 | tensor = tbufC1.Get<uint8_t>(); |
| @@ -132,6 +147,9 @@ public: | |||
| 132 | CATLASS_DEVICE | 147 | CATLASS_DEVICE |
| 133 | LocalTensorBuffer() | 148 | LocalTensorBuffer() |
| 134 | { | 149 | { |
| 150 | + if ASCEND_IS_AIV { | ||
| 151 | + return; | ||
| 152 | + } | ||
| 135 | AscendC::TBuf<AscendC::TPosition::C2> tbufC2; | 153 | AscendC::TBuf<AscendC::TPosition::C2> tbufC2; |
| 136 | GetTPipePtr()->InitBuffer(tbufC2, ArchTag::BIAS_SIZE); | 154 | GetTPipePtr()->InitBuffer(tbufC2, ArchTag::BIAS_SIZE); |
| 137 | tensor = tbufC2.Get<uint8_t>(); | 155 | tensor = tbufC2.Get<uint8_t>(); |
| @@ -149,6 +167,9 @@ public: | |||
| 149 | CATLASS_DEVICE | 167 | CATLASS_DEVICE |
| 150 | LocalTensorBuffer() | 168 | LocalTensorBuffer() |
| 151 | { | 169 | { |
| 170 | + if ASCEND_IS_AIV { | ||
| 171 | + return; | ||
| 172 | + } | ||
| 152 | AscendC::TBuf<AscendC::TPosition::CO1> tbufCO1; | 173 | AscendC::TBuf<AscendC::TPosition::CO1> tbufCO1; |
| 153 | GetTPipePtr()->InitBuffer(tbufCO1, ArchTag::L0C_SIZE); | 174 | GetTPipePtr()->InitBuffer(tbufCO1, ArchTag::L0C_SIZE); |
| 154 | tensor = tbufCO1.Get<uint8_t>(); | 175 | tensor = tbufCO1.Get<uint8_t>(); |
| @@ -167,6 +188,63 @@ public: | |||
| 167 | CATLASS_DEVICE | 188 | CATLASS_DEVICE |
| 168 | LocalTensorBuffer() | 189 | LocalTensorBuffer() |
| 169 | { | 190 | { |
| 191 | + if ASCEND_IS_AIV { | ||
| 192 | + return; | ||
| 193 | + } | ||
| 194 | + AscendC::TBuf<AscendC::TPosition::C2PIPE2GM> tbufC2PIPE2GM; | ||
| 195 | + GetTPipePtr()->InitBuffer(tbufC2PIPE2GM, ArchTag::FIXBUF_SIZE); | ||
| 196 | + tensor = tbufC2PIPE2GM.Get<uint8_t>(); | ||
| 197 | + } | ||
| 198 | +}; | ||
| 199 | + | ||
| 200 | +template <> | ||
| 201 | +struct LocalTensorBuffer<Arch::AtlasA5, AscendC::TPosition::C1> : LocalTensorBufferBase { | ||
| 202 | +public: | ||
| 203 | + using ArchTag = Arch::AtlasA5; | ||
| 204 | + static constexpr AscendC::TPosition Position = AscendC::TPosition::C1; | ||
| 205 | + | ||
| 206 | + CATLASS_DEVICE | ||
| 207 | + LocalTensorBuffer() | ||
| 208 | + { | ||
| 209 | + if ASCEND_IS_AIV { | ||
| 210 | + return; | ||
| 211 | + } | ||
| 212 | + AscendC::TBuf<AscendC::TPosition::C1> tbufC1; | ||
| 213 | + GetTPipePtr()->InitBuffer(tbufC1, ArchTag::L1_SIZE); | ||
| 214 | + tensor = tbufC1.Get<uint8_t>(); | ||
| 215 | + } | ||
| 216 | +}; | ||
| 217 | + | ||
| 218 | +template <> | ||
| 219 | +struct LocalTensorBuffer<Arch::AtlasA5, AscendC::TPosition::C2> : LocalTensorBufferBase { | ||
| 220 | +public: | ||
| 221 | + using ArchTag = Arch::AtlasA5; | ||
| 222 | + static constexpr AscendC::TPosition Position = AscendC::TPosition::C2; | ||
| 223 | + | ||
| 224 | + CATLASS_DEVICE | ||
| 225 | + LocalTensorBuffer() | ||
| 226 | + { | ||
| 227 | + if ASCEND_IS_AIV { | ||
| 228 | + return; | ||
| 229 | + } | ||
| 230 | + AscendC::TBuf<AscendC::TPosition::C2> tbufC2; | ||
| 231 | + GetTPipePtr()->InitBuffer(tbufC2, ArchTag::BIAS_SIZE); | ||
| 232 | + tensor = tbufC2.Get<uint8_t>(); | ||
| 233 | + } | ||
| 234 | +}; | ||
| 235 | + | ||
| 236 | +template <> | ||
| 237 | +struct LocalTensorBuffer<Arch::AtlasA5, AscendC::TPosition::C2PIPE2GM> : LocalTensorBufferBase { | ||
| 238 | +public: | ||
| 239 | + using ArchTag = Arch::AtlasA5; | ||
| 240 | + static constexpr AscendC::TPosition Position = AscendC::TPosition::C2PIPE2GM; | ||
| 241 | + | ||
| 242 | + CATLASS_DEVICE | ||
| 243 | + LocalTensorBuffer() | ||
| 244 | + { | ||
| 245 | + if ASCEND_IS_AIV { | ||
| 246 | + return; | ||
| 247 | + } | ||
| 170 | AscendC::TBuf<AscendC::TPosition::C2PIPE2GM> tbufC2PIPE2GM; | 248 | AscendC::TBuf<AscendC::TPosition::C2PIPE2GM> tbufC2PIPE2GM; |
| 171 | GetTPipePtr()->InitBuffer(tbufC2PIPE2GM, ArchTag::FIXBUF_SIZE); | 249 | GetTPipePtr()->InitBuffer(tbufC2PIPE2GM, ArchTag::FIXBUF_SIZE); |
| 172 | tensor = tbufC2PIPE2GM.Get<uint8_t>(); | 250 | tensor = tbufC2PIPE2GM.Get<uint8_t>(); |
| @@ -219,7 +297,12 @@ public: | |||
| 219 | LocalTensorBuffer() | 297 | LocalTensorBuffer() |
| 220 | { | 298 | { |
| 221 | AscendC::TBuf<AscendC::TPosition::VECCALC> tbufVECCALC; | 299 | AscendC::TBuf<AscendC::TPosition::VECCALC> tbufVECCALC; |
| 222 | - GetTPipePtr()->InitBuffer(tbufVECCALC, ArchTag::UB_SIZE); | 300 | + uint32_t ubBytes = ArchTag::UB_SIZE; |
| 301 | + if ASCEND_IS_AIC { | ||
| 302 | + // Cube 只需 dual-dst pingpong(2*64*128*4=64KB);AIC 上 InitBuffer 256KB VECCALC 可能卡住 Fixpipe。 | ||
| 303 | + ubBytes = 64 * 1024; | ||
| 304 | + } | ||
| 305 | + GetTPipePtr()->InitBuffer(tbufVECCALC, ubBytes); | ||
| 223 | tensor = tbufVECCALC.Get<uint8_t>(); | 306 | tensor = tbufVECCALC.Get<uint8_t>(); |
| 224 | } | 307 | } |
| 225 | }; | 308 | }; |
| @@ -109,6 +109,24 @@ struct ElementAccumulatorSelector<bfloat16_t, bfloat16_t> { | |||
| 109 | using ElementAccumulator = float; | 109 | using ElementAccumulator = float; |
| 110 | }; | 110 | }; |
| 111 | 111 | ||
| 112 | +// FP8 / hifloat8 仅 AIC 310(Ascend 950)。 | ||
| 113 | +#if defined(__CCE_AICORE__) && (__CCE_AICORE__ == 310) | ||
| 114 | +template <> | ||
| 115 | +struct ElementAccumulatorSelector<fp8_e4m3fn_t, fp8_e4m3fn_t> { | ||
| 116 | + using ElementAccumulator = float; | ||
| 117 | +}; | ||
| 118 | + | ||
| 119 | +template <> | ||
| 120 | +struct ElementAccumulatorSelector<fp8_e5m2_t, fp8_e5m2_t> { | ||
| 121 | + using ElementAccumulator = float; | ||
| 122 | +}; | ||
| 123 | + | ||
| 124 | +template <> | ||
| 125 | +struct ElementAccumulatorSelector<hifloat8, hifloat8> { | ||
| 126 | + using ElementAccumulator = float; | ||
| 127 | +}; | ||
| 128 | +#endif | ||
| 129 | + | ||
| 112 | template <> | 130 | template <> |
| 113 | struct ElementAccumulatorSelector<AscendC::int4b_t, AscendC::int4b_t> { | 131 | struct ElementAccumulatorSelector<AscendC::int4b_t, AscendC::int4b_t> { |
| 114 | using ElementAccumulator = int32_t; | 132 | using ElementAccumulator = int32_t; |
| @@ -1141,6 +1141,10 @@ struct CopyGmToL1<Arch::AtlasA2, Gemm::GemmType<Element, layout::RowMajor>> { | |||
| 1141 | }; | 1141 | }; |
| 1142 | ///////////////////////////////// | 1142 | ///////////////////////////////// |
| 1143 | 1143 | ||
| 1144 | +template <class Element> | ||
| 1145 | +struct CopyGmToL1<Arch::AtlasA5, Gemm::GemmType<Element, layout::RowMajor>> | ||
| 1146 | + : CopyGmToL1<Arch::AtlasA2, Gemm::GemmType<Element, layout::RowMajor>> {}; | ||
| 1147 | + | ||
| 1144 | /// Partial specialization for AtlasA2, ColumnMajor in and nZ out. | 1148 | /// Partial specialization for AtlasA2, ColumnMajor in and nZ out. |
| 1145 | template <class Element> | 1149 | template <class Element> |
| 1146 | struct CopyGmToL1<Arch::AtlasA2, Gemm::GemmType<Element, layout::ColumnMajor>> { | 1150 | struct CopyGmToL1<Arch::AtlasA2, Gemm::GemmType<Element, layout::ColumnMajor>> { |
| @@ -1190,6 +1194,10 @@ struct CopyGmToL1<Arch::AtlasA2, Gemm::GemmType<Element, layout::ColumnMajor>> { | |||
| 1190 | } | 1194 | } |
| 1191 | }; | 1195 | }; |
| 1192 | 1196 | ||
| 1197 | +template <class Element> | ||
| 1198 | +struct CopyGmToL1<Arch::AtlasA5, Gemm::GemmType<Element, layout::ColumnMajor>> | ||
| 1199 | + : CopyGmToL1<Arch::AtlasA2, Gemm::GemmType<Element, layout::ColumnMajor>> {}; | ||
| 1200 | + | ||
| 1193 | /// Partial specialization for zN in and zN out. | 1201 | /// Partial specialization for zN in and zN out. |
| 1194 | template <class ArchTag, class Element> | 1202 | template <class ArchTag, class Element> |
| 1195 | struct CopyGmToL1<ArchTag, Gemm::GemmType<Element, layout::zN>> { | 1203 | struct CopyGmToL1<ArchTag, Gemm::GemmType<Element, layout::zN>> { |
| @@ -38,6 +38,16 @@ struct CopyL0CToGmQuantMode<Catlass::Arch::AtlasA2, float, float, ScaleGranulari | |||
| 38 | static constexpr auto VALUE = QuantMode_t::NoQuant; | 38 | static constexpr auto VALUE = QuantMode_t::NoQuant; |
| 39 | }; | 39 | }; |
| 40 | 40 | ||
| 41 | +template <> | ||
| 42 | +struct CopyL0CToGmQuantMode<Catlass::Arch::AtlasA5, float, float, ScaleGranularity::NO_QUANT> { | ||
| 43 | + static constexpr auto VALUE = QuantMode_t::NoQuant; | ||
| 44 | +}; | ||
| 45 | + | ||
| 46 | +template <> | ||
| 47 | +struct CopyL0CToGmQuantMode<Catlass::Arch::AtlasA5, float, half, ScaleGranularity::NO_QUANT> { | ||
| 48 | + static constexpr auto VALUE = QuantMode_t::F322F16; | ||
| 49 | +}; | ||
| 50 | + | ||
| 41 | // CopyL0CToGm cast fp32 to fp16 | 51 | // CopyL0CToGm cast fp32 to fp16 |
| 42 | template <> | 52 | template <> |
| 43 | struct CopyL0CToGmQuantMode<Catlass::Arch::AtlasA2, float, half, ScaleGranularity::NO_QUANT> { | 53 | struct CopyL0CToGmQuantMode<Catlass::Arch::AtlasA2, float, half, ScaleGranularity::NO_QUANT> { |
| @@ -169,6 +179,50 @@ struct CopyL0CToGm<Catlass::Arch::AtlasA2, ElementAccumulator_, Gemm::GemmType<E | |||
| 169 | } | 179 | } |
| 170 | }; | 180 | }; |
| 171 | 181 | ||
| 182 | +/// AtlasA5 / C310:Fixpipe L0C→GM(950 主路径是 L0C→UB;本特化仅满足 TileCopy 实例化 / 编译期回退)。 | ||
| 183 | +template <class ElementAccumulator_, class ElementDst_, bool ReluEnable_> | ||
| 184 | +struct CopyL0CToGm<Catlass::Arch::AtlasA5, ElementAccumulator_, Gemm::GemmType<ElementDst_, layout::RowMajor>, | ||
| 185 | + ScaleGranularity::NO_QUANT, ReluEnable_> { | ||
| 186 | + using ArchTag = Catlass::Arch::AtlasA5; | ||
| 187 | + using ElementDst = ElementDst_; | ||
| 188 | + using ElementSrc = ElementAccumulator_; | ||
| 189 | + using LayoutSrc = Catlass::layout::zN; | ||
| 190 | + using LayoutDst = Catlass::layout::RowMajor; | ||
| 191 | + static constexpr auto quantPre = | ||
| 192 | + CopyL0CToGmQuantMode<ArchTag, ElementSrc, ElementDst, ScaleGranularity::NO_QUANT>::VALUE; | ||
| 193 | + static constexpr auto reluEn = ReluEnable_; | ||
| 194 | + | ||
| 195 | + struct Params {}; | ||
| 196 | + Params params; | ||
| 197 | + | ||
| 198 | + CATLASS_DEVICE | ||
| 199 | + CopyL0CToGm() = default; | ||
| 200 | + | ||
| 201 | + CATLASS_DEVICE | ||
| 202 | + CopyL0CToGm(Params const ¶ms_) | ||
| 203 | + : params(params_) {}; | ||
| 204 | + | ||
| 205 | + CATLASS_DEVICE | ||
| 206 | + void operator()(AscendC::GlobalTensor<ElementDst> const &dst, AscendC::LocalTensor<ElementSrc> const &src, | ||
| 207 | + LayoutDst const &dstLayout, LayoutSrc const &srcLayout, uint8_t unitFlag = 0) | ||
| 208 | + { | ||
| 209 | + // 950 L0C→GM:与仓内 KDA 相同的 DataCopyCO12Dst + SetFixpipeNz2ndFlag。 | ||
| 210 | + // mSize 用实际 M(dstLayout),srcStride 用 L0C 16 对齐步长。 | ||
| 211 | + // 写 mRound 行会把 D=16(M=4)打成全 -inf。 | ||
| 212 | + AscendC::DataCopyCO12DstParams copyParams; | ||
| 213 | + copyParams.nSize = dstLayout.shape(1); | ||
| 214 | + copyParams.mSize = dstLayout.shape(0); | ||
| 215 | + copyParams.srcStride = srcLayout.stride(LayoutSrc::RANK - 1) / srcLayout.stride(0); | ||
| 216 | + copyParams.dstStride = dstLayout.stride(0); | ||
| 217 | + copyParams.quantPre = quantPre; | ||
| 218 | + copyParams.nz2ndEn = true; | ||
| 219 | + copyParams.reluPre = reluEn; | ||
| 220 | + copyParams.unitFlag = unitFlag; | ||
| 221 | + AscendC::SetFixpipeNz2ndFlag(1, 1, 1); | ||
| 222 | + AscendC::DataCopy(dst, src, copyParams); | ||
| 223 | + } | ||
| 224 | +}; | ||
| 225 | + | ||
| 172 | template <class ElementAccumulator_, class ElementDst_, bool ReluEnable_> | 226 | template <class ElementAccumulator_, class ElementDst_, bool ReluEnable_> |
| 173 | struct CopyL0CToGm<Catlass::Arch::AtlasA2, ElementAccumulator_, Gemm::GemmType<ElementDst_, layout::RowMajor>, | 227 | struct CopyL0CToGm<Catlass::Arch::AtlasA2, ElementAccumulator_, Gemm::GemmType<ElementDst_, layout::RowMajor>, |
| 174 | ScaleGranularity::PER_TENSOR, ReluEnable_> { | 228 | ScaleGranularity::PER_TENSOR, ReluEnable_> { |
| @@ -379,6 +379,37 @@ struct CopyL1ToL0A<ArchTag, Gemm::GemmType<float, layout::nZ, AscendC::TPosition | |||
| 379 | } | 379 | } |
| 380 | }; | 380 | }; |
| 381 | 381 | ||
| 382 | +#if defined(__CCE_AICORE__) && (__CCE_AICORE__ == 310) | ||
| 383 | +/// AtlasA5:L0A 硬件格式为 zN(A2 为 zZ)。K=16 时两者重合,故 D=16 能过、D=128 失败。 | ||
| 384 | +/// 参数对齐 chunk_kda_fwd 仓内已验证的 Ascend950 CopyL1ToL0A。 | ||
| 385 | +template <class Element> | ||
| 386 | +struct CopyL1ToL0A<Arch::AtlasA5, Gemm::GemmType<Element, layout::zN, AscendC::TPosition::A1>> { | ||
| 387 | + using LayoutDst = layout::zN; | ||
| 388 | + using LayoutSrc = layout::zN; | ||
| 389 | + | ||
| 390 | + static constexpr uint32_t ELE_NUM_PER_C0 = BytesToBits(BYTE_PER_C0) / SizeOfBits<Element>::value; | ||
| 391 | + static constexpr uint32_t ELE_NUM_PER_FRACTAL = BytesToBits(BYTE_PER_FRACTAL) / SizeOfBits<Element>::value; | ||
| 392 | + | ||
| 393 | + CATLASS_DEVICE | ||
| 394 | + CopyL1ToL0A() {}; | ||
| 395 | + | ||
| 396 | + CATLASS_DEVICE | ||
| 397 | + void operator()(AscendC::LocalTensor<Element> const &dstTensor, AscendC::LocalTensor<Element> const &srcTensor, | ||
| 398 | + LayoutDst const &layoutDst, LayoutSrc const &layoutSrc) | ||
| 399 | + { | ||
| 400 | + AscendC::LoadData2DParamsV2 loadDataParams; | ||
| 401 | + loadDataParams.mStartPosition = 0; | ||
| 402 | + loadDataParams.kStartPosition = 0; | ||
| 403 | + loadDataParams.mStep = layoutDst.shape(1); | ||
| 404 | + loadDataParams.kStep = layoutDst.shape(LayoutDst::RANK - 1); | ||
| 405 | + loadDataParams.srcStride = CeilDiv<ELE_NUM_PER_FRACTAL>(layoutSrc.stride(LayoutSrc::RANK - 1)); | ||
| 406 | + loadDataParams.dstStride = CeilDiv<ELE_NUM_PER_FRACTAL>(layoutDst.stride(LayoutDst::RANK - 1)); | ||
| 407 | + loadDataParams.ifTranspose = false; | ||
| 408 | + AscendC::LoadData(dstTensor, srcTensor, loadDataParams); | ||
| 409 | + } | ||
| 410 | +}; | ||
| 411 | +#endif | ||
| 412 | + | ||
| 382 | } // namespace Catlass::Gemm::Tile | 413 | } // namespace Catlass::Gemm::Tile |
| 383 | 414 | ||
| 384 | #endif // CATLASS_MSA_GEMM_TILE_COPY_L1_TO_L0A_HPP | 415 | #endif // CATLASS_MSA_GEMM_TILE_COPY_L1_TO_L0A_HPP |
| @@ -475,6 +475,37 @@ struct CopyL1ToL0B<ArchTag, Gemm::GemmType<Element, layout::nZ, AscendC::TPositi | |||
| 475 | } | 475 | } |
| 476 | }; | 476 | }; |
| 477 | 477 | ||
| 478 | +#if defined(__CCE_AICORE__) && (__CCE_AICORE__ == 310) | ||
| 479 | +/// AtlasA5:nZ→nZ 用 LoadData2DParamsV2 一次搬完(A2 LoadData2D 在 950 上只对单 C0 正确)。 | ||
| 480 | +/// 参数对齐 chunk_kda_fwd 仓内已验证的 Ascend950 CopyL1ToL0B;ifTranspose 必须为 false。 | ||
| 481 | +template <class Element> | ||
| 482 | +struct CopyL1ToL0B<Arch::AtlasA5, Gemm::GemmType<Element, layout::nZ, AscendC::TPosition::A1>> { | ||
| 483 | + using LayoutDst = layout::nZ; | ||
| 484 | + using LayoutSrc = layout::nZ; | ||
| 485 | + | ||
| 486 | + static constexpr uint32_t ELE_NUM_PER_C0 = BytesToBits(BYTE_PER_C0) / SizeOfBits<Element>::value; | ||
| 487 | + static constexpr uint32_t ELE_NUM_PER_FRACTAL = BytesToBits(BYTE_PER_FRACTAL) / SizeOfBits<Element>::value; | ||
| 488 | + | ||
| 489 | + CATLASS_DEVICE | ||
| 490 | + CopyL1ToL0B() {}; | ||
| 491 | + | ||
| 492 | + CATLASS_DEVICE | ||
| 493 | + void operator()(AscendC::LocalTensor<Element> const &dstTensor, AscendC::LocalTensor<Element> const &srcTensor, | ||
| 494 | + LayoutDst const &layoutDst, LayoutSrc const &layoutSrc) | ||
| 495 | + { | ||
| 496 | + AscendC::LoadData2DParamsV2 loadDataParams; | ||
| 497 | + loadDataParams.mStartPosition = 0; | ||
| 498 | + loadDataParams.kStartPosition = 0; | ||
| 499 | + loadDataParams.mStep = layoutDst.shape(LayoutDst::RANK - 1); | ||
| 500 | + loadDataParams.kStep = layoutDst.shape(1); | ||
| 501 | + loadDataParams.srcStride = CeilDiv<ELE_NUM_PER_FRACTAL>(layoutSrc.stride(1)); | ||
| 502 | + loadDataParams.dstStride = CeilDiv<ELE_NUM_PER_FRACTAL>(layoutDst.stride(1)); | ||
| 503 | + loadDataParams.ifTranspose = false; | ||
| 504 | + AscendC::LoadData(dstTensor, srcTensor, loadDataParams); | ||
| 505 | + } | ||
| 506 | +}; | ||
| 507 | +#endif | ||
| 508 | + | ||
| 478 | } // namespace Catlass::Gemm::Tile | 509 | } // namespace Catlass::Gemm::Tile |
| 479 | 510 | ||
| 480 | #endif // CATLASS_MSA_GEMM_TILE_COPY_L1_TO_L0B_HPP | 511 | #endif // CATLASS_MSA_GEMM_TILE_COPY_L1_TO_L0B_HPP |
| @@ -12,6 +12,7 @@ | |||
| 12 | #define CATLASS_MSA_GEMM_TILE_TILE_MMAD_HPP | 12 | #define CATLASS_MSA_GEMM_TILE_TILE_MMAD_HPP |
| 13 | 13 | ||
| 14 | #include "catlass/msa_catlass.hpp" | 14 | #include "catlass/msa_catlass.hpp" |
| 15 | +#include "catlass/arch/msa_arch.hpp" | ||
| 15 | #include "catlass/gemm/msa_gemm_helper.hpp" | 16 | #include "catlass/gemm/msa_gemm_helper.hpp" |
| 16 | namespace Catlass::Gemm::Tile { | 17 | namespace Catlass::Gemm::Tile { |
| 17 | 18 | ||
| @@ -48,6 +49,12 @@ struct TileMmad { | |||
| 48 | mmadParams.k = k; | 49 | mmadParams.k = k; |
| 49 | mmadParams.unitFlag = unitFlag; | 50 | mmadParams.unitFlag = unitFlag; |
| 50 | mmadParams.cmatrixInitVal = initC; | 51 | mmadParams.cmatrixInitVal = initC; |
| 52 | + // disableGemv 仅 950 MmadParams 有;if constexpr 挡不住非依赖成员检查。 | ||
| 53 | +#if defined(__CCE_AICORE__) && (__CCE_AICORE__ == 310) | ||
| 54 | + if constexpr (std::is_same_v<ArchTag_, Catlass::Arch::AtlasA5>) { | ||
| 55 | + mmadParams.disableGemv = true; | ||
| 56 | + } | ||
| 57 | +#endif | ||
| 51 | if constexpr (std::is_same_v<ElementA, float> && | 58 | if constexpr (std::is_same_v<ElementA, float> && |
| 52 | (std::is_same_v<typename AType_::Layout, layout::ColumnMajor> || | 59 | (std::is_same_v<typename AType_::Layout, layout::ColumnMajor> || |
| 53 | std::is_same_v<typename AType_::Layout, layout::nZ>)) { | 60 | std::is_same_v<typename AType_::Layout, layout::nZ>)) { |
| @@ -74,6 +81,11 @@ struct TileMmad { | |||
| 74 | mmadParams.k = k; | 81 | mmadParams.k = k; |
| 75 | mmadParams.unitFlag = unitFlag; | 82 | mmadParams.unitFlag = unitFlag; |
| 76 | mmadParams.cmatrixInitVal = false; | 83 | mmadParams.cmatrixInitVal = false; |
| 84 | +#if defined(__CCE_AICORE__) && (__CCE_AICORE__ == 310) | ||
| 85 | + if constexpr (std::is_same_v<ArchTag_, Catlass::Arch::AtlasA5>) { | ||
| 86 | + mmadParams.disableGemv = true; | ||
| 87 | + } | ||
| 88 | +#endif | ||
| 77 | if constexpr (std::is_same_v<ElementA, float> && | 89 | if constexpr (std::is_same_v<ElementA, float> && |
| 78 | (std::is_same_v<typename AType_::Layout, layout::ColumnMajor> || | 90 | (std::is_same_v<typename AType_::Layout, layout::ColumnMajor> || |
| 79 | std::is_same_v<typename AType_::Layout, layout::nZ>)) { | 91 | std::is_same_v<typename AType_::Layout, layout::nZ>)) { |
| @@ -17,10 +17,14 @@ | |||
| 17 | * atten_mask 仅做 host 校验;device 侧按 sparse_mode 解析因果,不消费该 GM。 | 17 | * atten_mask 仅做 host 校验;device 侧按 sparse_mode 解析因果,不消费该 GM。 |
| 18 | */ | 18 | */ |
| 19 | 19 | ||
| 20 | -#include "kernel_operator.h" | 20 | +#include "kernel_operator.h" // force-rebuild-arch22-v65-int8-4slot-dual-aiv |
| 21 | #include "lib/matmul_intf.h" | 21 | #include "lib/matmul_intf.h" |
| 22 | #include "msa_index_score_common.h" | 22 | #include "msa_index_score_common.h" |
| 23 | +#if (__CCE_AICORE__ == 310) | ||
| 24 | +#include "arch35/msa_index_score_kernel.h" | ||
| 25 | +#else | ||
| 23 | #include "arch22/msa_index_score_kernel.h" | 26 | #include "arch22/msa_index_score_kernel.h" |
| 27 | +#endif | ||
| 24 | 28 | ||
| 25 | using namespace MsaIndexScoreNs; | 29 | using namespace MsaIndexScoreNs; |
| 26 | 30 | ||
| @@ -38,9 +42,10 @@ extern "C" __global__ __aicore__ void msa_index_score(GM_ADDR query, GM_ADDR key | |||
| 38 | { | 42 | { |
| 39 | KERNEL_TASK_TYPE_DEFAULT(KERNEL_TYPE_MIX_AIC_1_2); | 43 | KERNEL_TASK_TYPE_DEFAULT(KERNEL_TYPE_MIX_AIC_1_2); |
| 40 | (void)attenMask; // host 校验;device 按 sparse_mode 解析 rightDownCausal | 44 | (void)attenMask; // host 校验;device 按 sparse_mode 解析 rightDownCausal |
| 45 | + // 950 非量化 PA:S 在 UB、无 K scratch,user workspace 可能为 0。不能因此跳过整个 kernel 流程。 | ||
| 41 | GM_ADDR userWs = AscendC::GetUserWorkspace(workspace); | 46 | GM_ADDR userWs = AscendC::GetUserWorkspace(workspace); |
| 42 | if (userWs == nullptr) { | 47 | if (userWs == nullptr) { |
| 43 | - return; | 48 | + userWs = workspace; |
| 44 | } | 49 | } |
| 45 | GET_TILING_DATA_WITH_STRUCT(MsaIndexScoreTilingData, tilingDataIn, tiling); | 50 | GET_TILING_DATA_WITH_STRUCT(MsaIndexScoreTilingData, tilingDataIn, tiling); |
| 46 | const MsaIndexScoreTilingData *__restrict tilingData = &tilingDataIn; | 51 | const MsaIndexScoreTilingData *__restrict tilingData = &tilingDataIn; |
| @@ -51,5 +56,13 @@ extern "C" __global__ __aicore__ void msa_index_score(GM_ADDR query, GM_ADDR key | |||
| 51 | MSA_INVOKE_KERNEL(half, false); | 56 | MSA_INVOKE_KERNEL(half, false); |
| 52 | } else if (TILING_KEY_IS(MSA_TILING_KEY_FP16_INT8)) { | 57 | } else if (TILING_KEY_IS(MSA_TILING_KEY_FP16_INT8)) { |
| 53 | MSA_INVOKE_KERNEL(half, true); | 58 | MSA_INVOKE_KERNEL(half, true); |
| 59 | +#if (__CCE_AICORE__ == 310) | ||
| 60 | + } else if (TILING_KEY_IS(MSA_TILING_KEY_FP8_E4M3FN)) { | ||
| 61 | + MSA_INVOKE_KERNEL(fp8_e4m3fn_t, false); | ||
| 62 | + } else if (TILING_KEY_IS(MSA_TILING_KEY_FP8_E5M2)) { | ||
| 63 | + MSA_INVOKE_KERNEL(fp8_e5m2_t, false); | ||
| 64 | + } else if (TILING_KEY_IS(MSA_TILING_KEY_HIFLOAT8)) { | ||
| 65 | + MSA_INVOKE_KERNEL(hifloat8_t, false); | ||
| 66 | +#endif | ||
| 54 | } | 67 | } |
| 55 | } | 68 | } |
| @@ -41,7 +41,7 @@ constexpr uint32_t MSA_K_TILE = 128; | |||
| 41 | constexpr uint32_t MSA_BLOCKS_PER_STILE = 8; | 41 | constexpr uint32_t MSA_BLOCKS_PER_STILE = 8; |
| 42 | 42 | ||
| 43 | // S workspace 轮转级数。AIC 领先 AIV 的未消费 tile 数 <= MSA_WORKSPACE_STAGES - 1。 | 43 | // S workspace 轮转级数。AIC 领先 AIV 的未消费 tile 数 <= MSA_WORKSPACE_STAGES - 1。 |
| 44 | -// 5 -> 8:AIV 乒乓化后服务速度提升,加深缓冲让 AIC 几乎不因 AIV 停顿(wait_id2→0)。 | 44 | +// 5 -> 8:AIV 乒乓化后速度提升,加深缓冲让 AIC 几乎不因 AIV 停顿(wait_id2→0)。 |
| 45 | // 非量化路径 S 为 fp16,8 级 × 256KB × 20 核 = 40MB,仍可整体驻留 L2。 | 45 | // 非量化路径 S 为 fp16,8 级 × 256KB × 20 核 = 40MB,仍可整体驻留 L2。 |
| 46 | constexpr uint32_t MSA_WORKSPACE_STAGES = 8; | 46 | constexpr uint32_t MSA_WORKSPACE_STAGES = 8; |
| 47 | 47 | ||
| @@ -57,6 +57,18 @@ constexpr uint32_t MSA_STILE_BYTES_FP32 = MSA_STILE_ELEM_NUM * sizeof(float); | |||
| 57 | // (AIV 先 cast,AIC 再 Mmad,避免逐 page 细粒度同步)。 | 57 | // (AIV 先 cast,AIC 再 Mmad,避免逐 page 细粒度同步)。 |
| 58 | // 单页布局 [blockSize, headDim] 行主序,按 j*P*D 排列。 | 58 | // 单页布局 [blockSize, headDim] 行主序,按 j*P*D 排列。 |
| 59 | constexpr uint32_t MSA_K_SCRATCH_ELEM_NUM = MSA_BLOCKS_PER_STILE * MSA_BLOCK_SIZE * MSA_K_TILE; | 59 | constexpr uint32_t MSA_K_SCRATCH_ELEM_NUM = MSA_BLOCKS_PER_STILE * MSA_BLOCK_SIZE * MSA_K_TILE; |
| 60 | +// A2/A3 int8:四槽 scratch。WaitS(st) 后 slot[st%4] 已空,gather(st+4) 叠在 | ||
| 61 | +// AIC 的 cube(st+1)+cube(st+2)+cube(st+3) 上;epilogue(st) 再叠在 cube(st+4) 上。950 不用。 | ||
| 62 | +constexpr uint32_t MSA_K_SCRATCH_STAGES_A2 = 4; | ||
| 63 | +// 950 一页握手:每 AIC 只需一页 K scratch。 | ||
| 64 | +constexpr uint32_t MSA_A5_K_SCRATCH_ELEM_NUM = MSA_BLOCK_SIZE * MSA_K_TILE; | ||
| 65 | +constexpr uint32_t MSA_A5_S_STAGES = 2; | ||
| 66 | +constexpr uint32_t MSA_A5_FP8_C0 = 32; | ||
| 67 | +constexpr uint32_t MSA_A5_S_PAGE_ELEM_NUM = MSA_ROW_TILE_M * MSA_BLOCK_SIZE; | ||
| 68 | + | ||
| 69 | +#ifndef MSA_A5_USE_C2UB | ||
| 70 | +#define MSA_A5_USE_C2UB 1 | ||
| 71 | +#endif | ||
| 60 | 72 | ||
| 61 | // AIV 单次归约处理的行数。受 level-0 向量 API repeatTimes(uint8_t) 上限约束: | 73 | // AIV 单次归约处理的行数。受 level-0 向量 API repeatTimes(uint8_t) 上限约束: |
| 62 | // MSA_ROWS_PER_PASS * MSA_BLOCKS_PER_STILE <= 255,且乘积需为 8 的倍数。 | 74 | // MSA_ROWS_PER_PASS * MSA_BLOCKS_PER_STILE <= 255,且乘积需为 8 的倍数。 |
| @@ -74,10 +86,15 @@ constexpr uint32_t MSA_DATABLOCK_BYTES = 32; | |||
| 74 | constexpr uint32_t MSA_VEC_MASK_WORDS = 2; | 86 | constexpr uint32_t MSA_VEC_MASK_WORDS = 2; |
| 75 | // MIX 核比:1 AIC : 2 AIV。 | 87 | // MIX 核比:1 AIC : 2 AIV。 |
| 76 | constexpr uint32_t MSA_AIV_PER_AIC = 2; | 88 | constexpr uint32_t MSA_AIV_PER_AIC = 2; |
| 89 | +// 950 C_to_UB:dual-dst 后每 AIV 半个 M-tile × N=128 fp32 pingpong。 | ||
| 90 | +constexpr uint32_t MSA_A5_S_PING_ROWS = MSA_ROW_TILE_M / MSA_AIV_PER_AIC; | ||
| 91 | +constexpr uint32_t MSA_A5_S_PING_ELEM_NUM = MSA_A5_S_PING_ROWS * MSA_BLOCK_SIZE; | ||
| 92 | +constexpr uint32_t MSA_A5_S_PING_BYTES = MSA_A5_S_PING_ELEM_NUM * static_cast<uint32_t>(sizeof(float)); | ||
| 77 | // UB 手工布局 512B 对齐。 | 93 | // UB 手工布局 512B 对齐。 |
| 78 | constexpr uint32_t MSA_UB_ALIGN_BYTES = 512; | 94 | constexpr uint32_t MSA_UB_ALIGN_BYTES = 512; |
| 79 | 95 | ||
| 80 | -// BlockMmad L1B pingpong 级数:int8 用 scratch 时序保守取 2;非量化取 3。 | 96 | +// BlockMmad L1B pingpong 级数:950 int8 仍为 2(单页 scratch 竞态);A2/A3 int8 在 |
| 97 | +// arch22 kernel 里改用 MSA_L1B_STAGES_FP16(双槽后安全)。 | ||
| 81 | constexpr uint32_t MSA_L1B_STAGES_INT8 = 2; | 98 | constexpr uint32_t MSA_L1B_STAGES_INT8 = 2; |
| 82 | constexpr uint32_t MSA_L1B_STAGES_FP16 = 3; | 99 | constexpr uint32_t MSA_L1B_STAGES_FP16 = 3; |
| 83 | 100 | ||
| @@ -110,9 +127,18 @@ constexpr uint32_t MSA_ATTEN_MASK_SIZE = 2048; | |||
| 110 | // FFTS cross-core flag id,合法范围 0~7(8/9/10 为保留 barrier)。 | 127 | // FFTS cross-core flag id,合法范围 0~7(8/9/10 为保留 barrier)。 |
| 111 | constexpr uint16_t MSA_FLAG_S_READY = 1; | 128 | constexpr uint16_t MSA_FLAG_S_READY = 1; |
| 112 | constexpr uint16_t MSA_FLAG_S_READY_REVERSE = 2; | 129 | constexpr uint16_t MSA_FLAG_S_READY_REVERSE = 2; |
| 113 | -// int8 路径:AIV cast K→scratch 完成后通知 AIC。 | 130 | +// int8 路径:AIV cast K→scratch 完成后通知 AIC。A2 四槽用 3/4/5/6。 |
| 114 | constexpr uint16_t MSA_FLAG_K_READY = 3; | 131 | constexpr uint16_t MSA_FLAG_K_READY = 3; |
| 115 | constexpr uint16_t MSA_FLAG_K_READY_REVERSE = 4; | 132 | constexpr uint16_t MSA_FLAG_K_READY_REVERSE = 4; |
| 133 | +constexpr uint16_t MSA_FLAG_K_READY_2 = 5; | ||
| 134 | +constexpr uint16_t MSA_FLAG_K_READY_3 = 6; | ||
| 135 | + | ||
| 136 | +// Ascend 950 MIX:MODE4 下 AIC 分别给两个 AIV 置位,flagId+16。 | ||
| 137 | +constexpr uint32_t MSA_A5_SYNC_MODE4 = 4; | ||
| 138 | +constexpr uint16_t MSA_A5_AIV_FLAG_OFFSET = 16; | ||
| 139 | +constexpr uint16_t MSA_A5_FLAG_CV = 0; // Cube→Vector:S 已在 GM/UB | ||
| 140 | +constexpr uint16_t MSA_A5_FLAG_VC = 2; // Vector→Cube:UB slot 可覆盖 | ||
| 141 | +constexpr uint16_t MSA_A5_FLAG_K = 4; // Vector→Cube:K scratch 就绪 | ||
| 116 | 142 | ||
| 117 | } // namespace MsaIndexScoreNs | 143 | } // namespace MsaIndexScoreNs |
| 118 | 144 | ||
| @@ -122,5 +148,8 @@ constexpr uint16_t MSA_FLAG_K_READY_REVERSE = 4; | |||
| 122 | #define MSA_TILING_KEY_FP16 1 | 148 | #define MSA_TILING_KEY_FP16 1 |
| 123 | #define MSA_TILING_KEY_BF16_INT8 2 | 149 | #define MSA_TILING_KEY_BF16_INT8 2 |
| 124 | #define MSA_TILING_KEY_FP16_INT8 3 | 150 | #define MSA_TILING_KEY_FP16_INT8 3 |
| 151 | +#define MSA_TILING_KEY_HIFLOAT8 4 | ||
| 152 | +#define MSA_TILING_KEY_FP8_E5M2 5 | ||
| 153 | +#define MSA_TILING_KEY_FP8_E4M3FN 6 | ||
| 125 | 154 | ||
| 126 | #endif // MSA_INDEX_SCORE_COMMON_H | 155 | #endif // MSA_INDEX_SCORE_COMMON_H |