优化 3.1 源码解析:展开各分支原理/公式/数据流,补充章节关系说明

This commit is contained in:
2026-08-27 14:17:36 +00:00
parent 2944595dd5
commit e99f0b96ae

View File

@@ -19,236 +19,373 @@
</blockquote> </blockquote>
<hr/> <hr/>
<h2>0. 源码结构总览</h2> <h2>0. 源码结构总览</h2>
<p>mat_mul_v3 采用 <strong>Host TilingCPU+ Device KernelNPU</strong> 两层结构,中间通过 <strong>tiling key位域编码</strong> 传递决策结果</p> <p>mat_mul_v3 采用 <strong>Host TilingCPU+ Device KernelNPU</strong> 两层结构,中间 <strong>tiling key位域编码+ tiling data参数结构体</strong> 传递决策:</p>
<pre><code>┌─────────────────────────── HostCPU 侧,算子启动前)───────────────────────────┐ <pre><code>┌─────────────────────────── HostCPU 侧,算子启动前执行一次)───────────────────────────┐
│ MatMulV3Tiling::DoTiling() │ │ MatMulV3Tiling::DoTiling() │
│ └─ MMTilingRegistry 按 priority 顺序尝试各 tiling 类(策略模式) │ └─ MMTilingRegistry::DoTilingImpl() 按 priority 依次尝试各 tiling 类(策略模式) │
│ ├─ MatMulV3KEqZeroTiling (priority 0) │ │ ├─ MatMulV3KEqZeroTiling (priority 0) │
│ ├─ MatMulV3ToMulTiling (priority 1) │ │ ├─ MatMulV3ToMulTiling (priority 1) │
│ ├─ MatMulV3ToVectorTiling (priority 2) │ │ ├─ MatMulV3ToVectorTiling (priority 2) │
│ ├─ MatMulV3BasicStreamKTiling (priority 3) │ │ ├─ MatMulV3BasicStreamKTiling (priority 3) │
│ ├─ MatMulV3BasicAswtTiling (priority 4) │ │ ├─ MatMulV3BasicAswtTiling (priority 4) │
│ └─ MatMulV3AswTiling (priority 999, 兜底) │ │ └─ MatMulV3AswTiling (priority 999, 兜底) │
│ 每个类IsCapable() 判定是否满足条件,满足则 DoOpTiling() 计算 tiling 参数, │ 每个类IsCapable() 判条件 → DoOpTiling() 算参数 → 编码 tiling key + tiling data
│ 编码成 tiling key + tiling data 下发给 device。 │ └─────────────────────────────────────────────────────────────────────────────────────────┘
└─────────────────────────────────────────────────────────────────────────────────┘ │ tiling key分支选择结果+ tiling data参数
│ tiling key + tiling data
┌─────────────────────────── DeviceNPU 侧,每执行)───────────────────────────┐ ┌─────────────────────────── DeviceNPU 侧,每个核独立执行)───────────────────────────
│ mat_mul_v3() 入口if constexpr 按 (ApiLevel, FullLoad, Model, L0C2Out) 分发 │ mat_mul_v3() 入口if constexpr 按 (ApiLevel, FullLoad, Model, L0C2Out) 四元组分发
│ ├─ MatMulInputKEqZeroClearOutput K==0 清零 │ │ ├─ MatMulInputKEqZeroClearOutput K==0 清零 │
│ ├─ MatMulToMulActKernel / MatMulToVectorActKernel 退化向量乘 │ │ ├─ MatMulToMulActKernel / MatMulToVectorActKernel 退化向量乘 │
│ ├─ MatMulStreamKKernel / MatMulStreamKSplitKKernel StreamK 切 K │ │ ├─ MatMulStreamKKernel / MatMulStreamKSplitKKernel StreamK 切 K │
│ ├─ MatMulBasicKernel / MatMulBasicSplitKKernel ASWT 基础模板 │ │ ├─ MatMulBasicKernel / MatMulBasicSplitKKernel ASWT 基础模板 │
│ ├─ MatMulAL1FullLoadKernel / MatMulBL1FullLoadKernel A/B 全载 │ │ ├─ MatMulAL1FullLoadKernel / MatMulBL1FullLoadKernel A/B 全载 │
│ └─ MatMulFixpipeOptiTensorKernel Fixpipe 随路搬出优化 │ │ └─ MatMulFixpipeOptiTensorKernel Fixpipe 随路搬出优化 │
└─────────────────────────────────────────────────────────────────────────────────┘</code></pre> └─────────────────────────────────────────────────────────────────────────────────────────</code></pre>
<p><strong>关键枚举</strong><code>mat_mul_v3_tiling_key_public.h</code></p> <p><strong>关键枚举</strong><code>mat_mul_v3_tiling_key_public.h</code></p>
<table><thead><tr><th>维度</th><th>枚举值</th><th>含义</th></tr></thead><tbody> <table><thead><tr><th>维度</th><th>枚举值</th><th>含义</th></tr></thead><tbody>
<tr><td>MatMulV3Model</td><td>BASIC=0 / STREAM_K=1 / K_EQUAL_ZERO=2 / TO_MUL=3 / TO_MULTI_MUL=4 / SLICE=5 / BASIC_SPLIT_K=6 / SK_SPLIT_K=7</td><td>主计算模型</td></tr> <tr><td>MatMulV3Model</td><td>BASIC=0 / STREAM_K=1 / K_EQUAL_ZERO=2 / TO_MUL=3 / TO_MULTI_MUL=4 / SLICE=5 / BASIC_SPLIT_K=6 / SK_SPLIT_K=7</td><td>主计算模型</td></tr>
<tr><td>MatMulV3FullLoad</td><td>NONE=0 / A_FULL_LOAD=1 / B_FULL_LOAD=2 / AB_FULL_LOAD=3</td><td>L1 全载模式</td></tr> <tr><td>MatMulV3FullLoad</td><td>NONE=0 / A_FULL_LOAD=1 / B_FULL_LOAD=2 / AB_FULL_LOAD=3</td><td>L1 全载模式</td></tr>
<tr><td>MatMulV3L0C2Out</td><td>ON_THE_FLY=0 / ND_FIXPIPE_1_1=1 / ND_FIXPIPE_1_2=2</td><td>L0C 输出方式</td></tr> <tr><td>MatMulV3L0C2Out</td><td>ON_THE_FLY=0 / ND_FIXPIPE_1_1=1 / ND_FIXPIPE_1_2=2</td><td>L0C 输出方式</td></tr>
<tr><td>MatMulV3ApiLevel</td><td>HIGH=0 / BASIC=1 / TENSOR=2</td><td>编程接口层级kernel 用 BASIC/TENSOR</td></tr> <tr><td>MatMulV3ApiLevel</td><td>HIGH=0 / BASIC=1 / TENSOR=2</td><td>编程接口层级kernel 实际用 BASIC/TENSOR</td></tr>
</tbody></table>
<p><strong>贯穿全文的符号约定(维度标注)</strong></p>
<table><thead><tr><th>符号</th><th>维度</th><th>含义</th></tr></thead><tbody>
<tr><td><code>M, N, K</code></td><td>元素个数</td><td>矩阵 <code>C[M,N] = A[M,K] · B[K,N]</code> 的三维</td></tr>
<tr><td><code>dtypeA, dtypeB</code></td><td>字节/元素</td><td>A、B 的 dtype 字节数FP16/BF16=2FP32=4</td></tr>
<tr><td><code>baseM, baseN, baseK</code></td><td>元素个数</td><td>L0 分块尺寸L0A/L0B 一次装的数据块),对齐 16</td></tr>
<tr><td><code>singleCoreM/N/K</code></td><td>元素个数</td><td>单核负责的 M/N/K 范围(= base 或更大)</td></tr>
<tr><td><code>mL1, nL1, kL1</code></td><td>元素个数</td><td>L1 中的分块尺寸(= singleCore 或 base见各分支</td></tr>
<tr><td><code>stepKa, stepKb</code></td><td>无量纲(个数)</td><td>K 方向 L1 分片步数K 在 L1 中切几段)</td></tr>
<tr><td><code>depthA1, depthB1</code></td><td>无量纲(个数)</td><td>L1 流水深度A/B 在 L1 中缓存的 tile 层数)</td></tr>
<tr><td><code>mCnt, nCnt</code></td><td>无量纲(个数)</td><td>M/N 方向 base 块个数 <code>mCnt=⌈M/baseM⌉</code></td></tr>
<tr><td><code>aicNum, aivNum</code></td><td></td><td>AIC 核数=32、AIV 核数=64</td></tr>
</tbody></table>
<p><strong>关键硬件规格(原理性推导的输入,来自 950PR 架构解读)</strong></p>
<table><thead><tr><th>规格</th><th>数值</th><th>推导用途</th></tr></thead><tbody>
<tr><td>Cube 每拍 MAC 数</td><td>16×16×16 = 4096 MAC = 8192 FLOP</td><td>单 AIC BF16 算力 = 1.65GHz × 8192 ≈ <strong>13.5 TFLOPS</strong></td></tr>
<tr><td>L0A / L0B / L0C / L1 / UB</td><td>64KB / 64KB / 256KB / 512KB / 512KB</td><td>分块尺寸上限(<code>baseM·baseN·4B ≤ L0C</code> → baseM=baseN=256 上限)</td></tr>
<tr><td>MTE1 / MTE3</td><td>256 BPC ≈ 422.4 GB/s单 AIC</td><td>核内 L1→L0、L0C→GM 搬移耗时</td></tr>
<tr><td>GM / L2 带宽</td><td>1.6 TB/s / 5.2 TB/s</td><td>MTE2 综合带宽(重复读走 L2 才喂饱 Cube</td></tr>
<tr><td>单 AIC 分摊 HBM</td><td>1.6TB/s ÷ 32 ≈ 50 GB/s</td><td>算术强度要求 = 13.5T/50G ≈ <strong>270 FLOP/Byte</strong>(带宽极紧张)</td></tr>
</tbody></table> </tbody></table>
<hr/> <hr/>
<h2>1. 分支决策总表DAV_3510 = 950PR</h2> <h2>1. 分支决策总表DAV_3510 = 950PR</h2>
<p>策略优先级定义在 <code>matmul_v3_tiling_strategy.h</code></p> <p>策略优先级定义在 <code>matmul_v3_tiling_strategy.h</code></p>
<pre><code>{ NpuArch::DAV_3510, {K_EQUAL_ZERO, TO_MUL, TO_MULTI_MUL, BASIC_STREAM_K, BASIC_ASWT} }</code></pre> <pre><code>{ NpuArch::DAV_3510, {K_EQUAL_ZERO, TO_MUL, TO_MULTI_MUL, BASIC_STREAM_K, BASIC_ASWT} }</code></pre>
<p>各 tiling 类按 priority 注册(<code>MM_REGISTER_TILING_TEMPLATE</code><code>MMTilingRegistry::DoTilingImpl</code>优先级逐个 <code>IsCapable()</code> + <code>DoTiling()</code><strong>第一个成功者胜出</strong></p> <p><code>MMTilingRegistry::DoTilingImpl</code> priority 顺序逐个 <code>IsCapable() + DoTiling()</code><strong>第一个成功者胜出</strong>(每个 tiling 类的 <code>DoTiling</code> 返回失败则尝试下一优先级)</p>
<table><thead><tr><th>Priority</th><th>分支</th><th>触发条件IsCapable</th><th>Device Kernel</th></tr></thead><tbody> <table><thead><tr><th>Priority</th><th>分支</th><th>Model 枚举</th><th>触发条件IsCapable</th><th>Device Kernel</th></tr></thead><tbody>
<tr><td>0</td><td><strong>K_EQUAL_ZERO</strong></td><td>无 bias 且 <code>K == 0</code></td><td>MatMulInputKEqZeroClearOutput</td></tr> <tr><td>0</td><td>K_EQUAL_ZERO</td><td>K_EQUAL_ZERO(2)</td><td>无 bias 且 <code>K==0</code></td><td>MatMulInputKEqZeroClearOutput</td></tr>
<tr><td>1</td><td><strong>TO_MUL</strong></td><td>高精度FP32(<code>isForceGrpAccForFp32</code>) ∧ 非slice ∧ (M==1 N==1) ∧ A/B 均 FP32</td><td>MatMulToMulActKernel</td></tr> <tr><td>1</td><td>TO_MUL</td><td>TO_MUL(3)</td><td>高精度FP32 ∧ 非slice ∧ (M==1 N==1) ∧ A/B 均 FP32</td><td>MatMulToMulActKernel</td></tr>
<tr><td>2</td><td><strong>TO_MULTI_MUL</strong></td><td>高精度FP32 ∧ 非slice ∧ <code>!ATransBTrans</code> ∧ 无bias ∧ A/B 均 FP32</td><td>MatMulToVectorActKernel</td></tr> <tr><td>2</td><td>TO_MULTI_MUL</td><td>TO_MULTI_MUL(4)</td><td>高精度FP32 ∧ 非slice ∧ <code>!ATransBTrans</code> ∧ 无bias ∧ FP32</td><td>MatMulToVectorActKernel</td></tr>
<tr><td>3</td><td><strong>BASIC_STREAM_K</strong></td><td>deterministic≤1 ∧ A为ND ∧ 非slice ∧ aivNum==2·aicNum ∧ (SKDPSK 条件)</td><td>MatMulStreamKKernel / StreamKSplitK / StreamKActKernel</td></tr> <tr><td>3</td><td>BASIC_STREAM_K</td><td>STREAM_K(1)/SK_SPLIT_K(7)</td><td>deterministic≤1 ∧ A为ND ∧ 非slice ∧ aivNum==2·aicNum ∧ (SKDPSK)</td><td>MatMulStreamKKernel </td></tr>
<tr><td>4</td><td><strong>BASIC_ASWT</strong></td><td>无条件(主分支)</td><td>MatMulBasicKernel 等(含 fullLoad/fixpipe 子分支)</td></tr> <tr><td>4</td><td>BASIC_ASWT</td><td>BASIC(0)/SLICE(5)/BASIC_SPLIT_K(6)</td><td>无条件(主分支)</td><td>MatMulBasicKernel 等(含全载/fixpipe 子分支)</td></tr>
<tr><td>999</td><td><strong>BASE(ASW)</strong></td><td>无条件(最终兜底,老 Cmct 接口)</td><td>MatMulActKernel</td></tr> <tr><td>999</td><td>BASE(ASW)</td><td>BASIC(0)</td><td>无条件(最终兜底,老 Cmct 接口)</td><td>MatMulActKernel</td></tr>
</tbody></table> </tbody></table>
</blockquote> </blockquote>
<hr/> <hr/>
<h2>2. 各分支详细分析</h2> <h2>2. 各分支详细分析</h2>
</blockquote>
<hr/>
<h3>2.1 K_EQUAL_ZEROpriority 0</h3> <h3>2.1 K_EQUAL_ZEROpriority 0</h3>
<p><strong>条件</strong><code>!hasBias &amp;&amp; kValue == 0</code></p> <p><strong>触发条件</strong><code>!hasBias &amp;&amp; K == 0</code></p>
<p><strong>原理</strong>K=0 时 <code>C[i,j] = 0</code>,输出恒为全零矩阵。若走 CubeMMAD 对空 K 做无效计算;而 64 个 AIV 是天然的并行批量写单元,直接对 <code>C</code><code>M×N</code> 个元素清零即可。</p>
<p><strong>实现</strong><code>matmul_v3_k_equal_zero_tiling.cpp</code> + <code>mat_mul_input_k_eq_zero_clear_output.h</code></p> <p><strong>实现</strong><code>matmul_v3_k_equal_zero_tiling.cpp</code> + <code>mat_mul_input_k_eq_zero_clear_output.h</code></p>
<pre><code>totalDataAmount = M · N // 需清零的元素总数(元素个数)
usedCoreNum = aivNum // 64 个 AIV 并行</code></pre>
<p>Device 侧 <code>MatMulInputKEqZeroClearOutput(biasGM, cGM, tilingData)</code>:每个 AIV 负责一段连续的 <code>C</code> 地址,一次搬移清零。数据流极简:</p>
<pre><code>AIV ──(写0)──► GM(C) // 无 GM 读、无 Cube 参与</code></pre>
<p><strong>评价</strong>:正确且高效(不浪费 Cube、不产生 GM 读流量)。适用面窄(仅 K==0 且无 bias</p>
<hr/>
<h3>2.2 TO_MULpriority 1—— M==1 或 N==1 的 FP32 退化向量乘</h3>
<p><strong>触发条件</strong><code>isForceGrpAccForFp32</code>op_impl_mode_enum==0x4 高精度 FP32∧ 非 slice ∧ (M==1 N==1) ∧ dtypeA==dtypeB==FP32。</p>
<p><strong>原理(结合硬件)</strong>Cube 的 MMAD 指令最小分形是 <strong>16×16×16</strong>。当 <code>M==1</code>MMAD 的 M 维 16 个 lane 只能用 1 个 → <strong>Cube 算力利用率仅 1/16 ≈ 6%</strong>。此时 <code>[1,K]·[K,N]</code> 本质是<strong>一个 K 维向量与 N 个 K 维向量的点积</strong>,用 AIV 的向量乘加(<code>Mul+ReduceSum</code>)天然适合,且 64 个 AIV 的 FP32 算力 27 TFLOPS 足够覆盖。</p>
<p><strong>实现</strong><code>matmul_v3_to_mul_tiling.cpp</code> + <code>mat_mul_to_mul_cmct.h</code>数据流为「GM→UB→AIV 向量乘→UB→GM」全程不走 Cube</p>
<pre><code>GM(A[M=1,K]) ──MTE2──► UB ──AIV向量乘加──► UB ──MTE3──► GM(C[N])
GM(B[K,N]) ──MTE2──► UB</code></pre>
<p>tiling 参数由 <strong>UB 容量</strong><code>ubSize/4</code> 个 float反推</p>
<pre><code>tmpMN = ⌈(M·N) / aivNum⌉ // 每核摊到的 MN 元素数(元素个数)
baseMN = min(Align(tmpMN, 对齐), BASE_MN) // MN 方向分块(元素个数)
baseK = (ubCount - baseMN - biasCount)/5/baseMN // K 方向分块,受 UB 容量约束(元素个数)
loopK = ⌈K / baseK⌉ // K 循环次数(无量纲)
usedCoreNum = min(⌈M·N/baseMN⌉, aivNum) // 实际核数</code></pre>
<p><code>dataCopyMode</code> 区分「K 内轴连续((K,N)/(K,M) 布局」还是「MN 内轴连续」,决定 baseK/baseMN 的取舍顺序。</p>
<p><strong>评价</strong>:对退化场景绕开 Cube 走 AIV 正确。局限:① 仅覆盖高精度 FP32<strong>FP16/BF16 的 M==1/N==1 无退化分支、仍走 Cube</strong>§6 改进点);② TO_MULTI_MUL 不支持 bias。</p>
<hr/>
<h3>2.3 TO_MULTI_MULpriority 2—— 特定转置组合的 FP32 退化向量乘</h3>
<p><strong>触发条件</strong>高精度FP32 ∧ 非slice ∧ <code>!ATrans ∧ BTrans</code> ∧ 无bias ∧ FP32。</p>
<p><strong>原理</strong>:与 TO_MUL 同(退化向量乘),但锁定 <code>!ATrans∧BTrans</code>A 行主序K 连续、B 列主序K 连续),<strong>内积方向 K 两侧都连续</strong>,向量乘访存顺序、无需 gather。</p>
<p><strong>实现</strong><code>matmul_v3_to_multi_mul_tiling.cpp</code> + <code>mat_mul_to_multi_mul_cmct.h</code>同样「GM→UB→AIV 向量乘→UB→GM」。核数分配用 <code>CalcBasicBlock()</code> 启发式均衡:</p>
<pre><code>mCore = ⌈M/baseM⌉, nCore = ⌈N/baseN⌉
if (mCore·nCore &lt; aivNum) CalcBasicBlock() // 块数不足分满核时重调 baseM/baseN
while (baseN ≥ 2·baseM ∧ nCore &lt; aivNum/2): // N 侧块太多 → nCore·=2, baseN=⌈N/nCore⌉
while (baseM ≥ 2·baseN ∧ mCore &lt; aivNum/2): // M 侧块太多 → mCore·=2, baseM=⌈M/mCore⌉
usedCoreNum = min(mCore·nCore, aivNum)</code></pre>
<p><strong>评价</strong>:与 TO_MUL 同族但更窄(限转置、无 bias。共同原则<strong>矩阵乘退化到 M==1 或 N==1 时,绕开 Cube 走 AIV</strong>,规避 Cube 16 对齐分形浪费。</p>
<hr/>
<h3>2.4 BASIC_STREAM_Kpriority 3—— 切 K 满核</h3>
<h4>2.4.0 解决的问题(硬件原理)</h4>
<p><strong>核心矛盾</strong>baseM=baseN=256 时,若 M、N 较小(如 M=N=256<code>mCnt·nCnt = 1×1 = 1</code> 个 MN 块,但 <code>aicNum=32</code><strong>只有 1 个核工作、31 核空闲</strong>,算力利用率 1/32。950PR「算力/带宽比极高」架构下,大 K 瘦高形状(如 <code>[256, 8192]×[8192, 256]</code>)是典型 caseMN 方向分不满核,但 K 很大、总计算量足够喂饱 32 核。</p>
<p><strong>解法</strong>:把 K 轴切成 <code>kCnt</code> 份分给多个核,每个核算 <code>M×N×singleCoreK</code><strong>部分和</strong>,最后跨核累加。有效并行度变为 <code>mCnt·nCnt·kCnt</code></p>
<pre><code>普通 ASWTM=N=256, K=8192kCnt=1:
┌────────────────────────────────────────────┐
│ 核0: [256×256] × [256×8192] (算完整 K) │ ← 只有 1 核31 核空闲
└────────────────────────────────────────────┘
StreamK切 KkCnt=32:
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ 核0: K[0:256] │ │ 核1: K[256:512]│ ... │ 核31: K[...] │ ← 32 核各算一段 K
└───────┬───────┘ └───────┬───────┘ └───────┬───────┘
└──────────────────┴──────────────────────────┘
▼ 核间累加workspaceAIV 顺序累加)
C[M,N] = Σ_k 部分和</code></pre>
<h4>2.4.1 分支结构(含 3 个子模式)</h4>
<p><strong>IsCapable 前置条件</strong><code>MatMulV3BasicStreamKTiling::IsCapable</code></p>
<pre><code>deterministicLevel ≤ 1 // 切 K 核间累加顺序不确定,强一致性场景禁用整分支
∧ aFormat == ND // 左矩阵必须 ND
∧ 非 slice // 不支持非连续视图
∧ aivNum == 2·aicNum // 必须 AIC:AIV = 1:2
∧ (CheckStreamKSKTiling() CheckStreamKDPSKTiling())</code></pre>
<p><strong>子模式 ①SK纯切 K</strong> —— <code>CheckStreamKSKTilingDav3510</code></p>
<pre><code>align(K, 256) &gt;= max(8192, aicNum·256B) / dtypeA // K 足够大
mCnt · nCnt &lt;= aicNum / 2 // MN 块数不超过核数一半</code></pre>
<p>语义M/N 小到「MN 块数 ≤ 核数一半」K 大 → 全 K 切分,每核算一段 K。</p>
<p>FP32 且非 hf32 时,<code>alignValue</code> 从 256 退化为 32<code>BLOCK_BYTE_SIZE</code>)。</p>
<p><strong>子模式 ②DPSK数据并行 StreamK</strong> —— <code>CheckStreamKDPSKTilingDav3510</code></p>
<pre><code>M % 256 == 0 且 N % 256 == 0 // MN 严格 256 对齐
K &gt;= max(8192, aicNum·128B) / dtypeA // K 足够大
totalMNCnt &gt;= aicNum // MN 块数能分满核(主轮)
totalMNCnt % aicNum != 0 // 但有尾轮余数
(totalMNCnt % aicNum) &lt;= aicNum/2 // 余数不超过核数一半</code></pre>
<p>语义:<strong>主轮 MN 块数 ≥ 核数DP 满核、K 不分片),尾轮剩余 MN 块 &lt; 核数</strong> → 尾轮把 K 切开分给空闲核,且提前执行让 AIV 累加与 AIC 下一轮计算重叠(数据并行)。</p>
<p><strong>子模式 ③SK_SPLIT_K</strong>SK/DPSK 基础上,若 <code>FP32 ∧ singleCoreK ≥ FP32_SPLIT_K_THRESHOLD</code>,则单核内再切 K<code>splitSingleK</code>,保 FP32 累加精度model 由 <code>STREAM_K</code> 升级为 <code>SK_SPLIT_K</code></p>
<h4>2.4.2 tiling 参数推导(<code>DoOpTiling</code></h4>
<pre><code>baseM, baseN // 由 ResetBase + 后续逻辑给出§3.2),通常 256
mCnt = ⌈M/baseM⌉, nCnt = ⌈N/baseN⌉
totalMNCnt = mCnt · nCnt
if (totalMNCnt &lt;= aicNum/2): // 纯 SK 模式
调整 mCnt/nCnt 到 [aicNum/3, aicNum/2] 区间 // 避免切得过碎
baseM = Align(⌈M/mCnt⌉, 16); baseN = Align(⌈N/nCnt⌉, 16)
kCnt = ⌊aicNum / totalMNCnt⌋ // 每个 MN 块分配多少核切 K
singleCoreK = ⌈K / kCnt⌉ // 单核 K 分片(元素个数)
else: // DPSK 模式(尾轮切 K
kCnt = aicNum / (totalMNCnt % aicNum)
singleCoreK = ⌈K / kCnt⌉
kCnt = ⌈K / singleCoreK⌉ // 回算实际切分数
// baseK受 L0A 容量约束§3.3
kValueMax = ⌊L0A/DB/dtypeA / max(baseM, baseN)⌋ (按 128B/dtype 或 16 对齐)
baseK = min(singleCoreK, kValueMax)
CalL1Tiling(...) // 复用 §3.4 计算 stepKa/stepKb
workspace = aicNum·256·256·4B + 20·MB // 核间累加工作区(字节)</code></pre>
<p><strong>L0C2Out</strong>N 大且 N 不对齐(<code>N&gt;64 ∧ N%16≠0 ∧ M&gt;2 ∧ M·N≥256</code>)→ <code>ND_FIXPIPE_1_2</code>,否则 <code>ON_THE_FLY</code>(同 §2.5.4 的 fixpipe 逻辑)。</p>
<h4>2.4.3 Device 数据流(<code>block_scheduler_streamk.h</code> + StreamK kernel</h4>
<p>StreamK 的 L1 只装一个 base 块(<code>mL1=baseM, nL1=baseN</code>),核间通过 workspace 累加:</p>
<pre><code>DP 主轮(每核一个 MN 块,算完整 K:
GM(A块) ─MTE2─► L1 ─MTE1─► L0A ─┐
├─MMAD─► L0C ─Fixpipe─► GM(C块) // 直接写结果
GM(B块) ─MTE2─► L1 ─MTE1─► L0B ─┘
SK 尾轮(每核一个 MN 块的一段 K:
GM(A块) ─► L1 ─► L0A ─┐
├─MMAD─► L0C ─Fixpipe─► workspace(部分和) // 写部分和
GM(B块) ─► L1 ─► L0B ─┘
然后 AIV 从 workspace 顺序累加Atomic/确定性累加)─► GM(C块)</code></pre>
<p><code>CheckIsSkScene(tileIdx)</code> 判定当前核索引属于 DP 轮还是 SK 轮:<code>ceil((tileIdx+1)/usedCoreNum) == ceil(tileNum/usedCoreNum)</code>(最后一轮是 SK</p>
<h4>2.4.4 评价</h4>
<ul> <ul>
<li><code>totalDataAmount = M * N</code><code>usedCoreNum = aivNum</code>64 个 AIV</li> <li><strong>优点</strong>:① 解决&quot;MN 块数不足分满核、但 K 大&quot;的场景,大 K 瘦高形状算力利用率可从 1/32 提升到接近满核;② DPSK 让尾轮 SK 与 AIC 计算重叠,减少核空闲</li>
<li>Device 侧 <code>MatMulInputKEqZeroClearOutput</code> 直接用 AIV 对输出 <code>C</code> 清零,完全不启动 Cube</li> <li><strong>缺点</strong>:① workspace 核间累加带来<strong>额外 GM 写+读流量</strong>(部分和写出 + AIV 读回950PR 带宽紧张下成本不低;② 累加顺序不确定 → <code>deterministicLevel&gt;1</code> 整分支禁用;③ 仅 A 为 ND④ DPSK 要求 M/N 严格 256 对齐;⑤ FP32 切 K 需额外 <code>splitSingleK</code> 保精度,复杂度高</li>
</ul> </ul>
<p><strong>评价</strong>K=0 是退化空矩阵乘,输出恒为 0。用 AIV 批量清零、不浪费 Cube 计算单元,实现正确且高效。适用面窄(仅 K==0 且无 bias</p> <hr/>
<h3>2.2 TO_MULpriority 1</h3> <h3>2.5 BASIC_ASWTpriority 4—— 主分支(含 4 个子分支</h3>
<p><strong>条件</strong><code>isForceGrpAccForFp32</code>op_impl_mode_enum == 0x4 高精度)∧ 非 slice ∧ (M==1 N==1) ∧ A/B 均 FP32</p> <p><strong>IsCapable</strong>无条件返回 true除命中 StreamK 场景外的绝大多数 case 都落在这里)</p>
<p><strong>实现</strong><code>matmul_v3_to_mul_tiling.cpp</code> + <code>mat_mul_to_mul_cmct.h</code></p> <p><code>DoOpTiling</code> 先调用父类 <code>MatMulV3AswTiling::DoOpTiling()</code> 完成基础 tiling<code>ResetBase→GetRebalanceBlock→CalcTailBasicBlock→CalL1Tiling</code>,即 §3 的完整流程),得到 baseM/baseN/baseK 后,再按顺序做<strong>子分支决策</strong></p>
<ul>
<li>矩阵乘退化为<strong>向量乘</strong>M==1 或 N==1 时本质是点积/向量乘),全部由 AIV 完成,不占 Cube。</li>
<li>tiling<code>baseMN</code><code>baseK</code> 由 UB 容量(<code>ubSize/sizeof(float)</code>)反推;<code>loopK = ceil(K/baseK)</code>;区分 <code>dataCopyMode</code>(判断内外轴是否连续搬运)。</li>
<li><code>usedCoreNum = min(tileNum, aivNum)</code> 分核。</li>
</ul>
<p><strong>评价</strong>M==1 或 N==1 时 Cube 的 16×16×16 分形利用率极低,改用 AIV 向量乘是正确决策。局限:仅覆盖高精度 FP32 模式(<code>isForceGrpAccForFp32</code>FP16/BF16 的 M==1/N==1 场景走不到这里。</p>
<h3>2.3 TO_MULTI_MULpriority 2</h3>
<p><strong>条件</strong>高精度FP32 ∧ 非slice ∧ <code>!ATrans &amp;&amp; BTrans</code> ∧ 无 bias ∧ A/B 均 FP32。</p>
<p><strong>实现</strong><code>matmul_v3_to_multi_mul_tiling.cpp</code> + <code>mat_mul_to_multi_mul_cmct.h</code></p>
<ul>
<li>同样退化为 AIV 向量乘,但针对 <code>!ATrans &amp;&amp; BTrans</code> 的特定转置组合A 行主序、B 列主序,恰好内积方向连续)。</li>
<li><code>CalcBasicBlock()</code>:按 mCore/nCore 的核数分配调整 baseM/baseN使 M/N 方向的核分配均衡(<code>while baseN &gt;= 2*baseM ...</code> 启发式均衡)。</li>
</ul>
<p><strong>评价</strong>:与 TO_MUL 同类,但更窄(限定转置组合、不支持 bias。两个分支共同说明<strong>当矩阵乘退化到 M==1 或 N==1 时,源码选择绕开 Cube 走 AIV</strong>,规避 Cube 16 对齐分形粒度的浪费。</p>
<h3>2.4 BASIC_STREAM_Kpriority 3—— StreamK / DPSK</h3>
<p><strong>IsCapable 前置条件</strong><code>deterministicLevel ≤ 1</code>(切 K 核间累加顺序不确定,强一致性场景禁用)∧ A 为 ND ∧ 非 slice ∧ <code>aivNum == 2·aicNum</code></p>
<p><strong>SK 模式条件</strong><code>CheckStreamKSKTilingDav3510</code></p>
<pre><code>align(K, 256) &gt;= max(8192, aicNum·256B) / dtypeSize
mCnt · nCnt &lt;= aicNum / 2 // MN 用 base 块切的份数不超过核数一半</code></pre>
<ul>
<li>语义:<strong>M/N 太小、块数不够分满核,但 K 足够大</strong> → 把 K 切成多份分给多个核,每核算一段 K 后跨核累加。</li>
<li>FP32 且非 hf32 时 base 块对齐单位退化为 32<code>BLOCK_BYTE_SIZE</code>)。</li>
</ul>
<p><strong>DPSK 模式条件</strong><code>CheckStreamKDPSKTilingDav3510</code></p>
<pre><code>M % 256 == 0 且 N % 256 == 0
K &gt;= max(8192, aicNum·128B) / dtypeSize
totalMNCnt &gt;= aicNum 且 totalMNCnt % aicNum != 0 且 余数 &lt;= aicNum/2</code></pre>
<ul>
<li>语义:<strong>主轮能分满核DP但尾轮有 M/N 剩余块无法分满</strong> → 尾轮用 SK 方式(切 K提前执行让 AIV 累加时 AIC 继续算下一轮(数据并行)。</li>
</ul>
<p><strong>tiling 核心</strong><code>DoOpTiling</code></p>
<ul>
<li><code>singleCoreK = ceil(K / kCnt)</code><code>kCnt</code> 由核数与 MN 块数的关系决定;</li>
<li><code>baseK = min(singleCoreK, L0A容量约束)</code></li>
<li><code>workspace = aicNum · 256·256·4B + RPC·MB</code><strong>核间累加工作区</strong></li>
<li><code>GetL0C2Out</code>N 不对齐且大 N 时选 <code>ND_FIXPIPE_1_2</code></li>
<li>modelFP32 且 <code>singleCoreK &gt;= FP32_SPLIT_K_THRESHOLD</code><code>SK_SPLIT_K</code>(单核内再切 K 保精度),否则 <code>STREAM_K</code></li>
</ul>
<p><strong>Device swizzle</strong><code>block_scheduler_streamk.h</code></p>
<ul>
<li><code>mL1 = baseM</code><code>nL1 = baseN</code>StreamK 中 L1 只装一个 base 块);</li>
<li><code>tileNum = DP部分MN块数 + 尾轮SK部分MN块数·skKTileNum</code></li>
<li><code>CheckIsSkScene(tileIdx)</code> 判定当前块属于 DP 主轮K 不分片)还是 SK 尾轮K 分片);</li>
<li>同样应用 SWAT 窗口 + 蛇形扫描(见 §4</li>
</ul>
<p><strong>评价</strong></p>
<ul>
<li><strong>优点</strong>:解决&quot;MN 块数不足分满核、但 K 大&quot;这一 ASWT 无法高效处理的场景,大幅提升瘦高形状(大 K的算力利用率DPSK 让尾轮 SK 与 AIC 计算重叠,减少核空闲。</li>
<li><strong>缺点</strong>:① 需要 workspace 做跨核累加,<strong>额外 GM 写+读流量</strong>,在 950PR 带宽紧张下成本不低;② 累加顺序不确定 → <code>deterministicLevel&gt;1</code> 时整分支禁用;③ 仅支持 A 为 ND④ DPSK 要求 M/N 严格 256 对齐,形状不齐时退化为纯 SK 或走 ASWT。</li>
</ul>
<h3>2.5 BASIC_ASWTpriority 4—— 主分支</h3>
<p><strong>IsCapable</strong>:无条件返回 true。其 <code>DoOpTiling</code> 先调用父类 <code>MatMulV3AswTiling::DoOpTiling()</code> 完成基础 tilingbaseM/baseN/baseK/singleCore再按顺序做子决策</p>
<pre><code>DoOpTiling(): <pre><code>DoOpTiling():
isSlice_ = IsSelfNonContiguous() // 非连续 3D slice isSlice_ = IsSelfNonContiguous() // 子分支④:非连续 3D slice
l0C2Out_ = GetL0C2Out() // 是否走 fixpipe 优化 l0C2Out_ = GetL0C2Out() // 子分支③:fixpipe 优化判定
if (!isSlice_ &amp;&amp; CheckAL1FullLoad()) → DoAL1FullLoad() // A 全载 L1 if (!isSlice_ &amp;&amp; CheckAL1FullLoad()) → DoAL1FullLoad() // 子分支①:A 全载
elif (!isSlice_ &amp;&amp; CheckBL1FullLoad()) → DoBL1FullLoad() // B 全载 L1 elif (!isSlice_ &amp;&amp; CheckBL1FullLoad()) → DoBL1FullLoad() // 子分支②:B 全载
elif (l0C2Out_ == ON_THE_FLY) → 普通场景,L1 剩余容量均分 stepK elif (l0C2Out_ == ON_THE_FLY) → 普通场景L1 剩余容量均分 stepK
else → fixpipe 优化场景 else → fixpipe 优化场景
CheckFp32SplitK() // FP32 大 K → BASIC_SPLIT_K CheckFp32SplitK() // FP32 大 K → BASIC_SPLIT_K
CheckApiLevelAndModel() // tensor/basic api</code></pre> CheckApiLevelAndModel() // BASIC / TENSOR api</code></pre>
<p><strong>A 全载条件</strong><code>CheckAL1FullLoad</code></p> <p>ASWT 的<strong>标准数据流</strong>所有子分支共享的骨架,<code>mat_mul_asw_kernel.h</code><code>mm_.Iterate()</code></p>
<pre><code>GM(A块) ─MTE2─► L1(A) ─MTE1─► L0A ─┐
├─ MMAD(16×16×16) ─► L0C ─Fixpipe─► GM(C块)
GM(B块) ─MTE2─► L1(B) ─MTE1─► L0B ─┘
MTE2 重复读可命中 L25.2TB/sL1 用 Double Buffer 掩盖搬移延迟)</code></pre>
<hr/>
<h4>2.5.1 子分支①A 全载 L1A_FULL_LOAD</h4>
<p><strong>触发条件</strong><code>CheckAL1FullLoad</code></p>
<pre><code>l0C2Out == ON_THE_FLY // 不与 fixpipe 叠加
∧ cubeBoundParam &gt; cubeBoundEdge // 非 CubeBoundMTE2 是瓶颈,重复读代价高)
∧ nCnt &gt; aicNum // N 方向块数多于核数 → 同一 A 块被跨 N 的核重复读
∧ !(K &lt;= 128 &amp;&amp; mCnt != 1) // 排除 Fixp Bound 多轮
∧ (M·K·dtypeA + bias) &lt;= 3/4·L1 // 整个 A + bias 能装进 3/4 L1384KB</code></pre>
<p><strong>原理(结合硬件)</strong>:当 <code>nCnt &gt; aicNum</code>,同一列(固定 m的 A 块会被 <code>nCnt</code> 个核重复读。若 A 的 <code>M×K</code> 整个能装进 L1则把它<strong>常驻 L1</strong>,只流式搬 B → <strong>A 的重复读从 GM/L2 级别降为 0</strong>,只剩 B 的单次读 + A 的单次读。对 950PR 的 1.6TB/s 带宽是直接省流。</p>
<pre><code>A 全载数据流A 常驻 L1B 按 baseN 流式):
GM(A[M×K]) ─MTE2─► L1(A, 常驻, 不再搬出)
│每次只搬 B 块:
GM(B[K×baseN]) ─MTE2─► L1(B) ─MTE1─► L0B ─┐
L1(A) ─MTE1─► L0A ────────────────────────┼─MMAD─► L0C ─Fixpipe─► GM(C)</code></pre>
<p><strong>tiling</strong><code>DoAL1FullLoad</code></p>
<pre><code>singleCoreM = M // M 不再分核A 常驻M 方向一块)
singleCoreN = baseN // N 方向按 baseN 分
aL1Size = M·K·dtypeA // A 全载字节数
remainL1 = L1 - (aL1Size + bias) // L1 剩余容量
maxBaseN = min(remainL1/(baseK·dtypeB·DB), L0C/(baseM·4B·DB)) // 受 L1 剩余 + L0C 双缓冲约束
baseN = min(baseN, maxBaseN, ⌈N/aicNum⌉对齐16) // 三者取小
stepKa = ⌈K/baseK⌉, stepM = ⌈M/baseM⌉ // K、M 方向 L1 分片
stepKb = 由 B 的 L1 搬移量 + 256B 对齐确定 // B 侧 K 分片
l1BufferNum = (4·B搬移量 + aL1Size + bias &gt; L1) ? 2 : 4 // 能否 4-buffer
dbL0C = (baseM·baseN·4B·2 &lt;= L0C) ? 2 : 1 // L0C 双缓冲
usedCoreNum = min(nCore·batchNum, aicNum)</code></pre>
<hr/>
<h4>2.5.2 子分支②B 全载 L1B_FULL_LOAD</h4>
<p>与 A 全载完全对称:<code>!CubeBound ∧ mCnt &gt; aicNum ∧ (K·N·dtypeB + bias) &lt;= 3/4·L1</code> 时,整个 B 常驻 L1A 按 baseM 流式。</p>
<pre><code>B 全载数据流B 常驻 L1A 按 baseM 流式):
GM(B[K×N]) ─MTE2─► L1(B, 常驻)
GM(A[baseM×K]) ─MTE2─► L1(A) ─MTE1─► L0A ─┐
L1(B) ─MTE1─► L0B ────────────────────────┼─MMAD─► L0C ─Fixpipe─► GM(C)</code></pre>
<p>tiling 对称:<code>singleCoreN=N</code><code>singleCoreM=baseM</code><code>baseM = min(baseM, 剩余容量上限, L0C上限, ⌈M/aicNum⌉)</code></p>
<hr/>
<h4>2.5.3 子分支③Fixpipe 随路搬出优化ND_FIXPIPE_1_1 / 1_2</h4>
<p><strong>触发条件</strong><code>GetL0C2OutDav3510</code></p>
<pre><code>isValidMKN = (K &lt;= 256) ∧ (M &gt;= 256) // 计算量小、M 大
isMultiRound = (mCnt·nCnt &gt; aicNum) // 多轮(搬出流水与计算重叠才有意义)
isUnalignedN = (N·cDtypeSize % 128 != 0) ∧ (N·cDtypeSize &gt; 256) // N 不对齐 128B
fixpipeBound = isValidMKN ∧ isMultiRound ∧ isUnalignedN
→ 满足且 aivNum == 2·aicNum
FP16/BF16 → ND_FIXPIPE_1_1 FP32 → ND_FIXPIPE_1_2</code></pre>
<p><strong>原理(结合硬件)</strong>:小 K 大 M 场景MMAD 计算时间短,<code>T_FIXPIPE = M·N·dtypeC / BW_fixp</code> 可能超过 <code>T_MMAD</code>,成为 <strong>Fixpipe Bound</strong>。950PR 的 Fixpipe 随路硬化L0C→GM 直接量化/排布转换)但<strong>对 N 不对齐(非 128B 对齐)的搬出效率低</strong><code>ND_FIXPIPE_1_1/1_2</code> 用 AIV 辅助做搬出对齐1V1 或 1V2 的 AIC:AIV 配比),配合 <code>SetMMLayoutTransform(true)</code>fixp 用 N 搬出)+ UnitFlag512B 粒度同步),让 Fixpipe 与 Cube 并行、搬出对齐。</p>
<pre><code>普通ON_THE_FLY: MMAD ──────► 等算完 ──Fixpipe──► GM (串行)
Fixpipe 优化: MMAD(512B) ─► 立即随路 Fixpipe ─► GM (并行,粒度 512B)</code></pre>
<hr/>
<h4>2.5.4 子分支④:非连续 SliceSLICE model</h4>
<p><strong>触发条件</strong><code>IsSelfNonContiguous(context)</code> —— self 是 view 且 storageShape 为 1D、self 3D / mat2 2DcreateView with TensorV2 场景)。此时 model=SLICE<code>MatMulBasicKernel</code><code>sliceM/srcNdStride</code> 做非连续搬移NDDMA 多维重排),<code>GetBlockShape</code> 对 sliceM 对齐。</p>
<hr/>
<h4>2.5.5 评价</h4>
<ul> <ul>
<li><code>l0C2Out == ON_THE_FLY</code>(不叠加 fixpipe</li> <li><strong>优点</strong>:① 通用性强,覆盖绝大多数 shape② SWAT 提升 L2 命中§4③ A/B 全载在&quot;一侧重复读多、另一侧能装进 L1&quot;时显著减少 GM→L1 流量;④ fixpipe 优化在特定小 K 大 M 场景让搬出与计算并行;⑤ 尾轮负载均衡§3.5)减少尾轮算力浪费。</li>
<li>非 CubeBound<code>cubeBoundParam &gt; cubeBoundEdge</code>,即 MTE2 是瓶颈、重复读代价高);</li> <li><strong>缺点</strong>:① baseM/baseN 是<strong>启发式搜索</strong>§3.2 的 <code>cubeBoundParam/balanceRate</code> 权衡),非严格最优;② A/B 全载只支持&quot;整个矩阵常驻 L1&quot;一种粒度A/B 稍大于 3/4 L1 时直接放弃存在优化断档§6③ fixpipe 条件苛刻K≤256 ∧ M≥256 ∧ N 不对齐),覆盖面窄;④ GM 带宽未区分读写§6</li>
<li><code>nCnt &gt; aicNum</code>N 方向块数多于核数,存在跨核重复读 A</li>
<li>排除&quot;Fixp Bound 多轮&quot;<code>K&lt;=128 &amp;&amp; mCnt!=1</code></li>
<li>整个 AM×K+ bias ≤ <strong>3/4 L1</strong></li>
</ul>
<p><strong>B 全载条件</strong><code>CheckBL1FullLoad</code>):对称(非 CubeBound ∧ <code>mCnt &gt; aicNum</code> ∧ B+bias ≤ 3/4 L1</p>
<p><strong>A 全载实现</strong><code>DoAL1FullLoad</code>):整个 A 常驻 L1B 按 <code>baseN</code> 分块流式搬入;<code>singleCoreM = M</code>(不再分 M<code>singleCoreN = baseN</code><code>baseN</code> 取 min(原值, L1剩余容量上限, L0C双缓冲上限, 负载均衡值)<code>stepKb</code> 由 B 的 L1 搬移量 + 256B 对齐约束确定;<code>l1BufferNum</code> 判断能否 4-buffer。</p>
<p><strong>B 全载实现</strong><code>DoBL1FullLoad</code>):对称(整个 B 常驻 L1A 流式)。</p>
<p><strong>L0C2Outfixpipe 优化)条件</strong><code>GetL0C2OutDav3510</code></p>
<pre><code>isValidMKN = K&lt;=256 &amp;&amp; M&gt;=256
isMultiRound = mCnt·nCnt &gt; aicNum
isUnalignedN = (N·cDtypeSize % 128 != 0) &amp;&amp; (N·cDtypeSize &gt; 256)
fixpipeBound = isValidMKN &amp;&amp; isMultiRound &amp;&amp; isUnalignedN</code></pre>
<p>满足且 <code>aivNum == 2·aicNum</code> → FP16/BF16 选 <code>ND_FIXPIPE_1_1</code>FP32 选 <code>ND_FIXPIPE_1_2</code></p>
<p><strong>Device kernel 分发</strong><code>mat_mul_v3.cpp</code><code>if constexpr</code>):按 (ApiLevel, FullLoad, Model, L0C2Out) 组合映射到 <code>MatMulBasicKernel</code> / <code>MatMulAL1FullLoadKernel</code> / <code>MatMulBL1FullLoadKernel</code> / <code>MatMulFixpipeOptiTensorKernel</code> / <code>MatMulBasicSplitKKernel</code>(均基于 Blaze::Gemm 模板库)。</p>
<p><strong>评价</strong></p>
<ul>
<li><strong>优点</strong>:① 通用性强,覆盖绝大多数 shape② SWAT 提升 L2 命中率;③ A/B 全载在&quot;一侧重复读多、另一侧能装进 L1&quot;时显著减少 GM→L1 流量;④ fixpipe 优化在特定小 K 大 M 场景让搬出与计算并行;⑤ 尾轮负载均衡减少尾轮算力浪费。</li>
<li><strong>缺点</strong>:① baseM/baseN 的选择是<strong>启发式搜索</strong>§3 的 <code>cubeBoundParam/balanceRate</code> 权衡),不是严格最优解;② A/B 全载只支持&quot;整个矩阵常驻 L1&quot;一种粒度,且限定非 CubeBound、ND 场景;③ fixpipe 条件苛刻K≤256 且 M≥256 且 N 不对齐),覆盖面窄;④ 未区分 GM 读写带宽,<code>GetHbmBW</code> 用统一换算值。</li>
</ul> </ul>
<hr/>
<h3>2.6 BASEpriority 999—— 老 Cmct 接口兜底</h3> <h3>2.6 BASEpriority 999—— 老 Cmct 接口兜底</h3>
<p><strong>实现</strong><code>matmul_v3_asw_tiling.cpp</code> + <code>mat_mul_asw_kernel.h</code> / <code>mat_mul_asw_block.h</code></p> <p><strong>实现</strong><code>matmul_v3_asw_tiling.cpp</code> + <code>mat_mul_asw_kernel.h</code> / <code>mat_mul_asw_block.h</code></p>
<ul> <ul>
<li><code>DoOpTiling</code>: <code>ResetBase</code><code>GetRebalanceBlock</code><code>CalcTailBasicBlock</code><code>CalL1Tiling</code></li> <li><code>DoOpTiling</code>: <code>ResetBase → GetRebalanceBlock → CalcTailBasicBlock → CalL1Tiling</code>(与 BASIC_ASWT 基础 tiling 完全相同的引擎§3</li>
<li>Device <code>MatMulActKernel</code>(老 Cmct 接口),核内 <code>mm_.Iterate()</code> 执行标准 <code>GM→L1→L0A/L0B→Cube→L0C→Fixpipe</code> 流水;<code>SetMMLayoutTransform(true)</code> 让 Fixpipe 用 N 搬出实现 Cube 与 Fixpipe 并行;</li> <li>Device 走 <code>MatMulActKernel</code>(老 Cmct 接口),核内 <code>mm_.Iterate()</code> 执行标准 <code>GM→L1→L0A/L0B→Cube→L0C→Fixpipe</code> 流水;<code>SetMMLayoutTransform(true)</code> 让 Fixpipe 用 N 搬出实现 Cube 与 Fixpipe 并行;</li>
<li>块索引计算<code>MatmulAswBlock::UpdateBasicIndex</code>)同样实现 SWAT 窗口 + 蛇形扫描 + 尾轮重切。</li> <li>块索引(<code>MatmulAswBlock::UpdateBasicIndex</code>)同样实现 SWAT 窗口 + 蛇形扫描 + 尾轮重切§4</li>
</ul> </ul>
<p><strong>评价</strong>作为最终兜底保证正确性,逻辑与 BASIC_ASWT 基础 tiling 一致,但<strong>没有</strong>全载 / fixpipe / StreamK 等新优化,性能上限低于主分支</p> <p><strong>与 BASIC_ASWT 的区别</strong>两者共用同一套 tiling 引擎和 swizzle区别只在 <strong>Device 接口代际</strong>——BASE 走老 Cmct 接口(无全载/fixpipe/StreamK 的 kernel 模板BASIC_ASWT 走新 Blaze 接口(支持全载/fixpipe/SplitK。实际运行时 BASIC_ASWTpriority 4几乎总是先命中BASE 仅在注册表异常时兜底</p>
<p><strong>评价</strong>:保证正确性;性能上限低于主分支(缺全载/fixpipe/SplitK 等新优化)。</p>
<hr/> <hr/>
<h2>3. 核心 tiling 算法(决定 baseM/baseN/baseK 与单核形状</h2> <h2>3. 核心 tiling 算法(ASW 系分支共用的参数计算引擎</h2>
<h3>3.1 ResetBase初始值<code>matmul_v3_tiling_helper.cpp</code></h3> </blockquote>
<pre><code>// ResetBaseDefaultDAV_3510 在此基础上改 baseM <h3>3.1 ResetBase(初始值)</h3>
usedCoreNum = aicNum; // 32 <pre><code>// ResetBaseDav3510950PR
baseM = 256; baseN = 256; // 950PR 的 base 块 usedCoreNum = aicNum = 32
baseK = 128B / dtypeSize; // FP16=64, FP32=32 baseM = 256; baseN = 256 // 950PR 的 base 块(上代为 128
iterateOrder = ITER_COL_FIRST; // 列优先 baseK = 128B / dtypeA // FP16/BF16=64 元素FP32=32 元素
singleCoreK = K; singleCoreM/N = baseM/N;</code></pre> iterateOrder = ITER_COL_FIRST // 列优先遍历
singleCoreK = K; singleCoreM/N = baseM/N</code></pre>
<p><strong>为什么 baseM=baseN=256</strong>L0C=256KBFP32 累加 <code>256×256×4B = 256KB</code> 恰好填满 L0C。baseM=baseN=256 是&quot;L0C 能容纳的最大方块&quot;,也是重复读最少的配置(<code>重复读 ∝ 1/baseM + 1/baseN</code>,见 §3.2 的 <code>cubeBoundParam</code>。950PR 的 L0C 比上代128KB大一倍所以 baseM 从 128 升到 256。</p>
<p><strong>为什么 baseK = 128B/dtype</strong>GM→L1 高效搬移要求单次 dValue ≥ 128B连续列数据量。K 方向对齐 128B 保证 MTE2 搬移效率。</p>
<h3>3.2 GetRebalanceBlockbaseM/baseN 最优搜索,核心)</h3> <h3>3.2 GetRebalanceBlockbaseM/baseN 最优搜索,核心)</h3>
<p>这是整个 tiling 最关键的函数,分两步:</p> <p>分两步:</p>
<p><strong>① Roofline 判 CubeBound</strong></p> <p><strong>① Roofline 判 CubeBound</strong><code>cubeBoundEdge</code> 计算)</p>
<pre><code>hbmBW = freq · 32核 · 31B/拍 / 1024 // ≈ 1.6TB/s <pre><code>hbmBW = freq · 32核 · 31B/拍 / 1024 ≈ 1.6 TB/s
l2BW = freq · 32核 · 100B/拍 / 1024 // ≈ 5.2TB/s l2BW = freq · 32核 · 100B/拍 / 1024 ≈ 5.2 TB/s
singleCoreComputePower = freq · 8 // ≈ 13.2 TFLOPS(单核 BF16) singleCoreComputePower = freq · 8 ≈ 13.2 TFLOPS单核 BF16
computePower = singleCoreComputePower · aicNum computePower = singleCoreComputePower · aicNum
cmr = (M+N)/(M·N) // 临界算术强度相关量 cmr = (M+N) / (M·N) // 临界算术强度相关量1/元素)
cubeBoundEdge = (l2BW/computePower) + l2CacheUsage·(1 - l2BW/hbmBW)·cmr l2CacheUsage = max(batch·(M+N)·K·dtypeA / L2, 1) // L2 占用倍率(无量纲)
cubeBoundEdge = (l2BW/computePower)
+ l2CacheUsage·(1 - l2BW/hbmBW)·cmr
- (1 + l2BW/hbmBW)/K - (1 + l2BW/hbmBW)/K
cubeBoundParam = 1/baseM + 1/baseN cubeBoundParam = 1/baseM + 1/baseN // 重复读因子1/元素)
// Cube Bound 条件cubeBoundParam &lt;= cubeBoundEdge</code></pre> // Cube Bound 条件cubeBoundParam &lt;= cubeBoundEdge</code></pre>
<p><strong>② 搜索最优 (baseM, baseN)</strong>:在 <code>maxBaseM × maxBaseN</code> 解空间内双重循环,对每个候选算:</p> <p>物理含义:<code>cubeBoundParam</code> 是当前 tiling 的 MTE2 重复搬移强度,<code>cubeBoundEdge</code>&quot;恰好 Cube Bound&quot;的临界强度。<code>cubeBoundParam ≤ cubeBoundEdge</code> ⇔ MTE2 不成为瓶颈。这与官方 <code>matmul_performance.md</code> 的 MTE2 Bound 条件 <code>BW_mte2 ≥ (1/baseN + 1/baseM)·dtype·16·16·16·核数·频率</code> 等价。</p>
<ul> <p><strong>② 搜索最优 (baseM, baseN)</strong>:在 <code>maxBaseM × maxBaseN</code> 解空间双重循环,每个候选算:</p>
<li><code>curCubeBoundParam = 1/curBaseM + 1/curBaseN</code></li> <pre><code>curCubeBoundParam = 1/curBaseM + 1/curBaseN
<li><code>curBalanceRate</code>(尾轮负载均衡率,<code>GetBalanceRateWithTail</code></li> curBalanceRate = GetBalanceRateWithTail(...) // 尾轮负载均衡率(无量纲 0~1
<li>目标:优先满足 CubeBound 且 balanceRate 更高;否则综合 <code>cubeBoundParam/balanceRate</code> 评选。</li> 目标:优先满足 CubeBound 且 balanceRate 更高;
</ul> 否则综合 (curCubeBoundParam/curBalanceRate) 评选(值小者优)</code></pre>
<p><code>maxBaseM/maxBaseN</code><code>GetMaxBaseWithLimit</code> 计算,受 L0A/L0C/L1/bias table/K 对齐多重约束。</p> <p><code>maxBaseM/maxBaseN</code><code>GetMaxBaseWithLimit</code> 计算,受 L0A/L0C/L1/bias table/K 对齐多重约束。搜索步长 <code>baseMAlignUnit/baseNAlignUnit</code> 与转置、fixp bound 相关16 / 32 / 64 / 128B 对齐)。</p>
<h3>3.3 GetBaseK</h3> <h3>3.3 GetBaseKbaseK 约束)</h3>
<pre><code>maxBaseK = L0A_SIZE / DB_SIZE / dtypeSize / max(baseM, baseN) <pre><code>maxBaseK = L0A_SIZE/DB_SIZE/dtypeA / max(baseM, baseN) // 受 L0A 容量约束(元素个数)
// 优先 K 全载进 L0A否则按 256B 对齐;再退 128/64/32/16</code></pre> // K 全载进 L0A 则取 K 对齐值;否则按 256B 对齐;再退候选 {128,64,32,16}</code></pre>
<h3>3.4 CalL1TilingK 方向 L1 分片<code>CalL1TilingDefault</code></h3> <h3>3.4 CalL1TilingK 方向 L1 分片)</h3>
<pre><code>isKInner = !ATrans || BTrans <pre><code>isKInner = !ATrans || BTrans // K 是否内轴
maxStepK = min(ceil(K/baseK), 8) // K 方向 L1 分片数上限 8 maxStepK = min(K/baseK, 8) // K 方向 L1 分片数上限 8
// 遍历 stepK:满足 (aL1+bL1)·DB &lt;= L1 且 K 256B 对齐 且 单次搬移量约束 遍历 stepK1..maxStepK:
stepKa = stepKb = resKL1 / baseK 满足 (aL1+bL1)·DB &lt;= L1 且 K 256B 对齐 且 单次搬移量约束
depthA1 = stepKa · DB; depthB1 = stepKb · DB</code></pre> stepKa = stepKb = resKL1 / baseK // K 方向 L1 分片步数(无量纲)
depthA1 = stepKa·DB; depthB1 = stepKb·DB // L1 流水深度tile 层数)
singleCoreM = baseM; singleCoreN = baseN</code></pre>
<h3>3.5 CalcTailBasicBlock尾轮重切</h3> <h3>3.5 CalcTailBasicBlock尾轮重切</h3>
<pre><code>tailCnt = (mCnt·nCnt &gt; aicNum) ? (mCnt·nCnt % aicNum) : 0 <pre><code>tailCnt = (mCnt·nCnt &gt; aicNum) ? (mCnt·nCnt % aicNum) : 0
// 尾轮把 base 块在 M/N 方向重切 mTailCnt×nTailCnt 份, // 尾轮把 base 块在 M/N 方向重切 mTailCnt×nTailCnt 份,
// 使尾轮也尽量填满核,且保持搬移效率128B 对齐约束)</code></pre> // 使尾轮也尽量填满核,且保持 128B 搬移对齐
while ((mTailCnt+1)·nTailCnt·tailCnt &lt;= aicNum ∧ 搬移对齐) mTailCnt++</code></pre>
<hr/> <hr/>
<h2>4. Swizzle 编排SWAT 窗口 + 蛇形扫描)</h2> <h2>4. Swizzle 编排SWAT 窗口 + 蛇形扫描)</h2>
<p>ASWT 与 StreamK 共用的 swizzle 核心(<code>block_scheduler_aswt.h</code><code>UpdateMNTileIdx</code><code>mat_mul_asw_block.h</code><code>UpdateBasicIndex</code></p> </blockquote>
<pre><code>mainWindow = min(4, mTileNum) // 固定窗口 4 行WINDOW_LEN=4 <h3>4.1 ASWT 的 SWAT 蛇形滑动(<code>UpdateMNTileIdx</code></h3>
mainRow = mTileNum / mainWindow - 1 <pre><code>mainWindow = min(4, mCnt) // 固定窗口 4 个 base 块WINDOW_LEN=4
tailWindow = mTileNum - mainRow · mainWindow mainRow = mCnt / mainWindow - 1
tailWindow = mCnt - mainRow·mainWindow
rowIdx = tileIdx / nTileNum / mainWindow rowIdx = tileIdx / nCnt / mainWindow
if (rowIdx &lt; mainRow): if (rowIdx &lt; mainRow): // 主窗口区
mTileIdx = rowIdx·mainWindow + tileIdx % mainWindow // 窗口内 M 小步滑动 mTileIdx = rowIdx·mainWindow + tileIdx % mainWindow // 窗口内 M 小步滑动
nTileIdx = (tileIdx / mainWindow) % nTileNum // N 方向连续滑动 nTileIdx = (tileIdx / mainWindow) % nCnt // N 方向连续滑动
else: else: // 尾窗口区M 剩余不足一窗)
// 尾窗口特殊处理 mTileIdx = mainRow·mainWindow + tailIndex % tailWindow
if (rowIdx % 2 != 0): nTileIdx = (tailIndex / tailWindow) % nCnt
nTileIdx = nTileNum - 1 - nTileIdx // 蛇形:奇数行 N 反向</code></pre> if (rowIdx % 2 != 0): // 蛇形:奇数行 N 反向扫描
<p><strong>SWAT 语义</strong>:把 M 轴按窗口(默认 4 个 base 块)分组,窗口内沿 N 连续滑动、M 小步滑动,使相邻核访问的数据在空间上邻近 → 最大化 L2 命中;奇数行 N 反向扫描(蛇形)让相邻轮的首尾块空间相邻,进一步提升 L2 复用。这是官方 <code>matmul_performance.md</code> 中 SWATSlide Window Adaptive Template的落地实现。</p> nTileIdx = nCnt - 1 - nTileIdx</code></pre>
<p><strong>尾轮重切</strong><code>GetBlockShape</code>):最后一轮把单个 base 块在 M/N 方向再切 <code>mTailCnt×nTailCnt</code> 份分给更多核,<code>blockIdx % tailCnt</code> 决定每核拿哪个子块,消除尾轮算力浪费。</p> <p><strong>物理含义(结合硬件)</strong>:把 M 轴按窗口4 个 base 块)分组,窗口内沿 N 连续滑动、M 小步滑动,使<strong>相邻核访问的数据在 GM 地址上空间邻近</strong> → 最大化 L2 命中(重复读走 5.2TB/s 的 L2 而非 1.6TB/s 的 GM。奇数行 N 反向(蛇形)让相邻两轮的首尾块空间相邻,进一步复用 L2。示意图</p>
<p><strong>StreamK 的 swizzle</strong><code>block_scheduler_streamk.h</code>):在 SWAT 窗口基础上叠加 <strong>DP 主轮K 不分片)+ SK 尾轮K 分片)</strong> 判定(<code>CheckIsSkScene</code>),主轮每核一个 MN 块算完整 K尾轮把剩余 MN 块切 K 分多核、由 AIV 在 workspace 上确定性累加。</p> <pre><code>M方向 base 块(窗口=4
┌──────────────────────────────────────────┐
│ 窗口0: 核0→N0, 核1→N1, 核2→N2, 核3→N3 (N 连续) │
│ 窗口1: 核4→N3, 核5→N2, 核6→N1, 核7→N0 (蛇形反向) │
│ ... │
└──────────────────────────────────────────┘
(每行窗口内 N 方向连续滑动 → 相邻核读相邻 A 块 → L2 命中)</code></pre>
<h3>4.2 尾轮重切(<code>GetBlockShape</code></h3>
<p>最后一轮把单个 base 块在 M/N 方向再切 <code>mTailCnt × nTailCnt</code> 份分给更多核,<code>blockIdx % tailCnt</code> 决定每核拿哪个子块,消除尾轮&quot;核数远大于剩余块数&quot;的算力浪费(配合 §3.5)。</p>
<h3>4.3 StreamK 的 DP/SK 编排(<code>block_scheduler_streamk.h</code></h3>
<p>在 SWAT 蛇形基础上叠加 <strong>DP 主轮 + SK 尾轮</strong> 判定:</p>
<pre><code>tileNum = (mCnt·nCnt - tailMNTileNum) + tailMNTileNum·skKTileNum·batch
└──────── DP 部分(每核一个MN块) ┘ └───── SK 尾轮(每MN块切K) ─────┘
CheckIsSkScene(tileIdx) = (⌈(tileIdx+1)/usedCoreNum⌉ == ⌈tileNum/usedCoreNum⌉)
// DP 轮kTileNum=1K 不分片SK 轮kTileNum=skKTileNumK 分片)</code></pre>
<p>DP 主轮每核一个 MN 块算完整 K数据并行SK 尾轮把剩余 MN 块切 K 分给空闲核提前执行AIV 累加与 AIC 下一轮重叠)。</p>
<hr/> <hr/>
<h2>5. 各分支优缺点对比</h2> <h2>5. 各分支优缺点对比</h2>
<table><thead><tr><th>分支</th><th>适用场景</th><th>优点</th><th>缺点 / 局限</th></tr></thead><tbody> <table><thead><tr><th>分支</th><th>适用场景</th><th>优点</th><th>缺点 / 局限</th></tr></thead><tbody>
<tr><td>K_EQUAL_ZERO</td><td>K==0 且无 bias</td><td>极简AIV 清零不浪费 Cube</td><td>仅空矩阵乘</td></tr> <tr><td>K_EQUAL_ZERO</td><td>K==0 且无 bias</td><td>极简AIV 清零不浪费 Cube</td><td>仅空矩阵乘</td></tr>
<tr><td>TO_MUL</td><td>高精度 FP32 且 M==1N==1</td><td>避开 Cube 分形浪费AIV 向量乘</td><td>仅 FP32 高精度模式</td></tr> <tr><td>TO_MUL</td><td>高精度 FP32 且 M==1/N==1</td><td>避开 Cube 分形浪费AIV 向量乘</td><td>仅 FP32 高精度</td></tr>
<tr><td>TO_MULTI_MUL</td><td>高精度 FP32 且 !ATrans∧BTrans 且 M/N 任意</td><td>同上 + 转置组合下的内积连续</td><td>更窄(限转置组合、无 bias</td></tr> <tr><td>TO_MULTI_MUL</td><td>高精度 FP32 且 !ATrans∧BTrans</td><td>同上 + 内积方向连续</td><td>更窄(限转置、无 bias</td></tr>
<tr><td>BASIC_STREAM_K</td><td>M/N 小、K 大(K≥8192</td><td>切 K 满核DPSK 尾轮与计算重叠</td><td>workspace 额外带宽;累加顺序不确定;仅 A 为 NDDPSK 要求 256 对齐</td></tr> <tr><td>BASIC_STREAM_K</td><td>M/N 小、K 大≥8192</td><td>切 K 满核DPSK 尾轮与计算重叠</td><td>workspace 额外带宽;累加顺序不确定;仅 A 为 NDDPSK 要求 256 对齐</td></tr>
<tr><td>BASIC_ASWT</td><td>通用主分支</td><td>SWAT 提 L2 命中A/B 全载减重复读fixpipe 并行搬出;尾轮均衡</td><td>baseM/N 启发式搜索非严格最优;全载/fixpipe 场景窄</td></tr> <tr><td>BASIC_ASWT</td><td>通用主分支</td><td>SWAT 提 L2 命中A/B 全载减重复读fixpipe 并行搬出;尾轮均衡</td><td>baseM/N 启发式最优;全载/fixpipe 场景窄GM 带宽不分读写</td></tr>
<tr><td>BASE(ASW)</td><td>最终兜底</td><td>保证正确性</td><td>老接口,无全载/fixpipe/StreamK 优化</td></tr> <tr><td>BASE(ASW)</td><td>最终兜底</td><td>保证正确性</td><td>老接口,无全载/fixpipe/StreamK 优化</td></tr>
</tbody></table> </tbody></table>
<hr/> <hr/>
<h2>6. 初步观察到的可改进点(衔接任务 3.2</h2> <h2>6. 初步观察到的可改进点(衔接任务 3.2</h2>
<p>在通读源码过程中,已浮现若干值得深挖的改进线索,留待后续对照性能模型严格论证:</p> <p>在通读源码过程中浮现的改进线索,留待后续性能模型严格论证:</p>
<ol> <ol>
<li><strong>baseM/baseN 搜索目标是启发式的</strong><code>GetRebalanceBlock</code><code>cubeBoundParam/balanceRate</code> 复合指标剪枝,而非直接代入 §3.2 的 <code>T_total = max(T_MMAD, T_MTE2, T_MTE1, T_FIXPIPE)</code> 精确评估。理论上可用性能模型对候选解精确打分。</li> <li><strong>baseM/baseN 搜索是启发式的</strong><code>GetRebalanceBlock</code><code>cubeBoundParam/balanceRate</code> 复合指标剪枝,而非直接代入 <code>T_total = max(T_MMAD, T_MTE2, T_MTE1, T_FIXPIPE)</code> 精确评估。理论上可用性能模型对候选解精确打分。</li>
<li><strong>SWAT 窗口固定为 4</strong><code>WINDOW_LEN=4</code>):未根据 L2 容量、shape、核数自适应调窗。窗口大小直接影响 L2 命中率与重复读量的权衡。</li> <li><strong>SWAT 窗口固定为 4</strong><code>WINDOW_LEN=4</code>):未根据 L2 容量、shape、核数自适应调窗。窗口大小直接影响 L2 命中率与重复读量的权衡。</li>
<li><strong>A/B 全载只有&quot;整个矩阵常驻&quot;一种粒度</strong>:没有&quot;部分驻留&quot;(多个 base 块驻留 L1 的中间态),A/B 稍大于 3/4 L1 时直接放弃全载,存在优化断档。</li> <li><strong>A/B 全载只有&quot;整个矩阵常驻&quot;一种粒度</strong>:没有&quot;部分驻留&quot;(多个 base 块驻留 L1 的中间态A/B 稍大于 3/4 L1 时直接放弃,存在优化断档。</li>
<li><strong>fixpipe 优化覆盖窄</strong>:仅 <code>K≤256 ∧ M≥256 ∧ N 不对齐</code> 场景触发,其它 Fixpipe Bound 场景(如更小 K未覆盖。</li> <li><strong>fixpipe 优化覆盖窄</strong>:仅 <code>K≤256 ∧ M≥256 ∧ N 不对齐</code> 触发,其它 Fixpipe Bound 场景未覆盖。</li>
<li><strong>GM 带宽未区分读写</strong><code>GetHbmBW</code> 统一按 <code>32核·31B/拍</code> 换算,未区分读/写共享 1.6TB/s 的竞争,可能高估有效带宽。</li> <li><strong>GM 带宽未区分读写</strong><code>GetHbmBW</code> 统一按 <code>32核·31B/拍</code> 换算,未区分读/写共享 1.6TB/s 的竞争,可能高估有效带宽。</li>
<li><strong>FP32 高精度isForceGrpAccForFp32的退化分支覆盖不全</strong>M==1/N==1 的 FP16/BF16 场景无对应 AIV 退化分支,仍走 Cube</li> <li><strong>M==1/N==1 的 FP16/BF16 无退化分支</strong>TO_MUL/TO_MULTI_MUL 仅覆盖 FP32 高精度FP16/BF16 的退化场景仍走 Cube1/16 利用率)</li>
</ol> </ol>
<hr/> <hr/>
<h2>附录:关键源码文件索引</h2> <h2>附录:关键源码文件索引</h2>
@@ -257,6 +394,7 @@ if (rowIdx % 2 != 0):
<tr><td>Host</td><td><code>op_host/op_tiling/arch35/matmul_tiling_registry.h</code></td><td>策略注册与 DoTilingImpl 调度</td></tr> <tr><td>Host</td><td><code>op_host/op_tiling/arch35/matmul_tiling_registry.h</code></td><td>策略注册与 DoTilingImpl 调度</td></tr>
<tr><td>Host</td><td><code>op_host/op_tiling/arch35/matmul_v3_tiling_advanced.cpp</code></td><td>主入口 + 各 Phase</td></tr> <tr><td>Host</td><td><code>op_host/op_tiling/arch35/matmul_v3_tiling_advanced.cpp</code></td><td>主入口 + 各 Phase</td></tr>
<tr><td>Host</td><td><code>op_host/op_tiling/arch35/matmul_v3_tiling_helper.cpp</code></td><td>ResetBase/GetRebalanceBlock/CalL1Tiling/GetL0C2Out</td></tr> <tr><td>Host</td><td><code>op_host/op_tiling/arch35/matmul_v3_tiling_helper.cpp</code></td><td>ResetBase/GetRebalanceBlock/CalL1Tiling/GetL0C2Out</td></tr>
<tr><td>Host</td><td><code>op_host/op_tiling/arch35/matmul_v3_common_advanced.h</code></td><td>常量与数据结构定义</td></tr>
<tr><td>Host</td><td><code>op_host/op_tiling/arch35/matmul_v3_basic_streamk_tiling.cpp</code></td><td>StreamK/DPSK 条件与 tiling</td></tr> <tr><td>Host</td><td><code>op_host/op_tiling/arch35/matmul_v3_basic_streamk_tiling.cpp</code></td><td>StreamK/DPSK 条件与 tiling</td></tr>
<tr><td>Host</td><td><code>op_host/op_tiling/arch35/matmul_v3_basic_aswt_tiling.cpp</code></td><td>ASWT 子分支(全载/fixpipe</td></tr> <tr><td>Host</td><td><code>op_host/op_tiling/arch35/matmul_v3_basic_aswt_tiling.cpp</code></td><td>ASWT 子分支(全载/fixpipe</td></tr>
<tr><td>Host</td><td><code>op_host/op_tiling/arch35/matmul_v3_{k_equal_zero,to_mul,to_multi_mul,asw}_tiling.cpp</code></td><td>其余分支</td></tr> <tr><td>Host</td><td><code>op_host/op_tiling/arch35/matmul_v3_{k_equal_zero,to_mul,to_multi_mul,asw}_tiling.cpp</code></td><td>其余分支</td></tr>