移动 BatchMatMulV3算子分支实现分析.html 到 BMM/ 目录
This commit is contained in:
923
BMM/BatchMatMulV3算子分支实现分析.html
Normal file
923
BMM/BatchMatMulV3算子分支实现分析.html
Normal file
@@ -0,0 +1,923 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>BatchMatMulV3 (BMM v3) 算子分支实现深度分析 —— 昇腾 950 / DAV_3510</title>
|
||||
<style>
|
||||
:root{
|
||||
--bg:#0f1419; --panel:#1a2230; --ink:#1f2933; --muted:#5f6b7a;
|
||||
--accent:#0b6bcb; --accent2:#0e9f6e; --warn:#b45309; --line:#d9e2ec;
|
||||
--code-bg:#f4f6f9; --hl:#fff7e6;
|
||||
}
|
||||
*{box-sizing:border-box}
|
||||
body{font-family:"PingFang SC","Microsoft YaHei","Helvetica Neue",Arial,sans-serif;
|
||||
color:var(--ink);background:#eef2f6;margin:0;line-height:1.75}
|
||||
.page{max-width:1180px;margin:0 auto;padding:32px 40px 80px;background:#fff;box-shadow:0 0 24px rgba(0,0,0,.06)}
|
||||
h1{font-size:28px;border-bottom:3px solid var(--accent);padding-bottom:12px;margin-top:8px}
|
||||
h2{font-size:22px;margin-top:48px;border-left:6px solid var(--accent);padding-left:12px;color:#0b3d73}
|
||||
h3{font-size:18px;margin-top:32px;color:#0b3d73;border-bottom:1px dashed var(--line);padding-bottom:6px}
|
||||
h4{font-size:16px;margin-top:20px;color:#123}
|
||||
table{border-collapse:collapse;width:100%;margin:14px 0;font-size:14px}
|
||||
th,td{border:1px solid var(--line);padding:7px 10px;text-align:left;vertical-align:top}
|
||||
th{background:#eaf2fb;color:#0b3d73;white-space:nowrap}
|
||||
tr:nth-child(even) td{background:#f8fafc}
|
||||
code,pre{font-family:"JetBrains Mono",Consolas,Menlo,monospace;font-size:13px}
|
||||
code{background:var(--code-bg);padding:1px 5px;border-radius:4px;color:#9d2c5e}
|
||||
pre{background:var(--code-bg);border:1px solid var(--line);border-radius:8px;padding:14px;overflow-x:auto;line-height:1.55}
|
||||
pre code{background:none;color:#243447;padding:0}
|
||||
.toc{background:#f6f9fc;border:1px solid var(--line);border-radius:10px;padding:18px 26px;font-size:14px}
|
||||
.toc a{color:var(--accent);text-decoration:none}
|
||||
.toc a:hover{text-decoration:underline}
|
||||
.toc ol{margin:6px 0}
|
||||
.note{background:#eafaf3;border-left:5px solid var(--accent2);padding:10px 16px;border-radius:0 8px 8px 0;margin:14px 0}
|
||||
.warn{background:#fdf3e7;border-left:5px solid var(--warn);padding:10px 16px;border-radius:0 8px 8px 0;margin:14px 0}
|
||||
.crit{background:#fdeeee;border-left:5px solid #c0392b;padding:10px 16px;border-radius:0 8px 8px 0;margin:14px 0}
|
||||
.src{color:var(--muted);font-size:12.5px}
|
||||
.tag{display:inline-block;background:#e3ecf7;color:#0b3d73;border-radius:4px;padding:0 7px;font-size:12px;margin-right:4px;font-family:Consolas,monospace}
|
||||
.tag.aiv{background:#e6f7ee;color:#0e6e4c}
|
||||
.tag.mix{background:#f3e8fd;color:#6d28a8}
|
||||
.meta{color:var(--muted);font-size:13px;margin-bottom:24px}
|
||||
.branch{border:1px solid var(--line);border-radius:10px;padding:4px 22px 16px;margin:22px 0;background:#fcfdfe}
|
||||
.branch h3{border-bottom:none}
|
||||
ul.tight li,ol.tight li{margin:3px 0}
|
||||
.figbox{border:1px solid var(--line);border-radius:10px;padding:14px;margin:16px 0;background:#fbfcfe;text-align:center}
|
||||
.figbox svg{max-width:100%;height:auto}
|
||||
.figcap{font-size:12.5px;color:var(--muted);margin-top:8px;text-align:center}
|
||||
|
||||
.flow{font-family:Consolas,monospace;font-size:13px;background:#f8fafc;border:1px dashed #b9c6d3;border-radius:8px;padding:14px;white-space:pre;overflow-x:auto}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="page">
|
||||
|
||||
<h1>BatchMatMulV3(BMM v3)算子分支实现深度分析</h1>
|
||||
<div class="meta">
|
||||
分析对象:<code>ops-nn/matmul/batch_mat_mul_v3</code>(CANN 开源算子仓,GitCode cann/ops-nn) |
|
||||
目标平台:昇腾 950PR(NPU 架构版本 351x,<code>NpuArch::DAV_3510</code>) |
|
||||
资料来源:昇腾NPU知识库(950 架构白皮书、CANN 9.0.0 AscendC 文档)+ 算子源码逐文件分析 | 日期:2026-08-20
|
||||
</div>
|
||||
|
||||
<div class="toc">
|
||||
<b>目录</b>
|
||||
<ol>
|
||||
<li><a href="#sec1">算子概览与代码地图</a></li>
|
||||
<li><a href="#sec2">硬件基础:昇腾 950PR 微架构规格与 tiling 设计约束</a></li>
|
||||
<li><a href="#sec3">Tiling 总体框架:入口、平台分流与分支短路遍历</a></li>
|
||||
<li><a href="#sec4">为什么是这些分支:从 case 空间到分支全集的系统性论证</a></li>
|
||||
<li><a href="#sec5">逐分支详解(进入条件 / 设计动机 / tiling 方法 / kernel 实现)</a>
|
||||
<ol>
|
||||
<li><a href="#b0">K_EQUAL_ZERO:K=0 清零(AIV)</a></li>
|
||||
<li><a href="#b1">TO_MUL:K=1 退化为向量乘(AIV)</a></li>
|
||||
<li><a href="#b2">STREAM_K:超大 K + 并行度不足时切 K</a></li>
|
||||
<li><a href="#b3">MERGE_BATCH_BASICAPI:小 M/N 大 batch 合并进 L0</a></li>
|
||||
<li><a href="#b4">ITER_BATCH_BROADCAST_BASICAPI:单边 batch 广播</a></li>
|
||||
<li><a href="#b5">ITER_BATCH_BASICAPI:普通大 batch(基础 API)</a></li>
|
||||
<li><a href="#b6">ITER_BATCH:大 batch(高阶 API / 自定义搬移)</a></li>
|
||||
<li><a href="#b7">AL1 / BL1_FULL_LOAD_BASIC:单边无 batch 的 L1 全载</a></li>
|
||||
<li><a href="#b8">ASW_BASIC 与 BASE:通用兜底 + 自适应滑窗</a></li>
|
||||
</ol>
|
||||
</li>
|
||||
<li><a href="#sec6">Swizzle 专题:核间分块执行顺序的三种实现机制</a></li>
|
||||
<li><a href="#sec7">tiling_key 编码:7 位段如何锁定一条 kernel 路径</a></li>
|
||||
<li><a href="#sec8">分支完备性论证与批判性讨论</a></li>
|
||||
<li><a href="#sec9">附:非 arch35 老路径分支(910B 等平台)简述</a></li>
|
||||
<li><a href="#sec10">参考来源清单</a></li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<!-- ============================== 1 ============================== -->
|
||||
<h2 id="sec1">1. 算子概览与代码地图</h2>
|
||||
<p>BatchMatMulV3 是 CANN ops-nn 仓中批量矩阵乘的主力算子,承载 <code>aclnnBatchMatMul / aclnnBaddbmm / aclnnAddbmm / aclnnEinsum / aclnnBatchMatMulWeightNz</code> 等全部 BMM 类 API。语义为 <code>C[b, m, n] = A[b, m, k] × B[b, k, n]</code>(+ 可选 bias),batch 维最多 4 级(总维度 ≤ 6),A/B/C 的 batch 维支持广播。</p>
|
||||
|
||||
<table>
|
||||
<tr><th>目录</th><th>内容</th><th>关键文件</th></tr>
|
||||
<tr><td><code>op_host/op_tiling/</code></td><td>Host 侧 tiling:分支选择、tiling 参数计算</td><td><code>batch_mat_mul_v3_tiling.cpp</code>(入口)、<code>batch_mat_mul_v3_base_tiling.cpp</code>(67KB,老路径基类)、<code>arch35/</code>(950 高级 tiling,12 个策略文件)</td></tr>
|
||||
<tr><td><code>op_kernel/</code></td><td>Device 侧 kernel</td><td><code>arch35/batch_mat_mul_v3.cpp</code>(arch35 入口,编译期 if constexpr 分发树)、<code>batch_mat_mul_v3*.h</code>(老架构 kernel)、<code>arch35/batch_mat_mul_v3_*_advanced.h</code>(各策略 kernel/block scheduler)</td></tr>
|
||||
<tr><td><code>op_host/op_api/</code></td><td>aclnn API 层</td><td>aclnnBatchMatMul / Baddbmm / Einsum 等</td></tr>
|
||||
<tr><td>依赖:ops-nn <code>matmul/mat_mul_v3</code>、<code>matmul/common/cmct</code></td><td>公共 tiling 基类、MatmulImpl 高层 API、Cmct/Blaze GEMM 框架</td><td>matmul_v3_tiling_advanced、mat_mul_pingpong_basic、kernel_matmul_iterbatch 等</td></tr>
|
||||
</table>
|
||||
|
||||
<p>BMM v3 的软件实现遵循昇腾算子的标准两层结构:</p>
|
||||
<ul class="tight">
|
||||
<li><b>Host tiling</b>:根据 shape/dtype/format/batch 结构 + 平台规格(aicNum/aivNum、L1/L0A/L0B/L0C/UB/L2 容量),选择一个<b>分支(strategy)</b>并算出分块参数(baseM/baseN/baseK、singleCoreM/N/K、iterBatch、mergeBatchL0、swizzle 窗长等),编码进 <b>tilingKey</b>;</li>
|
||||
<li><b>Device kernel</b>:tilingKey 在编译期实例化出唯一 kernel 模板组合(7 个模板参数),运行时按 tilingData 执行「分块到各 AIC/AIV 核的搬移(GM→L1→L0→Cube→L0C→GM)与计算,以及块到核的映射顺序(swizzle)」。</li>
|
||||
</ul>
|
||||
|
||||
<!-- ============================== 2 ============================== -->
|
||||
<h2 id="sec2">2. 硬件基础:昇腾 950PR 微架构规格与 tiling 设计约束</h2>
|
||||
<p>BMM v3 全部分支的设计动机都可以追溯到 950PR(DAV_3510)的以下硬件事实(来源:昇腾950 NPU 架构白皮书表3-1/表4-2、CANN 9.0.0《NPU架构版本351x》文档):</p>
|
||||
|
||||
<h3>2.1 关键规格表</h3>
|
||||
<table>
|
||||
<tr><th>规格项</th><th>昇腾 950PR</th><th>对 BMM tiling 的意义</th></tr>
|
||||
<tr><td>AI 子系统</td><td>36 个(满配),每个 = 1 Cube Core + 2 Vector Core</td><td rowspan="2">AIC:AIV = 1:2 是 StreamK、fixpipe 1V2 等 MIX 分支的结构性前提(源码多处硬性要求 <code>aivNum == 2*aicNum</code>)</td></tr>
|
||||
<tr><td>AIC / AIV 核数</td><td>32(降配 28)/ 64(降配 56),<b>AIC/AIV 分离架构</b></td></tr>
|
||||
<tr><td>Cube 算力</td><td>BF16/FP16 432 TFLOPS(纯 Cube);一拍完成 FP16 16×16×16</td><td>决定 cubeBound(计算访存比拐点),进而决定 baseM/baseN 寻优</td></tr>
|
||||
<tr><td>Vector 算力</td><td>FP16/BF16 54 TFLOPS;每拍 256B</td><td>K=0/K=1 等低密度计算退到 AIV 更划算的依据</td></tr>
|
||||
<tr><td>L1 Buffer</td><td><b>512KB</b> / AIC</td><td>L1 全载(AL1/BL1 full load)、iterBatchL1(L1 驻留 batch 数)的容量上界</td></tr>
|
||||
<tr><td>L0A / L0B</td><td><b>64KB</b> / AIC(512B 对齐,FRACTAL_NZ/ZN)</td><td>baseM×baseK、baseN×baseK 单块上界;mergeBatchL0 合并 batch 数的上界</td></tr>
|
||||
<tr><td>L0C</td><td><b>256KB</b> / AIC(较上代增大,白皮书明言"提供更灵活的 Tiling 策略")</td><td>fp32 累加:baseM×baseN×4B×DB ≤ 256KB;iterbatch 的 batchOutNum 由它决定</td></tr>
|
||||
<tr><td>UB</td><td><b>512KB</b> / AIV(32B 对齐)</td><td>AIV 分支(TO_MUL)单轮驻留 batch 数 = UB / 单 batch 字节数</td></tr>
|
||||
<tr><td>L2 Cache</td><td><b>128MB</b>(降配 112MB)全局统一</td><td>滑窗/对角错位 swizzle 的收益来源:让并发核访问的 A/B 块在 L2 命中</td></tr>
|
||||
<tr><td>片上内存</td><td>128GB / <b>1.6TB/s</b>(PR 降配 1.4TB/s;DT 为 4TB/s)</td><td>PR 算存比高 → 偏计算受限 → "减少 HBM 搬运"类分支(L1 全载、iterbatch)收益更大</td></tr>
|
||||
<tr><td>数据通路(351x 变化)</td><td>新增 L0C→UB、UB↔L1、AIV UB↔L1 硬通道、SSBuffer 核间通信;<b>删除 GM→L0 直通与 L1→GM</b></td><td>所有数据必须经 L1 中转;CV 协同(AIC 算 + AIV 后处理)有硬化通路支撑</td></tr>
|
||||
<tr><td>Fixpipe</td><td>L0C→GM/UB 随路量化/转置(NZ2ND 等)、ReLU 融合</td><td>tiling_key 的 L0C2OUT_MODEL 位段(ON_THE_FLY / ND_FIXPIPE_1_1 / 1_2)</td></tr>
|
||||
<tr><td>NDDMA</td><td>AI Core 内置 N 维 DMA,≤5 维重排随搬运完成</td><td>TENSOR_LEVEL(张量级 API)分支的硬件基础</td></tr>
|
||||
<tr><td>核间同步</td><td>CrossCoreSetFlag/WaitFlag,支持 AIC:AIV = 1:1 / 1:2</td><td>MIX_AIC_1_2 kernel 类型(StreamK、fixpipe 1V2 epilogue)的同步机制</td></tr>
|
||||
</table>
|
||||
|
||||
<h3>2.2 规格 → 设计约束的映射</h3>
|
||||
<ul class="tight">
|
||||
<li><b>Cube 基本块 16×16×16 + L0 512B 对齐</b> → baseM/baseN 16 对齐、baseK 按内轴 128B/dtype 对齐,是几乎所有分支的默认对齐约束;</li>
|
||||
<li><b>L1=512KB、L0A/B=64KB、L0C=256KB</b> → 「单 batch 的 A+B 能否 DB 驻留 L1」「L0 能否同时放多 batch」是 iterbatch 系分支的定量判据;「A(或B) 全量 ×2(DB) ≤ L1」是 L1 全载分支的定量判据;</li>
|
||||
<li><b>AIC 只有 32 核</b> → 当 batch×mCnt×nCnt < aicNum/2 时核大量闲置 → 需要 StreamK 沿 K 拆出并行度;当 batch ≫ aicNum 时则反过来把 batch 当主并行维度(iterbatch/mergebatch);</li>
|
||||
<li><b>AIC/AIV 分离、Vector 每拍 256B</b> → K∈{0,1} 时 Cube 阵列无累加深度可用,AIV 逐元素乘/清零吞吐更高 → 两个 AIV_ONLY 分支;</li>
|
||||
<li><b>L2 128MB 全局共享</b> → 块到核的遍历顺序(swizzle)直接影响 L2 命中率 → ASW 滑窗 + 蛇形、对角错位分核等调度优化。</li>
|
||||
</ul>
|
||||
<div class="note"><b>950PR vs 950DT 的差异动机</b>:PR 片上内存带宽 1.6TB/s 显著低于 DT 的 4TB/s,而 BF16 算力接近(486 vs 547 TFLOPS),即 <b>PR 算存比更高、更偏计算受限</b>(其定位即 LLM Prefill/推荐等吞吐场景)。这解释了为什么 950 的 BMM 分支体系如此重视"数据驻留复用"(L1 全载、iterbatch、mergebatch)——在 PR 上减少一字节 HBM 搬运比在 DT 上更值钱。</div>
|
||||
|
||||
<!-- ============================== 3 ============================== -->
|
||||
<h2 id="sec3">3. Tiling 总体框架:入口、平台分流与分支短路遍历</h2>
|
||||
|
||||
<h3>3.1 入口与平台分流</h3>
|
||||
<pre><code>// op_host/op_tiling/batch_mat_mul_v3_tiling.cpp
|
||||
static ge::graphStatus BatchMatMulV3TilingFunc(gert::TilingContext* context) {
|
||||
if (IsAdvancedSocVersion(context)) { // DAV_3510(950) / DAV_RESV(s8s4保留平台)
|
||||
return batch_matmul_v3_advanced::BatchMatMulV3Tiling(context).DoTiling(); // arch35 高级 tiling
|
||||
}
|
||||
return TilingRegistry::GetInstance().DoTilingImpl(context); // 老路径 BatchMatmulV3BaseTiling
|
||||
}
|
||||
IMPL_OP_OPTILING(BatchMatMulV3)
|
||||
.Tiling(BatchMatMulV3TilingFunc)
|
||||
.TilingParse<MatmulV3CompileInfo>(TilingPrepareForBatchMatMulV3) // 提取平台规格
|
||||
.GenSimplifiedKey(GenSimplifiedKey);</code></pre>
|
||||
<p><code>TilingPrepareForBatchMatMulV3</code> 在编译期从平台信息提取 <code>MatmulV3CompileInfo</code>:aicNum/aivNum、L1/L0A/L0B/L0C/L2/UB 容量、<code>supportL0c2out</code>(fixpipe)、<code>supportL12BtBf16</code>、btSize(1024/4096)、npuArch——<b>所有分支的定量条件都建立在这组平台参数之上</b>,同一套代码因此能适配 950 满配/降配及其他 351x 产品。</p>
|
||||
|
||||
<h3>3.2 arch35 主调用链</h3>
|
||||
<div class="flow">BatchMatMulV3TilingFunc
|
||||
└─ BatchMatMulV3Tiling::DoTiling() [batch_matmul_v3_tiling_advanced.*, 继承 MatMulV3Tiling]
|
||||
├─ GetShapeAttrsInfo / CheckArgs / GetArgs / Validate* (M/N/K、转置、dtype、format 校验)
|
||||
├─ ExtractMatrixBatchInfo() 提取 4 级 batch:batchA0~A3 / B0~B3 / C0~C3
|
||||
├─ ValidateMatrixBatchInfo() 广播规则校验 + ★MergeBatchAndMAxis():
|
||||
│ 当 batchB==1 且 A 不转置 → mValue = batchA*mValue,batch 折叠进 M 轴(BMM→MM 降维)
|
||||
├─ Extract/ValidateOptionalBatchInfo() (bias batch)
|
||||
└─ MMTilingRegistry::DoTilingImpl(opType="BatchMatMulV3", npuArch, priorities)
|
||||
└─ 按优先级表逐个构造策略类 → DoTiling():
|
||||
GetShapeAttrsInfo → IsCapable() → DoOpTiling() → AdjustOpTiling() → PostTiling()
|
||||
★ IsCapable()==false → GRAPH_PARAM_INVALID → 试下一个分支
|
||||
★ 任一分支 GRAPH_SUCCESS → 立即短路返回</div>
|
||||
|
||||
<h3>3.3 分支优先级表(DAV_3510)</h3>
|
||||
<p>定义于 <code>arch35/batch_matmul_v3_tiling_strategy.h</code>:</p>
|
||||
<table>
|
||||
<tr><th>优先级</th><th>strategy 常量</th><th>策略类</th><th>一句话定位</th><th>核类型</th></tr>
|
||||
<tr><td>0</td><td>BATCH_MATMUL_INPUT_K_EQUAL_ZERO</td><td>BatchMatMulV3KEqZeroTiling</td><td>K=0:输出置零</td><td><span class="tag aiv">AIV_ONLY</span></td></tr>
|
||||
<tr><td>1</td><td>BATCH_MATMUL_TO_MUL</td><td>BatchMatMulV3Matmul2MulTiling</td><td>K=1:退化为逐元素乘</td><td><span class="tag aiv">AIV_ONLY</span></td></tr>
|
||||
<tr><td>2</td><td>BATCH_STREAM_K</td><td>BatchMatMulV3BasicStreamKTiling</td><td>K 巨大且 MN 并行度 < 一半核数:切 K</td><td><span class="tag mix">MIX_AIC_1_2</span></td></tr>
|
||||
<tr><td>3</td><td>MERGE_BATCH_BASICAPI</td><td>BatchMatMulV3MergeBatchBasicApiTiling</td><td>小 M/N、巨大 batch:多 batch 合并进 L0</td><td><span class="tag">AIC_ONLY</span></td></tr>
|
||||
<tr><td>4</td><td>ITER_BATCH_BROADCAST_BASICAPI</td><td>BatchMatMulV3IterbatchBroadcastBasicApiTiling</td><td>单边单轴 batch 广播</td><td><span class="tag">AIC_ONLY</span></td></tr>
|
||||
<tr><td>5</td><td>ITER_BATCH_BASICAPI</td><td>BatchMatMulV3IterBatchBasicApiTiling</td><td>batch 相等且 > 核数:L1/L0 多 batch 流水</td><td><span class="tag">AIC_ONLY</span>/<span class="tag mix">MIX_AIC_1_2</span></td></tr>
|
||||
<tr><td>6</td><td>ITER_BATCH</td><td>BatchMatMulV3IterBatchTiling</td><td>同上但高阶 API(IterateBatch)</td><td><span class="tag">AIC_ONLY</span></td></tr>
|
||||
<tr><td>7</td><td>AL1_FULL_LOAD_BASIC</td><td>BatchMatMulV3AswAL1FullLoadBasicTiling</td><td>A 无 batch 且 M≤256:A 全量常驻 L1</td><td><span class="tag">AIC_ONLY</span></td></tr>
|
||||
<tr><td>8</td><td>BL1_FULL_LOAD_BASIC</td><td>BatchMatMulV3AswBL1FullLoadBasicTiling</td><td>B 无 batch 且 N≤256:B 全量常驻 L1</td><td><span class="tag">AIC_ONLY</span></td></tr>
|
||||
<tr><td>9</td><td>ASW_BASIC</td><td>BatchMatMulV3AswBasicTiling</td><td>通用基础 API + cubeBound 模型寻优</td><td><span class="tag">AIC_ONLY</span></td></tr>
|
||||
<tr><td>999</td><td>BASE</td><td>BatchMatMulV3AswTiling</td><td>最终兜底(高阶/张量级 API),IsCapable 恒 true</td><td><span class="tag">AIC_ONLY</span></td></tr>
|
||||
</table>
|
||||
<p class="src">DAV_RESV(s8s4 保留平台)仅有:ITER_BATCH_BASICAPI → AL1_FULL_LOAD_BASIC → BL1_FULL_LOAD_BASIC → ASW_BASIC → BASE。</p>
|
||||
|
||||
<div class="note"><b>遍历顺序的设计逻辑</b>(短路语义下,顺序 = 特判优先级):
|
||||
<ol class="tight">
|
||||
<li><b>计算模式彻底改变的特判最先</b>(K=0/K=1):它们根本不需要 Cube,若被后面的 cube 模板接住就是纯浪费,必须最先拦截;</li>
|
||||
<li><b>改变并行结构的分支次之</b>(StreamK):它对"核怎么分"做全局性改变(沿 K 拆核 + workspace 归约),要在 batch 优化之前决策;</li>
|
||||
<li><b>batch 维度优化居中</b>:mergebatch(L0 复用最高,条件最苛刻)→ 单边广播(复用次高)→ 普通 iterbatch 基础 API → 高阶 API iterbatch;</li>
|
||||
<li><b>数据驻留优化靠后</b>(AL1/BL1 全载):消除单边重复搬运,是通用路径上的加强;</li>
|
||||
<li><b>通用模板垫底</b>(ASW_BASIC → BASE=999):保证任意合法输入必然 tiling 成功。999 的大数值确保永远最后尝试。</li></ol></div>
|
||||
|
||||
<!-- ============================== 4 ============================== -->
|
||||
<h2 id="sec4">4. 为什么是这些分支:从 case 空间到分支全集的系统性论证</h2>
|
||||
<p>BMM 的 case 空间可由 5 个近似正交的特征维度刻画。<b>每个分支恰好占据其中一个特征区域,且这些区域合起来覆盖了全部合法输入</b>——这就是"为什么是这些分支"的系统回答:</p>
|
||||
<table>
|
||||
<tr><th>特征维度</th><th>取值区域</th><th>微架构矛盾</th><th>对应分支</th></tr>
|
||||
<tr><td rowspan="2">① K 的退化程度</td><td>K=0</td><td>无计算,Cube 阵列完全无用</td><td>K_EQUAL_ZERO(AIV 清零)</td></tr>
|
||||
<tr><td>K=1</td><td>无累加深度,mmad 16×16×16 粒度浪费 15/16</td><td>TO_MUL(AIV 向量乘)</td></tr>
|
||||
<tr><td>② MN×batch 并行度 vs 核数</td><td>batchC×mCnt×nCnt ≤ aicNum/2 且 K 巨大</td><td>32 个 AIC 大半闲置,唯一剩余并行维度是 K</td><td>STREAM_K(切 K + 部分和归约)</td></tr>
|
||||
<tr><td rowspan="3">③ batch 量级与 M/N 大小</td><td>M,N 很小(M≤N)、K≥64、batchC ≥ 4×aicNum</td><td>单 batch 基本块喂不饱 L0(如 16×16),mmad 效率极低</td><td>MERGE_BATCH(多 batch 拼成大 L0 块)</td></tr>
|
||||
<tr><td>batchC > aicNum,M/N/K 正常</td><td>每核要算多个 batch,逐 batch 重复搬 A/B 浪费 MTE</td><td>ITER_BATCH_BASICAPI / ITER_BATCH(L1/L0 一次驻留多 batch 流水)</td></tr>
|
||||
<tr><td>batch ≤ 核数量级,M/N 大</td><td>batch 不构成主并行维度,走 M/N 基本块切分</td><td>ASW_BASIC / BASE</td></tr>
|
||||
<tr><td>④ batch 广播形态</td><td>恰好单边、单轴广播</td><td>被广播算子的单 batch 数据可被对端多 batch 复用</td><td>ITER_BATCH_BROADCAST(广播侧 L1 驻留一份)</td></tr>
|
||||
<tr><td rowspan="2">⑤ 单边无 batch 且该边很小</td><td>batchA=1 且 M≤256(B 大)</td><td>A 全量可常驻 L1,消除全部重复搬运</td><td>AL1_FULL_LOAD</td></tr>
|
||||
<tr><td>batchB=1 且 N≤256(A 大)</td><td>对称(注:A 不转置时已被 MergeBatchAndMAxis 折叠成 MM)</td><td>BL1_FULL_LOAD</td></tr>
|
||||
</table>
|
||||
<p><b>完备性的结构性保证</b>:优先级表尾部是无条件分支(BASE=999 的 IsCapable 恒 true,ASW_BASIC 仅校验 dtype/batch 一致性),因此任何通过 Validate 的输入必然命中某一分支——不存在"所有分支都不收"的 case。而前 10 个分支的关系是<b>"特判 ⊂ 通用"</b>:每个特判分支覆盖的 case 落入通用分支也能跑,只是性能更差;优先级顺序保证收益最大的实现先被选中。这与"性能最优分支集必须是完备分支全集子集"的方法论一致:这里完备全集 = 通用 cube 模板(BASE),特判分支是它在各特征区域上的性能占优替代。</p>
|
||||
<div class="warn"><b>一个容易忽略的降维优化</b>:<code>ValidateMatrixBatchInfo</code> 阶段的 <code>MergeBatchAndMAxis()</code> 在 batchB==1 且 A 不转置时直接把 batchA 折叠进 M 轴(<code>mValue = batchA * mValue</code>,batch 置 1),把 BMM 变成单个大 M 的 MatMul——此时后续所有 batch 类分支都不会命中,case 直接落到 ASW/AL1 全载体系。这说明分支选择不是纯"分诊",前面还有一层<b>问题归约</b>。</div>
|
||||
<!-- ============================== 5 ============================== -->
|
||||
<h2 id="sec5">5. 逐分支详解</h2>
|
||||
<p>本章对每个分支按统一结构展开:<b>目标 case → 进入条件逐条解析(IsCapable,含每个阈值的取值依据)→ tiling 实现步骤(DoOpTiling 编号步骤,含公式)→ 关键参数的"为什么"分析 → kernel 实现</b>。所有条件表达式、常量、注释均直接引用源码(文件:<code>op_host/op_tiling/arch35/batch_matmul_v3_*_tiling.cpp</code> 及 mat_mul_v3 公共实现),微架构依据标注知识库文档篇目;<b>官方文档没有依据、仅为源码经验值的参数会明确标注</b>。</p>
|
||||
|
||||
<h3>5.0 总览:分支决策流程与两条数据通路</h3>
|
||||
<p>arch35 tiling 按优先级 0→9→999 短路遍历,每个分支的 IsCapable 是一组"与"条件。整体决策流如下(条件为简化表述,精确表达式见各分支小节):</p>
|
||||
|
||||
<div class="figbox">
|
||||
<svg viewBox="0 0 980 660" xmlns="http://www.w3.org/2000/svg" font-family="PingFang SC,Microsoft YaHei,sans-serif" font-size="12">
|
||||
<defs>
|
||||
<marker id="ar" markerWidth="8" markerHeight="8" refX="7" refY="3" orient="auto"><path d="M0,0 L7,3 L0,6 Z" fill="#5f6b7a"/></marker>
|
||||
</defs>
|
||||
<style>
|
||||
.dec{fill:#fff7e6;stroke:#d4a017;stroke-width:1.2}
|
||||
.br{fill:#eaf2fb;stroke:#0b6bcb;stroke-width:1.2}
|
||||
.aiv{fill:#e6f7ee;stroke:#0e9f6e;stroke-width:1.2}
|
||||
.mix{fill:#f3e8fd;stroke:#6d28a8;stroke-width:1.2}
|
||||
.no{fill:none;stroke:#5f6b7a;stroke-width:1.1}
|
||||
.yes{fill:none;stroke:#c0392b;stroke-width:1.3}
|
||||
.lb{font-size:11px;fill:#5f6b7a}
|
||||
.lby{font-size:11px;fill:#c0392b}
|
||||
</style>
|
||||
<rect x="20" y="8" width="180" height="30" rx="6" fill="#0b3d73"/><text x="110" y="27" fill="#fff" text-anchor="middle">tiling 输入(shape/dtype/batch)</text>
|
||||
<!-- decision chain -->
|
||||
<polygon class="dec" points="110,58 230,86 110,114 -10,86" transform="translate(60,0)"/><text x="170" y="90" text-anchor="middle">K == 0 ?</text>
|
||||
<polygon class="dec" points="110,58 250,86 110,114 -30,86" transform="translate(60,58)"/><text x="170" y="148" text-anchor="middle">K == 1 且 batch≥128 ?</text>
|
||||
<polygon class="dec" points="110,58 270,86 110,114 -50,86" transform="translate(60,116)"/><text x="170" y="206" text-anchor="middle">b×mCnt×nCnt ≤ aic/2 且 K 巨大 ?</text>
|
||||
<polygon class="dec" points="110,58 285,86 110,114 -65,86" transform="translate(60,174)"/><text x="170" y="264" text-anchor="middle">batch ≥ 4×aic 且 M≤N 小、K≥64 ?</text>
|
||||
<polygon class="dec" points="110,58 265,86 110,114 -45,86" transform="translate(60,232)"/><text x="170" y="322" text-anchor="middle">恰好单边单轴 batch 广播 ?</text>
|
||||
<polygon class="dec" points="110,58 275,86 110,114 -55,86" transform="translate(60,290)"/><text x="170" y="380" text-anchor="middle">batch 相等 > aic 且 L1 可载 ?</text>
|
||||
<polygon class="dec" points="110,58 280,86 110,114 -60,86" transform="translate(60,348)"/><text x="170" y="438" text-anchor="middle">batchA=1 且 M≤256 且 A 可全载 ?</text>
|
||||
<polygon class="dec" points="110,58 280,86 110,114 -60,86" transform="translate(60,406)"/><text x="170" y="496" text-anchor="middle">batchB=1 且 N≤256 且 B 可全载 ?</text>
|
||||
<polygon class="dec" points="110,58 255,86 110,114 -35,86" transform="translate(60,464)"/><text x="170" y="554" text-anchor="middle">batch 相等、AB转置连续态一致 ?</text>
|
||||
<!-- vertical no-chain -->
|
||||
<line class="no" x1="170" y1="38" x2="170" y2="58" marker-end="url(#ar)"/>
|
||||
<line class="no" x1="170" y1="114" x2="170" y2="116" /><line class="no" x1="170" y1="114" x2="170" y2="144" marker-end="url(#ar)"/>
|
||||
<line class="no" x1="170" y1="172" x2="170" y2="202" marker-end="url(#ar)"/>
|
||||
<line class="no" x1="170" y1="230" x2="170" y2="260" marker-end="url(#ar)"/>
|
||||
<line class="no" x1="170" y1="288" x2="170" y2="318" marker-end="url(#ar)"/>
|
||||
<line class="no" x1="170" y1="346" x2="170" y2="376" marker-end="url(#ar)"/>
|
||||
<line class="no" x1="170" y1="404" x2="170" y2="434" marker-end="url(#ar)"/>
|
||||
<line class="no" x1="170" y1="462" x2="170" y2="492" marker-end="url(#ar)"/>
|
||||
<line class="no" x1="170" y1="520" x2="170" y2="550" marker-end="url(#ar)"/>
|
||||
<text class="lb" x="178" y="140">否</text><text class="lb" x="178" y="198">否</text><text class="lb" x="178" y="256">否</text><text class="lb" x="178" y="314">否</text><text class="lb" x="178" y="372">否</text><text class="lb" x="178" y="430">否</text><text class="lb" x="178" y="488">否</text><text class="lb" x="178" y="546">否</text>
|
||||
<!-- branch boxes -->
|
||||
<rect class="br aiv" x="560" y="70" width="400" height="34" rx="6"/><text x="570" y="84"><tspan font-weight="bold">分支0 K_EQUAL_ZERO(AIV)</tspan></text><text x="570" y="98" class="lb">输出=零矩阵,AIV InitOutput 直写 GM</text>
|
||||
<rect class="br aiv" x="560" y="128" width="400" height="34" rx="6"/><text x="570" y="142"><tspan font-weight="bold">分支1 TO_MUL(AIV)</tspan></text><text x="570" y="156" class="lb">K=1 退化向量乘,UB 多 batch pingpong</text>
|
||||
<rect class="br mix" x="560" y="186" width="400" height="34" rx="6"/><text x="570" y="200"><tspan font-weight="bold">分支2 STREAM_K(AIC+AIV 1:2)</tspan></text><text x="570" y="214" class="lb">沿 K 切给空闲核算部分和,workspace 归约</text>
|
||||
<rect class="br" x="560" y="244" width="400" height="34" rx="6"/><text x="570" y="258"><tspan font-weight="bold">分支3 MERGE_BATCH</tspan></text><text x="570" y="272" class="lb">多 batch 合并成大 L0 块,喂饱 cube</text>
|
||||
<rect class="br" x="560" y="302" width="400" height="34" rx="6"/><text x="570" y="316"><tspan font-weight="bold">分支4 ITER_BATCH_BROADCAST</tspan></text><text x="570" y="330" class="lb">广播侧 L1 驻留一份,对端多 batch 流水</text>
|
||||
<rect class="br" x="560" y="360" width="400" height="34" rx="6"/><text x="570" y="374"><tspan font-weight="bold">分支5/6 ITER_BATCH(基础API/高阶API)</tspan></text><text x="570" y="388" class="lb">L1/L0 一次驻留多 batch,IterateBatch 流水</text>
|
||||
<rect class="br" x="560" y="418" width="400" height="34" rx="6"/><text x="570" y="432"><tspan font-weight="bold">分支7 AL1_FULL_LOAD</tspan></text><text x="570" y="446" class="lb">A 全量常驻 L1,整个 kernel 只搬一次</text>
|
||||
<rect class="br" x="560" y="476" width="400" height="34" rx="6"/><text x="570" y="490"><tspan font-weight="bold">分支8 BL1_FULL_LOAD</tspan></text><text x="570" y="504" class="lb">B 全量常驻 L1(与分支7 镜像)</text>
|
||||
<rect class="br" x="560" y="534" width="400" height="34" rx="6"/><text x="570" y="548"><tspan font-weight="bold">分支9 ASW_BASIC</tspan></text><text x="570" y="562" class="lb">cubeBound 模型寻优 baseM/baseN + 滑窗调度</text>
|
||||
<rect class="br" x="330" y="600" width="400" height="34" rx="6"/><text x="340" y="614"><tspan font-weight="bold">分支999 BASE(IsCapable 恒 true,最终兜底)</tspan></text><text x="340" y="628" class="lb">默认 256×256×(128B/dtype) 基块 + CalL1Tiling;TENSOR_LEVEL 时支持任意广播</text>
|
||||
<!-- yes arrows -->
|
||||
<line class="yes" x1="280" y1="86" x2="556" y2="86" marker-end="url(#ar)"/><text class="lby" x="420" y="80">是</text>
|
||||
<line class="yes" x1="310" y1="144" x2="556" y2="144" marker-end="url(#ar)"/><text class="lby" x="420" y="138">是</text>
|
||||
<line class="yes" x1="330" y1="202" x2="556" y2="202" marker-end="url(#ar)"/><text class="lby" x="430" y="196">是</text>
|
||||
<line class="yes" x1="345" y1="260" x2="556" y2="260" marker-end="url(#ar)"/><text class="lby" x="440" y="254">是</text>
|
||||
<line class="yes" x1="325" y1="318" x2="556" y2="318" marker-end="url(#ar)"/><text class="lby" x="430" y="312">是</text>
|
||||
<line class="yes" x1="335" y1="376" x2="556" y2="376" marker-end="url(#ar)"/><text class="lby" x="430" y="370">是</text>
|
||||
<line class="yes" x1="340" y1="434" x2="556" y2="434" marker-end="url(#ar)"/><text class="lby" x="435" y="428">是</text>
|
||||
<line class="yes" x1="340" y1="492" x2="556" y2="492" marker-end="url(#ar)"/><text class="lby" x="435" y="486">是</text>
|
||||
<line class="yes" x1="315" y1="550" x2="556" y2="550" marker-end="url(#ar)"/><text class="lby" x="425" y="544">是</text>
|
||||
<line class="no" x1="170" y1="578" x2="170" y2="617" marker-end="url(#ar)"/><line class="no" x1="170" y1="617" x2="326" y2="617" marker-end="url(#ar)"/><text class="lb" x="178" y="610">否</text>
|
||||
</svg>
|
||||
<div class="figcap">图 5-1 arch35(DAV_3510)分支短路决策流程。菱形为各分支 IsCapable 的核心判据(简化);任一分支命中即返回,不再尝试后续分支</div>
|
||||
</div>
|
||||
|
||||
<p>理解各分支前,先建立 950PR 上的两条数据通路模型——所有分支的本质都是在这两条通路上做不同的驻留/复用/并行安排:</p>
|
||||
|
||||
<div class="figbox">
|
||||
<svg viewBox="0 0 980 300" xmlns="http://www.w3.org/2000/svg" font-family="PingFang SC,Microsoft YaHei,sans-serif" font-size="12">
|
||||
<defs><marker id="ar2" markerWidth="8" markerHeight="8" refX="7" refY="3" orient="auto"><path d="M0,0 L7,3 L0,6 Z" fill="#0b6bcb"/></marker></defs>
|
||||
<!-- Cube lane -->
|
||||
<text x="20" y="24" font-weight="bold" fill="#0b3d73">通路 A:Cube 通路(AIC,分支 2~9/999)</text>
|
||||
<rect x="20" y="40" width="90" height="44" rx="6" fill="#fdeee2" stroke="#b45309"/><text x="65" y="58" text-anchor="middle">GM</text><text x="65" y="74" text-anchor="middle" font-size="10" fill="#5f6b7a">HBM 1.6TB/s</text>
|
||||
<rect x="150" y="40" width="110" height="44" rx="6" fill="#eaf2fb" stroke="#0b6bcb"/><text x="205" y="58" text-anchor="middle">L1 512KB</text><text x="205" y="74" text-anchor="middle" font-size="10" fill="#5f6b7a">MTE2 搬运,DB/4buffer</text>
|
||||
<rect x="300" y="40" width="130" height="44" rx="6" fill="#eaf2fb" stroke="#0b6bcb"/><text x="365" y="52" text-anchor="middle">L0A/L0B 64KB</text><text x="365" y="66" text-anchor="middle" font-size="10" fill="#5f6b7a">MTE1,512B对齐</text><text x="365" y="79" text-anchor="middle" font-size="10" fill="#5f6b7a">fractal 16×16</text>
|
||||
<rect x="470" y="40" width="110" height="44" rx="6" fill="#e3ecf7" stroke="#0b3d73"/><text x="525" y="58" text-anchor="middle">Cube 阵列</text><text x="525" y="74" text-anchor="middle" font-size="10" fill="#5f6b7a">一拍 16×16×16</text>
|
||||
<rect x="620" y="40" width="120" height="44" rx="6" fill="#eaf2fb" stroke="#0b6bcb"/><text x="680" y="58" text-anchor="middle">L0C 256KB</text><text x="680" y="74" text-anchor="middle" font-size="10" fill="#5f6b7a">fp32 累加,可驻留累加</text>
|
||||
<rect x="780" y="40" width="90" height="44" rx="6" fill="#e6f7ee" stroke="#0e9f6e"/><text x="825" y="58" text-anchor="middle">fixpipe</text><text x="825" y="74" text-anchor="middle" font-size="10" fill="#5f6b7a">NZ2ND/量化随路</text>
|
||||
<rect x="890" y="40" width="70" height="44" rx="6" fill="#fdeee2" stroke="#b45309"/><text x="925" y="66" text-anchor="middle">GM</text>
|
||||
<line x1="110" y1="62" x2="146" y2="62" stroke="#0b6bcb" stroke-width="1.5" marker-end="url(#ar2)"/>
|
||||
<line x1="260" y1="62" x2="296" y2="62" stroke="#0b6bcb" stroke-width="1.5" marker-end="url(#ar2)"/>
|
||||
<line x1="430" y1="62" x2="466" y2="62" stroke="#0b6bcb" stroke-width="1.5" marker-end="url(#ar2)"/>
|
||||
<line x1="580" y1="62" x2="616" y2="62" stroke="#0b6bcb" stroke-width="1.5" marker-end="url(#ar2)"/>
|
||||
<line x1="740" y1="62" x2="776" y2="62" stroke="#0b6bcb" stroke-width="1.5" marker-end="url(#ar2)"/>
|
||||
<line x1="870" y1="62" x2="886" y2="62" stroke="#0b6bcb" stroke-width="1.5" marker-end="url(#ar2)"/>
|
||||
<path d="M 680 84 C 680 120, 525 120, 525 88" fill="none" stroke="#0e9f6e" stroke-width="1.3" stroke-dasharray="4 3" marker-end="url(#ar2)"/>
|
||||
<text x="600" y="135" font-size="11" fill="#0e9f6e">L0C 驻留累加(K 循环不回 GM,文档191篇)</text>
|
||||
<text x="110" y="110" font-size="11" fill="#5f6b7a">351x 已删除 GM→L0 直通,所有数据必须经 L1 中转(054篇)</text>
|
||||
<!-- Vector lane -->
|
||||
<text x="20" y="176" font-weight="bold" fill="#0e6e4c">通路 B:Vector 通路(AIV,分支 0/1;老架构 GEMV)</text>
|
||||
<rect x="20" y="192" width="90" height="44" rx="6" fill="#fdeee2" stroke="#b45309"/><text x="65" y="218" text-anchor="middle">GM</text>
|
||||
<rect x="150" y="192" width="130" height="44" rx="6" fill="#e6f7ee" stroke="#0e9f6e"/><text x="215" y="210" text-anchor="middle">UB 512KB</text><text x="215" y="226" text-anchor="middle" font-size="10" fill="#5f6b7a">多 batch 驻留 pingpong</text>
|
||||
<rect x="320" y="192" width="150" height="44" rx="6" fill="#e6f7ee" stroke="#0e9f6e"/><text x="395" y="210" text-anchor="middle">Vector 单元</text><text x="395" y="226" text-anchor="middle" font-size="10" fill="#5f6b7a">每拍 256B:Mul / InitOutput</text>
|
||||
<rect x="510" y="192" width="70" height="44" rx="6" fill="#fdeee2" stroke="#b45309"/><text x="545" y="218" text-anchor="middle">GM</text>
|
||||
<line x1="110" y1="214" x2="146" y2="214" stroke="#0e9f6e" stroke-width="1.5" marker-end="url(#ar2)"/>
|
||||
<line x1="280" y1="214" x2="316" y2="214" stroke="#0e9f6e" stroke-width="1.5" marker-end="url(#ar2)"/>
|
||||
<line x1="470" y1="214" x2="506" y2="214" stroke="#0e9f6e" stroke-width="1.5" marker-end="url(#ar2)"/>
|
||||
<text x="600" y="218" font-size="11" fill="#5f6b7a">不经过 L1/L0/Cube,64 个 AIV 纯向量流水</text>
|
||||
</svg>
|
||||
<div class="figcap">图 5-2 昇腾 950PR 上 BMM 的两条数据通路。分支 0/1 走通路 B,其余走通路 A;各分支的差异在于"哪一级驻留什么、驻留多少、并行维度选谁"</div>
|
||||
</div>
|
||||
|
||||
<div class="branch">
|
||||
<h3 id="b0">5.1 K_EQUAL_ZERO(优先级 0)—— K=0,输出清零 <span class="tag aiv">AIV_ONLY</span></h3>
|
||||
<p class="src">tiling:arch35/batch_matmul_v3_k_equal_zero_tiling.cpp(68 行);kernel:mat_mul_v3/arch35/mat_mul_input_k_eq_zero_clear_output.h</p>
|
||||
<h4>① 目标 case</h4>
|
||||
<p>K 轴长度为 0 的退化 BMM。语义上 C = 对空 K 维求和 = 全零矩阵,<b>没有任何乘加计算</b>。</p>
|
||||
<h4>② 进入条件(IsCapable,cpp L27-41,逐条)</h4>
|
||||
<table>
|
||||
<tr><th>#</th><th>条件(源码表达式)</th><th>取值依据 / 源码注释</th></tr>
|
||||
<tr><td>1</td><td><code>aFormat == FORMAT_FRACTAL_NZ || bFormat == FORMAT_FRACTAL_NZ</code> → 拒绝</td><td>注释:<i>"The NZ format is not supported in this strategy."</i> 清零 kernel 是纯 AIV vector kernel,只吃 ND 连续布局,NZ 分形格式对它无意义</td></tr>
|
||||
<tr><td>2</td><td><code>hasBias</code> → 拒绝</td><td>有 bias 时输出应为 bias 广播值而非 0,清零语义不成立</td></tr>
|
||||
<tr><td>3</td><td><code>kValue != 0</code> → 拒绝</td><td>本分支唯一触发条件:K==0</td></tr>
|
||||
</table>
|
||||
<h4>③ tiling 实现步骤(DoOpTiling,cpp L43-48)</h4>
|
||||
<ol class="tight">
|
||||
<li><code>totalDataAmount = m × n × batchC</code> —— 输出总元素量,作为负载均分指标(本分支不再细分单核任务,kernel 侧按总量均分);</li>
|
||||
<li><code>usedCoreNum = aivNum</code> —— <b>用全部 64 个 AIV</b> 而非 32 个 AIC。<b>为什么</b>:清零是纯 vector 写操作,Cube 阵列全程空转;950 的 AIC/AIV 是分离架构、各有独立指令流,用 AIV 做这件事 AIC 可以完全不上电执行;且 AIV 核数是 AIC 的 2 倍,memset 类带宽型任务并行度更高。<code>GetNumBlocks()</code> 同样返回 aivNum。</li>
|
||||
</ol>
|
||||
<h4>④ kernel 实现</h4>
|
||||
<p><code>MatMulInputKEqZeroClearOutput</code>:<code>if ASCEND_IS_AIC { return; }</code>(AIC 直接退出);每个 AIV 对均分的输出区间调用 <code>AscendC::InitOutput<DTYPE_Y>(outputGM[...], copyDataAmount, 0)</code> 向 GM 写 0,尾区间用 tailDataCount 处理。数据通路即图 5-2 通路 B 的极简形态(连 UB 都不需要,InitOutput 直接铺 GM)。</p>
|
||||
<p><b>为什么要单列这个分支</b>:K=0 在 shape 校验中是合法输入(<code>k∈[0, INT32_MAX]</code>),若不放行会被通用 cube 模板接住——白白建立 GM→L1→L0 流水做"0 深度累加",既浪费 AIC 又拖慢端到端时延。作为计算模式彻底改变的特判,它必须排在最高优先级拦截。tilingKey:BASIC_LEVEL + BATCH_MODEL + <b>Model=K_EQUAL_ZERO</b> + NONE_FULL_LOAD + ON_THE_FLY。</p>
|
||||
</div>
|
||||
|
||||
<div class="branch">
|
||||
<h3 id="b1">5.2 TO_MUL / matmul2mul(优先级 1)—— K=1 退化为向量乘 <span class="tag aiv">AIV_ONLY</span></h3>
|
||||
<p class="src">tiling:arch35/batch_matmul_v3_matmul2mul_tiling.cpp(133 行);kernel:batch_mat_mul_v3_matmul2mul_cmct.h + common/cmct/kernel/kernel_batch_matmul_to_mul.h</p>
|
||||
<h4>① 目标 case</h4>
|
||||
<p>K==1 的 BMM:<code>C[m,n] = A[m,0] × B[0,n]</code>,矩阵乘退化为列向量⊗行向量的外积,逐元素乘即可,<b>K 维无累加深度</b>。</p>
|
||||
<h4>② 进入条件(IsCapable,cpp L67-115,逐条)</h4>
|
||||
<table>
|
||||
<tr><th>#</th><th>条件(源码表达式)</th><th>取值依据(源码注释原文 / 分析)</th></tr>
|
||||
<tr><td>1</td><td>输入 0/1 为"非连续转置" → 拒绝</td><td>注释:<i>"Non-contiguous transpose does not support Matmul2Mul."</i> mul kernel 按连续内存寻址,不支持 view 转置的 stride 访问</td></tr>
|
||||
<tr><td>2</td><td><code>aType != bType || aType != cType</code> → 拒绝</td><td>注释:<i>"Inconsistent a/b/c data types are not supported"</i> 向量路径没有类型提升逻辑,三端必须同型</td></tr>
|
||||
<tr><td>3</td><td>NZ 格式 → 拒绝</td><td>同分支 0,纯 vector kernel 只吃 ND</td></tr>
|
||||
<tr><td>4</td><td>A/B 四级 batch 任一不等 → 拒绝</td><td>mul 是逐 batch 对应元素相乘,<b>不支持 broadcast</b>(广播 case 留给通用分支的 % 映射)</td></tr>
|
||||
<tr><td>5</td><td><code>batchC < 128</code> → 拒绝</td><td>注释原文:<i>"batch数大于等于128(AIV CoreNum *2) 才能开pingpong"</i>。<b>128 = aivNum(64) × 2</b>:每核至少分到 2 个 batch,才能一份在算、一份在搬,构成 UB pingpong 流水</td></tr>
|
||||
<tr><td>6</td><td><code>hasBias</code> → 拒绝</td><td>bias 加法未在 mul kernel 实现</td></tr>
|
||||
<tr><td>7</td><td><code>kValue != 1</code> → 拒绝</td><td>触发条件:K==1</td></tr>
|
||||
<tr><td>8</td><td><code>n > 32B/dtype && n <= 256B/dtype</code> → 拒绝</td><td>注释原文:<i>"N>256/DtypeSize才能用满Vector的计算能力,小N走特殊优化"</i>。Vector 一拍处理 256B(054篇):N>256B/dtype 时一次 Mul 指令打满吞吐;N≤32B/dtype(不足一个 32B block)走 DoOpTiling 的小 N 特殊优化公式;<b>中间区</b> (32B, 256B]/dtype 两头不占,让给其它分支</td></tr>
|
||||
<tr><td>9</td><td><code>n == 1</code> → 拒绝</td><td>注释原文:<i>"N=1时部分case劣化"</i>——实测劣化的经验排除项</td></tr>
|
||||
<tr><td>10</td><td><code>(alignM + alignN + alignM×alignN)×dtype > ubSize</code> → 拒绝</td><td>UB 容量校验:单 batch 要同时驻留 A 向量(alignM 元素)、B 行(alignN)、C 外积结果(alignM×alignN),三者放不下 UB(512KB)则该分支不可行。alignNum_=32B/dtype 是 vector 一次访存的最小对齐粒度</td></tr>
|
||||
<tr><td>11</td><td><code>n % (256B/dtype) == 0</code> → 拒绝</td><td>N 恰为 256B/dtype 整数倍属规整形状,存在更优的规整路径,本策略主动让出(防重复覆盖)</td></tr>
|
||||
</table>
|
||||
<h4>③ tiling 实现步骤(DoOpTiling,cpp L29-65)</h4>
|
||||
<ol class="tight">
|
||||
<li>对齐准备:<code>alignM = CeilAlign(m, 32B/dtype)</code>,<code>alignN = CeilAlign(n, 32B/dtype)</code>;<code>usedCoreNum = aivNum</code>;</li>
|
||||
<li><code>singleBatchSize = (m + alignN + m×alignN) × dtypeSize</code> —— 单 batch 占 UB 字节数(A + B + C 三段);<b>小 N 特殊优化</b>:若 <code>alignN ≤ 32B/dtype</code>(不足一条向量指令),改用 <code>(alignM + alignN + alignM×alignN) × dtypeSize</code>,A/C 也按对齐块整块处理;</li>
|
||||
<li><code>singleCoreBatch = CeilDiv(batchC, aivNum)</code> —— 每核应分 batch 数(负载均衡基准);</li>
|
||||
<li><code>ubLimitBatchNum = ubSize / singleBatchSize</code> —— UB 容量允许的单轮驻留 batch 数;</li>
|
||||
<li><code>batchNum = min(singleCoreBatch, ubLimitBatchNum)</code> —— <b>每核每轮实际处理 batch 数 = 负载均分与 UB 容量的较小者</b>。这个 min 是本分支的核心:batchNum 决定 pingpong 的深度;</li>
|
||||
<li>尾轮处理三连:<code>batchNumLastRound = singleCoreBatch % batchNum(整除时为 batchNum)</code>;<code>lastCoreNum = batchC % (batchNum×aivNum) / batchNumLastRound</code>(最后一轮满载核数);<code>batchNumLastRoundTail</code>(最后一个不满载核的 batch 数,恰整除时归并为满载核)——把"总 batch 不能整除 batchNum×核数"的余数精确拆成<b>若干满核 + 一个尾核</b>,kernel 直接按这三参数索引,无需运行时再做除法。</li>
|
||||
</ol>
|
||||
<h4>④ 为什么 K=1 必须离开 Cube?</h4>
|
||||
<p>Cube 阵列一拍完成 16×16×16(fp16)的 fractal 乘加(054篇)。K=1 时,每次 mmad 的 K 维只有 1/16 被利用——<b>阵列 15/16 的 MAC 空转</b>,还要为此支付 GM→L1→L0A/L0B 的分形搬运、L0C 累加与 fixpipe 写出全链路开销,计算访存比极差。而 AIV 每拍处理 256B 连续数据,外积逐元素乘 + UB 多 batch pingpong 能把 MTE 带宽吃满;64 个 AIV 的聚合向量算力(FP16 54 TFLOPS)对这个计算密度绰绰有余。<b>这就是为什么触发条件里 batchC≥128(开 pingpong)与 N>256B/dtype(打满向量指令)都以"带宽/指令利用率"为判据</b>——该分支的收益模型是纯带宽模型,不是算力模型。</p>
|
||||
<h4>⑤ kernel 实现</h4>
|
||||
<p><code>KernelBatchMatMulToMul</code>(Cmct 框架,ProblemShape 的 k 直接写死 1):<code>if ASCEND_IS_AIC { return; }</code>;AIV 按 <code>BlockSchedulerBatchMatMulToMulBuiltIn</code> 给出的 usedCoreNum/singleCoreBatch/batchNum/lastCoreNum 做 batch 组轮询(<code>tileIdx += blockNum</code>),每组由 <code>BlockMmadBuilder<..., BatchMatmulToMul<>></code> 生成的向量乘 block 处理。通路:GM→UB→Mul→GM,完全绕过 L1/L0/Cube(图 5-2 通路 B)。tilingKey:BASIC_LEVEL + <b>BatchModel=BATCH_MATMUL_TO_MUL</b>(AIV_ONLY)。</p>
|
||||
</div>
|
||||
<div class="branch">
|
||||
<h3 id="b2">5.3 STREAM_K(优先级 2)—— 超大 K + MN 并行度不足时切 K <span class="tag mix">MIX_AIC_1_2</span></h3>
|
||||
<p class="src">tiling:arch35/batch_matmul_v3_basic_streamk_tiling.cpp(165 行);kernel:mat_mul_v3/arch35/mat_mul_streamk.h(MatMulStreamKKernel / MatMulStreamKActKernel)</p>
|
||||
<h4>① 目标 case</h4>
|
||||
<p><b>K 极大而 batch×M×N 基本块总数填不满 AIC</b> 的场景(典型:小 batch、小 M/N、超长 K)。此时若只沿 batch/M/N 切分,32 个 AIC 大半空转,唯一剩余的并行维度是 K。</p>
|
||||
<h4>② 进入条件(IsCapable cpp L70-98 + CheckStreamKSKTiling cpp L37-68,逐条)</h4>
|
||||
<table>
|
||||
<tr><th>#</th><th>条件(源码表达式)</th><th>取值依据(注释原文 / 分析)</th></tr>
|
||||
<tr><td>1</td><td><code>GetDeterministicLevel() > 1</code> → 拒绝</td><td>注释原文:<i>"batch一致性控制,当开关等级为2或3时,拒绝切k模板,达到强一致性和batch一致性"</i>。切 K 后多核部分和的归约顺序不定,引入浮点累加非确定性;确定性等级 ≥2 的业务(如训练梯度对比)禁用</td></tr>
|
||||
<tr><td>2</td><td>A/B 四级 batch 不等 → 拒绝</td><td>kernel 按等 batch 均摊核数(blocksPerBatch = aicNum/batchC),广播 batch 无法均摊</td></tr>
|
||||
<tr><td>3</td><td><code>aFormat != FORMAT_ND</code> → 拒绝</td><td>注释:<i>"ND is the only supported format for tensor_a in basic api"</i></td></tr>
|
||||
<tr><td>4</td><td><code>batchBias > 1</code> → 拒绝</td><td>bias 多 batch 未支持</td></tr>
|
||||
<tr><td>5</td><td>非连续转置 → 拒绝</td><td>注释:<i>"Non-contiguous transpose does not support StreamK."</i></td></tr>
|
||||
<tr><td>6</td><td><code>aivNum != aicNum × 2</code> → 拒绝</td><td>注释:<i>"streamk only support aivNum == aicNum * 2"</i>。归约/fixpipe 后处理依赖 1 AIC : 2 AIV 的固定配比(351x 硬件特征,054篇);非此配比平台直接拒绝</td></tr>
|
||||
<tr><td>7</td><td>fp32 非 hf32 且 <code>k > 2,000,000</code> → 拒绝</td><td>注释原文:<i>"如果dtype是fp32且k轴大于200万 则走基础模板来保证fp32的精度"</i>、<i>"Due to the requirement of binary accumulation..."</i>。fp32 超长 K 需要二叉树累加保精度,StreamK 的串行归约不满足;<b>200 万是精度劣化可接受的经验上限(源码经验值,文档无依据)</b></td></tr>
|
||||
<tr><td>8</td><td><code>CeilAlign(k,256) < max(8192, aicNum×256B/dtype)</code> → 拒绝</td><td>K 下界双保险:<b>aicNum×256B/dtype</b> 保证"即使全部 AIC 都参与切 K,每核也至少分到 256B(一个 K 内轴搬运块 BASIC_BLOCK_K_256_BYTE)",否则单核 K 段太短、搬运启动开销盖过收益;<b>8192</b> 是绝对收益门槛(元素数)。外层 CeilAlign(k,256) 先把 K 对齐到基本块再比较</td></tr>
|
||||
<tr><td>9</td><td><code>batchC × CeilDiv(m,alignV) × CeilDiv(n,alignV) > aicNum/2</code> → 拒绝</td><td>★ 核心判据。alignV=256(fp32 非 hf32 时 32,注释<i>"如果是Fp32 基本块判断要用32"</i>)。注释:<i>"判断bmn是否需要已经能切16份及以上"</i>。语义:<b>若 batch×M×N 已能切出超过"核数一半"的块,MN 方向并行度已够</b>(留一半核作均衡余量),无需切 K;反之才进 StreamK。此条件同时保证 DoOpTiling 中 mnCnt ≤ blocksPerBatch/2,使 kCnt ≥ 2(见步骤 4/7 推导)</td></tr>
|
||||
</table>
|
||||
<h4>③ tiling 实现步骤(DoOpTiling,cpp L100-138)</h4>
|
||||
<ol class="tight">
|
||||
<li><code>ResetBase</code>(DAV_3510 特化:baseM=baseN=256,baseK=128B/dtype,"256 is better base");</li>
|
||||
<li><code>mCnt = CeilDiv(m, baseM)</code>、<code>nCnt = CeilDiv(n, baseN)</code>;</li>
|
||||
<li><code>blocksPerBatch = aicNum / batchC</code> —— 每个 batch 平均分到的 AIC 核数;</li>
|
||||
<li><b>mCnt/nCnt 收拢</b>:<code>if (mCnt > blocksPerBatch/3 && mCnt < blocksPerBatch/2) mCnt = blocksPerBatch/2</code>(nCnt 同理)。<b>推导</b>:由条件 9 已知 mnCnt ≤ bpb/2;若 mCnt 落在 (bpb/3, bpb/2),则 nCnt ≤ (bpb/2)/mCnt < 1.5 即 nCnt 必为 1;收拢后 mnCnt = bpb/2,下一步 kCnt = bpb/mnCnt = 2,恰好把该 batch 的核预算在 M 向和 K 向各分一半,得到规则的 2 路切 K;不收拢则产生零散尾块、负载不均。这是"块数离散化取整到规则分点"的手法;</li>
|
||||
<li>反推实际基本块:<code>baseM = CeilAlign(CeilDiv(m, mCnt), 16)</code>,baseN 同理(16 = cube fractal 粒度,054篇);</li>
|
||||
<li><code>tailInfo.kCnt = blocksPerBatch / mnCnt</code> —— <b>kCnt 推导</b>:每 batch 的核预算 ÷ MN 已占块数 = 每个输出块还能再切几份 K;由 mnCnt ≤ bpb/2 得 <b>kCnt ≥ 2</b>(至少 2 路切分);</li>
|
||||
<li><code>singleCoreK = CeilDiv(k, kCnt)</code> —— 每核承担的 K 长度;</li>
|
||||
<li><code>baseK = min(singleCoreK, FloorAlign(L0A半区/dtype/max(baseM,baseN), alignV))</code> —— K 基本块取"均分 K"与"L0A 容量上限"较小者。L0A 半区 = 64KB/2(DB 两份,067篇);K 为内轴时(A 不转置或 B 转置)按 128B 对齐(056篇:内轴 128B/256B/512B 对齐发挥带宽),否则按 16 对齐;</li>
|
||||
<li><code>CalL1Tiling</code> 定 stepK/depthA1/depthB1;</li>
|
||||
<li><b>深度再平衡</b>:<code>if (baseM==baseN && depthB1==2×depthA1) { depthA1×=2; depthB1÷=2; }</code>(注释<i>"depthb1 is less than deptha1"</i>)——M、N 块相等时 A/B 应对称,若 B 的 L1 深度恰为 A 两倍则互换,使 depthA1 ≥ depthB1;</li>
|
||||
<li>apiLevel:仅原生 BatchMatMulV3 节点且不回避 tensor API 时 TENSOR_LEVEL,否则 BASIC_LEVEL(注释:<i>"DAV_RESV及CV自动融合当前只支持基础API"</i>)。</li>
|
||||
</ol>
|
||||
<h4>④ workspace 与 L0C2Out 的设计</h4>
|
||||
<p><code>GetWorkspaceSize() = aicNum × 256×256 × 4B + 20MB</code>:前半是<b>每个 AIC 一块 256×256 fp32 部分和缓冲</b>(对应 baseM×baseN 上限的累加结果——各核的部分和先写 GM workspace,再由归约阶段累加);后半 20MB 是基类默认 RPC 区(注释 <i>"20MB workspace for RPC"</i>,核间通信/同步标志位)。fixpipe 选择 <code>GetL0C2OutFlag()</code>:<code>n>64 且 n%16≠0 且 m>2 且 m×n≥256</code> → ND_FIXPIPE_1_2(N 不对齐且输出块足够大时,L0C→GM 直通会成为瓶颈,改走 1 AIC : 2 AIV 的 ND fixpipe 通路分担搬出)。</p>
|
||||
<h4>⑤ 为什么这么做:StreamK 方法学背景</h4>
|
||||
<p>StreamK 思想源于 GPU 端的 Stream-K 工作中心分解(Osama 等,arXiv:2301.03598,2023):传统 data-parallel GEMM 按输出块切核,当输出块数 < 核数时算力浪费;StreamK 把总工作量(输出块×K 迭代)按"工作量流"均分给所有核,核间通过部分和归约汇合。CANN 公开文档中<b>没有 StreamK 的说明</b>(知识库检索仅命中 catlass/ops-nn 源码),官方文档的切 K 建议是 EnableMultiCoreSplitK + GM 清零 + AtomicAdd(206篇,案例 K=1024 切 2 核后 19.60us→13.70us)。BMM 这里的实现与官方简易切 K 的差异在于:<b>用 workspace 归约而非 AtomicAdd</b>(fp32 部分和写 workspace 再规约,避免 GM 原子加的顺序随机性——这也解释了条件 1 的确定性等级管控),并用 <code>aivNum==2×aicNum</code> 的 MIX 模式让 AIV 承担归约/搬出。进入条件的两条 K 阈值(≥max(8192, aic×256B/dtype) 才切、fp32 ≤200 万)分别守住"切 K 收益下限"与"fp32 精度上限"。</p>
|
||||
<div class="figbox">
|
||||
<svg viewBox="0 0 980 250" xmlns="http://www.w3.org/2000/svg" font-family="PingFang SC,Microsoft YaHei,sans-serif" font-size="12">
|
||||
<defs><marker id="ar3" markerWidth="8" markerHeight="8" refX="7" refY="3" orient="auto"><path d="M0,0 L7,3 L0,6 Z" fill="#6d28a8"/></marker></defs>
|
||||
<text x="20" y="22" font-weight="bold" fill="#4c1d95">StreamK:一个输出块的 K 维被切给 kCnt 个核</text>
|
||||
<rect x="20" y="40" width="200" height="60" rx="6" fill="#f3e8fd" stroke="#6d28a8"/>
|
||||
<text x="120" y="60" text-anchor="middle">输出块 C[m,n]</text><text x="120" y="78" text-anchor="middle" font-size="11" fill="#5f6b7a">K = singleCoreK × kCnt</text>
|
||||
<line x1="220" y1="55" x2="280" y2="40" stroke="#6d28a8" stroke-width="1.3" marker-end="url(#ar3)"/>
|
||||
<line x1="220" y1="70" x2="280" y2="85" stroke="#6d28a8" stroke-width="1.3" marker-end="url(#ar3)"/>
|
||||
<line x1="220" y1="85" x2="280" y2="130" stroke="#6d28a8" stroke-width="1.3" marker-end="url(#ar3)"/>
|
||||
<rect x="285" y="30" width="150" height="36" rx="5" fill="#eaf2fb" stroke="#0b6bcb"/><text x="360" y="52" text-anchor="middle">核 i:K 段 0 部分和</text>
|
||||
<rect x="285" y="75" width="150" height="36" rx="5" fill="#eaf2fb" stroke="#0b6bcb"/><text x="360" y="97" text-anchor="middle">核 i+1:K 段 1 部分和</text>
|
||||
<rect x="285" y="120" width="150" height="36" rx="5" fill="#eaf2fb" stroke="#0b6bcb"/><text x="360" y="142" text-anchor="middle">…核 i+kCnt−1</text>
|
||||
<line x1="435" y1="48" x2="500" y2="80" stroke="#6d28a8" stroke-width="1.3" marker-end="url(#ar3)"/>
|
||||
<line x1="435" y1="93" x2="500" y2="93" stroke="#6d28a8" stroke-width="1.3" marker-end="url(#ar3)"/>
|
||||
<line x1="435" y1="138" x2="500" y2="106" stroke="#6d28a8" stroke-width="1.3" marker-end="url(#ar3)"/>
|
||||
<rect x="505" y="66" width="190" height="55" rx="6" fill="#fdeee2" stroke="#b45309"/>
|
||||
<text x="600" y="86" text-anchor="middle">GM workspace(fp32 部分和)</text>
|
||||
<text x="600" y="104" text-anchor="middle" font-size="11" fill="#5f6b7a">aicNum × 256×256 × 4B + 20MB RPC</text>
|
||||
<line x1="695" y1="93" x2="755" y2="93" stroke="#6d28a8" stroke-width="1.3" marker-end="url(#ar3)"/>
|
||||
<rect x="760" y="66" width="200" height="55" rx="6" fill="#e6f7ee" stroke="#0e9f6e"/>
|
||||
<text x="860" y="86" text-anchor="middle">归约(AIV,1:2 配比)+ fixpipe</text>
|
||||
<text x="860" y="104" text-anchor="middle" font-size="11" fill="#5f6b7a">确定性等级≥2 时此分支被禁用</text>
|
||||
<text x="20" y="180" font-size="11" fill="#5f6b7a">进入前提:batchC×mCnt×nCnt ≤ aicNum/2(MN 并行度不足一半核数)且 K ≥ max(8192, aicNum×256B/dtype)(每核 K 段足够长)</text>
|
||||
<text x="20" y="200" font-size="11" fill="#5f6b7a">kCnt = blocksPerBatch / (mCnt×nCnt),由判据保证 kCnt ≥ 2;fp32 且 K>200 万时因 binary accumulation 精度要求回退基础模板</text>
|
||||
</svg>
|
||||
<div class="figcap">图 5-3 StreamK 数据流:K 向切分 → 各核部分和写 workspace → AIV 归约写出</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="branch">
|
||||
<h3 id="b3">5.4 MERGE_BATCH_BASICAPI(优先级 3)—— 小 M/N 大 batch 合并进 L0 <span class="tag">AIC_ONLY</span></h3>
|
||||
<p class="src">tiling:arch35/batch_matmul_v3_mergebatch_basicapi_tiling.cpp(145 行);kernel:batch_mat_mul_v3_mergebatch_basicapi_cmct.h + common/cmct/kernel/kernel_matmul_merge_batch.h</p>
|
||||
<h4>① 目标 case</h4>
|
||||
<p>M、N 很小(要求 M≤N)、K≥64、<b>batch 巨大</b>的 BMM(如小维度投影层的海量 batch 推理)。单 batch 的基本块太小喂不饱 Cube,把多个 batch 在 M/N 方向"拼接"成大块一次算。</p>
|
||||
<h4>② 进入条件(IsCapable,cpp L28-77,逐条)</h4>
|
||||
<table>
|
||||
<tr><th>#</th><th>条件(源码表达式)</th><th>取值依据(注释原文 / 分析)</th></tr>
|
||||
<tr><td>1</td><td>非连续转置 / NZ → 拒绝</td><td>合并 batch 要求物理内存连续(注释:<i>"Non-contiguous transpose does not support MergeBatch."</i>)</td></tr>
|
||||
<tr><td>2</td><td><code>hasBias || (fp32 且非 hf32)</code> → 拒绝</td><td>bias 未支持;fp32 不转 hf32 时 L0C 内 fp32 累加块容量减半、合并收益差,直接排除</td></tr>
|
||||
<tr><td>3</td><td>A/B 四级 batch 不等 → 拒绝</td><td>合并要求 A/B batch 完全一致(无广播)</td></tr>
|
||||
<tr><td>4</td><td><code>batchC < 4 × aicNum</code> → 拒绝</td><td>常量 <code>MIN_BATCH_L0=4</code>(.h L46),注释原文:<i>"each aic should process at least 4 batchs"</i>。<b>为什么 4</b>:合并的最小粒度是 4 个 batch(合并 4 个才有"L0 一块装下"的收益),且每个 AIC 至少要分到一份最小合并块 → 总 batch ≥ 4×aicNum</td></tr>
|
||||
<tr><td>5</td><td><code>alignK < 64 || m > n</code> → 拒绝</td><td>注释:<i>"shape check, aligned k shape should be at least 64"</i>。K≥64(fp16 下 128B)是一次 K 内轴搬运的最低收益门槛;m≤n 因为合并沿 N 方向展开(tempAlignN=4×alignN),N 是较大维才有合并收益</td></tr>
|
||||
<tr><td>6</td><td>预计算 tempAlignM / tempAlignN</td><td><code>tempAlignM = CeilAlign(4m, 16)</code>;<b>但 isATrans 且 m>1 时 <code>tempAlignM = 4×CeilAlign(m,16)</code></b>——A 不转置时 4 个 batch 的 M 行内存天然连续,先拼成 4m 再整体对齐(损耗 ≤15 元素);A 转置时每个 batch 的 fractal 独立、M 维须各自对齐 16 再拼接(损耗 ≤4×15),容量校验更保守。tempAlignN = 4×alignN(N 向总是逐 batch 对齐后堆叠)</td></tr>
|
||||
<tr><td>7</td><td><b>L0 三容量校验</b>(最小合并粒度 4 下):<code>tempAlignM×minBaseK×dtype×2 > l0ASize</code> 或 <code>tempAlignN×minBaseK×dtype×2 > l0BSize</code> 或 <code>tempAlignM×tempAlignN×4B×2 > l0CSize</code> → 拒绝</td><td>注释:<i>"l0 buffer check"</i>。L0A 须装下"4 合并 M × 最小 K"的双缓冲;L0B 对称;L0C 须装下"4M×4N"的 fp32 累加双缓冲(64KB/64KB/256KB,950 白皮书表4-2)。<b>连最小合并(4 batch)都放不下,合并就无可行性</b>。minBaseK:K 参与 fractal 对齐(A 转置或 B 不转置)时取 16 倍数,否则取 c0Size=32B/dtype</td></tr>
|
||||
</table>
|
||||
<h4>③ tiling 实现步骤(DoOpTiling,cpp L79-113)</h4>
|
||||
<ol class="tight">
|
||||
<li><code>batchNumPerCore = CeilDiv(batchC, aicNum)</code>;</li>
|
||||
<li>由 L0B 反推 baseK 上限:<code>maxBaseK = l0BSize / 4 / alignN / dtype / 2</code>(L0B 要同时容纳 4 个 batch 的 B 块、双缓冲),向下 16 对齐、保底 16;</li>
|
||||
<li><code>baseK = min(maxBaseK, 64)</code> —— 注释:<i>"threshold of basek is 64"</i>。<b>为什么 64</b>:mergebatch 的收益来自 batch 合并而非 K 深度;baseK=64(fp16 下 128B)已满足 K 内轴最小搬运粒度(056篇),把 baseK 压小可腾出 L0B 容量给更多合并 batch(下一步 maxBatchL0 与 baseK 成反比)——64 是"搬运效率"与"合并度"折中的经验上限(源码经验值);</li>
|
||||
<li><code>maxBatchL0 = max(l0BSize/baseK/dtype/2 / alignN, 1)</code> —— 已定 baseK 下,L0B 双缓冲能容纳的 N 向总元素 ÷ 单 batch 对齐 N = <b>L0B 容量允许的最大合并 batch 数</b>;</li>
|
||||
<li><code>mergeBatchL0 = min(CalBatchL0WithPolynomial(l0cElem, m), maxBatchL0, batchNumPerCore)</code> —— 最终合并数取三者最小:<b>L0C 多项式解(容量最优)∩ L0B 上限 ∩ 每核实际 batch 数</b>;</li>
|
||||
<li>合并块:<code>baseM = CeilAlign(mergeBatchL0×m, 16)</code>(transA 时逐 batch 对齐再累加)、<code>baseN = mergeBatchL0 × alignN</code>;</li>
|
||||
<li>L1 步进(注释 <i>"4 buffer for al1_db and bl1_db"</i>):<code>stepKaMax = min(CeilDiv(batchNumPerCore, mergeBatchL0), l1Size/4/(baseM×baseK×dtype))</code>——L1 均分 4 份(A pingpong 2 份 + B pingpong 2 份),单份容量 l1Size/4 除以单步数据量得容量允许的最大步数,再与 batch 轮数、K 总步数取 min;</li>
|
||||
<li>对外写回 <code>baseM = alignM, baseN = alignN</code>(单 batch 对齐值)+ <code>mergeBatchL0</code> 字段 —— 注释:<i>"M和N在L1和L0里不切分"</i>,<b>切分全部发生在 batch 维</b>,合并倍数由独立字段传给 kernel。</li>
|
||||
</ol>
|
||||
<h4>④ CalBatchL0WithPolynomial:L0C 容量下最优合并数的多项式求根(cpp L115-124)</h4>
|
||||
<p>注释原文:<i>"多项式求解最佳batchL0"</i>。设合并 x 个 batch,合并后 L0C 输出块为 <code>CeilAlign(x·m,16) × (x·alignN)</code>(fp32)。L0C 单缓冲容量 S = l0CSize/2/4 元素,约束:</p>
|
||||
<pre><code>CeilAlign(x·m, 16) · x·alignN ≤ S
|
||||
令 a = m/16,取整余量 t = 15/16(向上取整的最大余量),则 CeilAlign(x·m,16) ≈ 16(ax + t)
|
||||
代入:16(ax + t) · x·alignN ≤ S ⟹ ax² + tx ≤ S/(16·alignN)
|
||||
两边乘 a 构造完全平方:(ax)² + t(ax) ≤ aS/(16·alignN) ≜ p
|
||||
解正根:ax = √(p + t²/4) − t/2 ≜ y // 与源码 L121 逐符一致
|
||||
x = floor( min( p/(ceil(y)·a), ceil(y)/a ) ) // L0C 容量约束 ∩ M 块数自洽,保底 1</code></pre>
|
||||
<p><b>为什么这个根是最优</b>:y 是连续意义下"恰好装满 L0C 单缓冲"的合并块 M 向 16-块数上界——约束取等号时 L0C 利用率 100%:x 再大就溢出、再小则合并度不足(cube 有效算力占比随合并度单调上升)。取整时用 <code>ceil(y)</code> 作实际 M 块数 Y,再同时满足 L0C 容量(x ≤ p/(Y·a))与 M 块数自洽(x·a ≤ Y)两个约束取 min,floor 后保底 1,得到不超过 L0C 容量的最大合法整数合并数。</p>
|
||||
<h4>⑤ kernel 实现</h4>
|
||||
<p><code>KernelMatMulMergeBatch</code> + <code>BlockSchedulerMergeBatchBuiltIn</code>:构造时做负载均衡——<code>mainBatchNum = batchC/batchAL1</code>、<code>mainBatchLoop = mainBatchNum/blockNum</code>,余数均摊成"主块/主尾块/最终尾块"三种 tile(前几个核多算一个),<code>GetIterBatchTuple = {batchAL1, batchBL1, batchL0, batchL0}</code>(A/B 在 L1/L0 可载不同 batch 数)。支持 fixpipe 1V2 融合 add/mul epilogue(<code>BlockEpilogueMergeBatch</code>)。tilingKey:BASIC_LEVEL + <b>BatchModel=MERGE_BATCH_MODEL</b>;另有一个细节:<code>transA = isATrans && m>1</code>(注释 <i>"trans_a=true && m==1 equals to trans_a=False"</i>,m=1 的转置是退化情形按非转置选 kernel)。</p>
|
||||
<div class="figbox">
|
||||
<svg viewBox="0 0 980 210" xmlns="http://www.w3.org/2000/svg" font-family="PingFang SC,Microsoft YaHei,sans-serif" font-size="12">
|
||||
<defs><marker id="ar4" markerWidth="8" markerHeight="8" refX="7" refY="3" orient="auto"><path d="M0,0 L7,3 L0,6 Z" fill="#0b6bcb"/></marker></defs>
|
||||
<text x="20" y="22" font-weight="bold" fill="#0b3d73">mergeBatchL0 个 batch 在 M/N 方向拼接,一次 mmad 等价连续算多个 batch</text>
|
||||
<!-- small blocks -->
|
||||
<g>
|
||||
<rect x="30" y="45" width="40" height="26" fill="#dbe7f5" stroke="#0b6bcb"/><text x="50" y="62" text-anchor="middle" font-size="10">b0</text>
|
||||
<rect x="30" y="75" width="40" height="26" fill="#dbe7f5" stroke="#0b6bcb"/><text x="50" y="92" text-anchor="middle" font-size="10">b1</text>
|
||||
<rect x="30" y="105" width="40" height="26" fill="#dbe7f5" stroke="#0b6bcb"/><text x="50" y="122" text-anchor="middle" font-size="10">b2</text>
|
||||
<rect x="30" y="135" width="40" height="26" fill="#dbe7f5" stroke="#0b6bcb"/><text x="50" y="152" text-anchor="middle" font-size="10">b3</text>
|
||||
<text x="50" y="180" text-anchor="middle" font-size="11" fill="#5f6b7a">单 batch M×N 很小</text>
|
||||
</g>
|
||||
<line x1="80" y1="105" x2="150" y2="105" stroke="#0b6bcb" stroke-width="1.5" marker-end="url(#ar4)"/>
|
||||
<rect x="155" y="45" width="70" height="116" fill="#eaf2fb" stroke="#0b3d73" stroke-width="1.5"/>
|
||||
<line x1="155" y1="74" x2="225" y2="74" stroke="#0b3d73" stroke-dasharray="3 2"/>
|
||||
<line x1="155" y1="103" x2="225" y2="103" stroke="#0b3d73" stroke-dasharray="3 2"/>
|
||||
<line x1="155" y1="132" x2="225" y2="132" stroke="#0b3d73" stroke-dasharray="3 2"/>
|
||||
<text x="190" y="180" text-anchor="middle" font-size="11" fill="#5f6b7a">L0A:合并后 baseM 块</text>
|
||||
<rect x="245" y="45" width="180" height="40" fill="#eaf2fb" stroke="#0b3d73" stroke-width="1.5"/>
|
||||
<line x1="290" y1="45" x2="290" y2="85" stroke="#0b3d73" stroke-dasharray="3 2"/>
|
||||
<line x1="335" y1="45" x2="335" y2="85" stroke="#0b3d73" stroke-dasharray="3 2"/>
|
||||
<line x1="380" y1="45" x2="380" y2="85" stroke="#0b3d73" stroke-dasharray="3 2"/>
|
||||
<text x="335" y="105" text-anchor="middle" font-size="11" fill="#5f6b7a">L0B:合并后 baseN 块(N 向堆叠)</text>
|
||||
<line x1="425" y1="85" x2="490" y2="100" stroke="#0b6bcb" stroke-width="1.5" marker-end="url(#ar4)"/>
|
||||
<rect x="495" y="60" width="110" height="80" rx="6" fill="#e3ecf7" stroke="#0b3d73"/>
|
||||
<text x="550" y="95" text-anchor="middle">Cube 一次计算</text>
|
||||
<text x="550" y="115" text-anchor="middle" font-size="10" fill="#5f6b7a">fractal 利用率拉满</text>
|
||||
<line x1="605" y1="100" x2="665" y2="100" stroke="#0b6bcb" stroke-width="1.5" marker-end="url(#ar4)"/>
|
||||
<rect x="670" y="60" width="150" height="80" rx="6" fill="#eaf2fb" stroke="#0b6bcb"/>
|
||||
<text x="745" y="88" text-anchor="middle">L0C 256KB</text>
|
||||
<text x="745" y="106" text-anchor="middle" font-size="10" fill="#5f6b7a">mergeBatchL0 由多项式求根</text>
|
||||
<text x="745" y="122" text-anchor="middle" font-size="10" fill="#5f6b7a">恰好装满 L0C 单缓冲</text>
|
||||
<text x="840" y="104" font-size="11" fill="#5f6b7a">前提:batchC ≥ 4×aicNum,K ≥ 64,M ≤ N</text>
|
||||
</svg>
|
||||
<div class="figcap">图 5-4 MERGE_BATCH:多 batch 合并进 L0 的基本块拼接</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="branch">
|
||||
<h3 id="b4">5.5 ITER_BATCH_BROADCAST_BASICAPI(优先级 4)—— 单边单轴 batch 广播 <span class="tag">AIC_ONLY</span></h3>
|
||||
<p class="src">tiling:arch35/batch_matmul_v3_iterbatch_broadcast_basicapi_tiling.cpp(333 行);kernel:batch_mat_mul_v3_iterbatch_broadcast.h(Blaze BatchMatMulIterBatchBroadcastKernel)</p>
|
||||
<h4>① 目标 case</h4>
|
||||
<p>A 或 B 的<b>某一级</b> batch 维为 1 的单边广播场景(如一份共享权重对多 batch 激活)。广播语义下被广播算子的单 batch 数据可被对端多个 batch 复用——L1 只需驻留一份广播算子,其余空间全部用来流水加载非广播算子的多个 batch,<b>MTE 搬运量按广播倍数减少</b>。</p>
|
||||
<h4>② 进入条件(IsCapable,cpp L232-261 + 子检查,逐条)</h4>
|
||||
<table>
|
||||
<tr><th>#</th><th>条件</th><th>取值依据(注释原文 / 分析)</th></tr>
|
||||
<tr><td>1</td><td>NZ → 拒绝;任一输入 stride 非连续 → 拒绝</td><td><code>IsContiguousStride</code> 自实现检查:收集 view 各维 (stride, size),跳过 stride=0(广播维)和 size=1 的维,按 stride 降序排序后验证期望 stride 逐维累乘——保证 L1 迭代搬运的地址连续</td></tr>
|
||||
<tr><td>2</td><td><code>batchBias > 1</code> → 拒绝</td><td>bias 必须单 batch</td></tr>
|
||||
<tr><td>3</td><td><code>hasBroadcastAxis()</code> 必须为真</td><td><b>4 级 batch 逐一比对</b>(cpp L92-124):<code>A维==1 且 B维!=1</code> 记 A 广播轴,反之记 B 广播轴。<b>"单边单轴"= 广播轴只在 A 或 B 一侧恰好出现 1 次</b>:双边广播(<i>"Dual-side broadcast is not supported."</i>)、多轴广播(<i>"Multi-axis broadcast is not supported."</i>)、无广播(<i>"No broadcast axis detected, should use ITER_BATCH."</i>)均拒绝。<b>为什么</b>:只有"单边单轴"才能保证 L1 驻留一份广播数据后、迭代轴是单一连续维;双边/多轴广播的复用关系无法用单驻留+单迭代表达,复杂 case 让位给通用 ASW 的 % 广播映射</td></tr>
|
||||
<tr><td>4</td><td><code>CheckNonBroadcastAxisMatch()</code></td><td>除广播轴外,A/B 其余 3 级 batch 维必须逐维相等(允许两侧都为 1)</td></tr>
|
||||
<tr><td>5</td><td><code>batchC ≤ aicNum</code> → 拒绝</td><td>日志:<i>"batchC <= aicNum, no need for iterbatch."</i> batch 不大于核数时 batch 维并行度不足,无 iterbatch 收益</td></tr>
|
||||
<tr><td>6</td><td><code>CheckL1IterBatch()</code>(cpp L143-206)</td><td>见下方③的 L1 容量推导</td></tr>
|
||||
<tr><td>7</td><td><code>CheckL0IterBatch()</code>(cpp L208-230)</td><td>见下方④</td></tr>
|
||||
</table>
|
||||
<h4>③ L1 容量推导与 iterBatchL1 的两种公式(CheckL1IterBatch)</h4>
|
||||
<ol class="tight">
|
||||
<li>单 batch 字节数:<code>sizeAOneBatch = alignM×alignK×dtype</code>、<code>sizeBOneBatch = alignK×alignN×dtype</code>、<code>sizeCOneBatch = alignM×alignN×4B</code>(L0C 内 fp32);</li>
|
||||
<li>L1 双缓冲门槛:<code>(sizeA + sizeB + bias) × 2 ≤ l1Size</code>(A、B、bias 各一份、开 DB 要放得下 512KB L1);</li>
|
||||
<li><b>单 batch 广播判定</b>:<code>innerDimsProduct = 广播轴之后(更内层)所有 batch 维在广播侧的乘积</code>;<code>broadcastSingleBatch = (innerDimsProduct == 1)</code>——含义:广播轴之后的内层维在广播侧全是 1,即广播侧在整个迭代中只对应<b>同一份单 batch 数据</b>,可以永久驻留 L1,循环只在非广播侧上做;</li>
|
||||
<li><b>iterBatchL1 两公式</b>(l1Avail = l1Size/2,先扣 DB):
|
||||
<ul class="tight">
|
||||
<li>广播侧单 batch 驻留(如 A 广播):<code>iterBatchL1 = (l1Avail − sizeA − bias) / sizeB</code> —— L1 半区先扣驻留的 A 单 batch 和 bias,<b>余下全部装 B 的多个 batch</b>;</li>
|
||||
<li>成对驻留(广播轴内层仍有真实 batch 维):<code>iterBatchL1 = (l1Avail − bias) / (sizeA + sizeB)</code> —— A、B 必须按 batch 成对进入;</li></ul></li>
|
||||
<li>下限与截断:<code>iterBatchL1 ≥ 2</code>(注释:<i>"iterBatchL1 expected to be no less than 2"</i>——广播收益的前提是至少 2 个 batch 复用驻留侧);再 <code>min(iterBatchL1, CeilDiv(batchC, aicNum))</code> 不超过单核平均 batch 数;</li>
|
||||
<li><b>整除因子收缩</b>:迭代轴长度 = 单batch广播时的非广播侧广播轴维度(如 A 广播则 B 的第 bcAxis 维),或成对驻留时的 innerDimsProduct;从 <code>min(iterBatchL1, 迭代轴长)</code> 向下找<b>能整除迭代轴的最大因子</b>——保证 L1 一次搬运的 batch 段在迭代轴上连续且整除,避免尾批破坏地址连续性(BMM 的 batch 段搬运是 NDDMA 多维整块搬,段边界不对齐迭代轴会产生碎地址)。</li>
|
||||
</ol>
|
||||
<h4>④ L0 容量检查与 0.8 均衡率(CheckL0IterBatch)</h4>
|
||||
<pre><code>iterBatchL0A = (l0ASize/2) / sizeAOneBatch; // L0A 半区(DB)可驻留 batch 数,L0B/L0C 同理
|
||||
l0CanLoadBatch = min(L0A, L0B, L0C) ≥ 1 // 标准情形:三级半区各放≥1 batch
|
||||
|| (L0A无DB ≥ 1 && L0B无DB ≥ 1 && L0C半区 > 1) // 放宽:牺牲输入 DB,换 L0C 多攒 batch
|
||||
// 源码注释:// try to reduce fixpipe instr
|
||||
// —— L0C 多攒几个 batch 的结果再一次 fixpipe 搬出,摊薄 fixpipe 指令开销</code></pre>
|
||||
<p><b>0.8 均衡率</b>(仅当 !l0CanLoadBatch 时执行):</p>
|
||||
<pre><code>avgIterBatch = batchC / aicNum // 每核理想平均 batch 数
|
||||
actualMaxIterBatch = CeilDiv(CeilDiv(batchC, iterBatchL1), aicNum) × iterBatchL1 // 最忙核实际负载
|
||||
balanceRate = avgIterBatch / actualMaxIterBatch < 0.8 → 拒绝</code></pre>
|
||||
<p>语义:batch 先按 iterBatchL1 分组、组再均分到核,最忙核要处理"ceil(组数/核数)×iterBatchL1"个 batch(尾组按满组放大)。若 L1 分组导致核间倾斜超过 20%,iterbatch 得不偿失,宁可落到通用分支。<b>为什么只在 L0 载不了多 batch 时检查</b>:L0 顺畅时 iterbatch 的收益足够覆盖一定的倾斜;L0 已打折时再叠加 20% 以上倾斜就不值了。0.8 为源码经验值(defaultBalanceOfBatch)。</p>
|
||||
<h4>⑤ DoOpTiling 步骤(cpp L265-325)</h4>
|
||||
<ol class="tight">
|
||||
<li>写回 <code>iterBatchL1</code>;<code>iterBatchL0 = max(min(L0A, L0B, L0C, iterBatchL1), 1)</code>(L0 侧迭代不超过 L1 侧);</li>
|
||||
<li>写广播轴 <code>broadcastAxisA/B</code>(无广播侧保持哨兵 4,kernel 据此识别方向);</li>
|
||||
<li><b>基本块决策</b>:l0CanLoadBatch 时 <code>baseM/N/K = alignM/N/K</code>(L0 能整 batch 装,一个 batch 一砖,M/N/K 都不切);</li>
|
||||
<li>否则按 M/N/K 大小关系 <b>4-case 收缩基本块</b>(思想:<b>选最小的对齐维固定为整块</b>——保证该维单次装完、至少一个矩阵的一维不被切,再用对应 L0 半区字节数反推另两维):
|
||||
<table>
|
||||
<tr><th>case</th><th>条件</th><th>固定</th><th>推导</th></tr>
|
||||
<tr><td>1</td><td>M<N 且 M>K(K 最小)</td><td>baseK=alignK</td><td>baseM=min(l0A/2/dtype/baseK, alignM);baseN=min(l0B/2/dtype/baseK, alignN)</td></tr>
|
||||
<tr><td>2</td><td>M<N 且 M≤K(M 最小)</td><td>baseM=alignM</td><td>baseK=min(l0A/2/dtype/baseM, alignK);baseN=min(l0B/2/dtype/baseK, alignN)</td></tr>
|
||||
<tr><td>3</td><td>M≥N 且 N>K(K 最小)</td><td>baseK=alignK</td><td>baseN=min(l0B/2/dtype/baseK, alignN);baseM=min(l0A/2/dtype/baseK, alignM)</td></tr>
|
||||
<tr><td>4</td><td>M≥N 且 N≤K(N 最小)</td><td>baseN=alignN</td><td>baseK=min(l0B/2/dtype/baseN, alignK);baseM=min(l0A/2/dtype/baseK, alignM)</td></tr>
|
||||
</table></li>
|
||||
<li>bias 约束:<code>baseN = min(baseN, btSize/2/4)</code>(BT 表半区能容纳的 fp32 元素数);</li>
|
||||
<li>L0C 约束:较大维收缩至 <code>l0CSize/2/4B ÷ 另一维</code>(保证 baseM×baseN 不超 L0C 半区);</li>
|
||||
<li>三维 <code>FloorAlign(max(x,16), 16)</code>(cube fractal 粒度保底)。</li>
|
||||
</ol>
|
||||
<h4>⑥ kernel 实现</h4>
|
||||
<p>Blaze 栈 <code>BatchMatMulIterBatchBroadcastKernel</code>(GemmUniversal + BlockSchedulerIterBatchBroadcast + MatmulIterBatchBroadcast<A_BC,B_BC>),scheduler 参数携带 broadcastAxisA/B 与 A/B/C 各 4 维 batch;NZ 等不支持 Blaze 的场景回落通用 BatchMatMulAswKernel(其 CalcGMOffset 的 <code>batchCiIndex % batchAi / % batchBi</code> 天然支持广播映射,只是失去 L1 单份驻留的搬运优化)。tilingKey:<b>TENSOR_LEVEL</b> + ITER_BATCH_BROADCAST_A/B_MODEL + 固定 ON_THE_FLY。</p>
|
||||
</div>
|
||||
|
||||
<div class="branch">
|
||||
<h3 id="b5">5.6 ITER_BATCH_BASICAPI(优先级 5)—— 普通大 batch(基础 API)<span class="tag">AIC_ONLY</span> <span class="tag mix">MIX_AIC_1_2</span></h3>
|
||||
<p class="src">tiling:arch35/batch_matmul_v3_iterbatch_basicapi_tiling.cpp(241 行);kernel:batch_mat_mul_v3_iterbatch_basicapi_cmct.h + common/cmct/kernel/kernel_matmul_iterbatch.h</p>
|
||||
<h4>① 目标 case</h4>
|
||||
<p>A/B batch 完全相等、<code>batchC > aicNum</code> 的普通大 batch BMM——每核要顺序算多个 batch,让 L1/L0 一次驻留多个 batch 形成流水,摊薄每 batch 的搬运与启动开销。</p>
|
||||
<h4>② 进入条件(IsCapable,cpp L74-136)</h4>
|
||||
<ul class="tight">
|
||||
<li>A 侧非连续转置拒绝(注意:<b>B 侧的 3D 非连续转置在 DoOpTiling 有专门兼容逻辑</b>,见步骤 3);NZ 拒绝;batchBias ≤ 1;四级 batch A/B 完全相等(广播已被分支 4 截胡);<code>batchC > aicNum</code>;</li>
|
||||
<li>L1 门槛:<code>((alignM×alignK + alignK×alignN)×dtype + bias) × 2 ≤ l1Size</code>(单 batch A+B+bias 开 DB 放得下)。对齐细节(源码注释):fp16 或 fp32 的 (m,k)/(n,k) 布局 M/N 按 16 对齐;fp32 的 (k,m)/(k,n) 布局按 8×2 对齐,<i>"for frac combine in loadtol0a/l0b"</i>(L0 装载时的分形合并要求);</li>
|
||||
<li><code>l0CanLoadBatch = min(L0A半区/sizeA, L0B半区/sizeB, L0C半区/sizeC, L1半区/(sizeA+sizeB)) ≥ 1</code>(带 bias 另需 <code>(btSize/2)/(alignN×4) ≥ 1</code>);不满足时执行与分支 4 相同的 0.8 均衡率检查。</li>
|
||||
</ul>
|
||||
<h4>③ DoOpTiling 步骤(cpp L140-233)与两个经验常数</h4>
|
||||
<ol class="tight">
|
||||
<li><b>两个微架构经验常数</b>(L142-143,注释原文):
|
||||
<pre><code>constexpr uint64_t mmadCount = 8UL; // cube count which will cause issuequene
|
||||
constexpr uint64_t fullCopySize = 64 * 1024UL; // datasize moving once which can use full of bandwith</code></pre>
|
||||
<b>依据分析</b>:056篇给出"MTE1 和 MMAD 指令队列深度为 32,队列满会阻塞其他指令下发、引起流水断流"——mmadCount=8 是"L1 一次驻留的 batch 数使连续 mmad 发射不撑爆 issue queue"的工程经验值(具体数字 8 文档无出处,属源码经验值);fullCopySize=64KB 是"单次搬运打满带宽"的粒度(168篇的官方经验值是 <b>16KB</b>,64KB 更保守,同为源码经验值)。</li>
|
||||
<li><b>iterBatchL1 的 8 截断</b>(L144-153):<code>if (8 × (单batch A+B 字节数) > 64KB)</code> 则 <code>iterBatchL1 = min(iterBatchL1, 8, CeilDiv(batchC, aicNum))</code>、iterBatchL0 同步钳到 8。<b>逻辑</b>:若 8 个 batch 的搬运量已超 64KB,说明单 batch 足够大、8 次搬运即可打满带宽——再增加驻留 batch 数只增加 issue queue 压力而无带宽收益;反之(单 batch 很小,8 个也填不满 64KB)不钳制,让 L1 尽量多装以摊薄搬运启动开销;</li>
|
||||
<li><b>B 侧 3D 非连续转置兼容</b>(L155-174,注释 <i>"特殊处理3D非连续场景"</i>):非连续转置 view 下只有<b>最内层 batch 维</b>(倒数第 3 维 innerBatch)保证物理连续,L1 迭代只能沿 innerBatch 且必须整除它——从 <code>min(innerBatch, iterBatchL1)</code> 向下找 innerBatch 的最大因子作为新 iterBatchL1,并把 innerBatch 写进 runInfo 供 kernel 算地址(与分支 4 的整除因子逻辑同构);</li>
|
||||
<li>基本块:l0CanLoadBatch 时 baseM/N/K = 完整对齐 M/N/K;否则同分支 4 的 4-case 收缩(本分支 A 侧用 aDtypeSize、B 侧用 bDtypeSize,分开计算);</li>
|
||||
<li>bias 约束 baseN ≤ btSize/2/4(注释 <i>"baseN满足C2大小"</i>,C2 即 bias 的 BT 空间);L0C 约束较大维;三维 16 对齐;</li>
|
||||
<li><b>fixpipe 优化开关 GetL0C2OutFlagDav3510</b>(L31-39):
|
||||
<pre><code>if (n×bDtype > 256B && n % (256B/bDtype) != 0 && aivNum == 2×aicNum)
|
||||
return ND_FIXPIPE_1_2; else return ON_THE_FLY;</code></pre>
|
||||
三条件同时满足才启用:① N 单行字节 > 256B;② N 非 256B 对齐(直接 fixpipe 写 GM 会产生非对齐写,056篇:非对齐向上取整补零、效率损失);③ 1C:2V 配比(vector 侧有足够算力消化 fixpipe 输出)。ND_FIXPIPE_1_2 的语义是"1 个 AIC 配 2 个 AIV 做 ND 对齐 fixpipe 后处理"(AIC↔AIV 用 MODE_4 CrossCoreFlag 逐 AIV 握手,087篇/054篇);<b>该枚举无任何 CANN 文档说明,语义以源码为准</b>。</li>
|
||||
</ol>
|
||||
<h4>④ kernel 实现</h4>
|
||||
<p>Cmct 四层组装 <code>KernelMatMulIterBatch<ProblemShape, BlockMmad, BlockEpilogue, BlockScheduler></code>:<code>BlockSchedulerIterBatchBuiltIn</code> 的调度粒度是<b>"一组 batch"</b>——<code>GetTileNum = ceil(b/iterBatchL1)</code>,<code>GetBlockCoord = {0,0,0, tileIdx×iterBatchL1}</code>,<code>GetIterBatchTuple = {iterBatchL1, iterBatchL0, 0, 0}</code>(L1 一次装 iterBatchL1 个 batch、L0 一次装 iterBatchL0 个);核映射为 batch 组轮询。ND_FIXPIPE_1_2 变体(MIX_AIC_1_2)经 <code>BlockEpilogueIterbatch</code> 由 2 个 AIV 配合 1 个 AIC 做后处理(支持 add/mul/relu 融合)。tilingKey:BASIC_LEVEL + SINGLE_BIAS_MODEL + l0C2Out 动态位。</p>
|
||||
</div>
|
||||
|
||||
<div class="branch">
|
||||
<h3 id="b6">5.7 ITER_BATCH(优先级 6)—— 大 batch 高阶 API 版 <span class="tag">AIC_ONLY</span></h3>
|
||||
<p class="src">tiling:arch35/batch_matmul_v3_iterbatch_tiling.cpp(143 行);kernel:arch35/batch_mat_mul_v3_iterbatch_kernel_advanced.h(BatchMatMulMultiBatchKernel)</p>
|
||||
<h4>① 目标 case 与分支 5 的关系</h4>
|
||||
<p>目标 case 与分支 5 相同(大 batch、batch 相等),区别在于<b>搬移抽象层</b>:分支 5 用基础 API/Cmct(host 手工管理 L1/L0 双缓冲),本分支用高阶 MatmulImpl API 的 <code>IterateBatch</code>(batch 维打进 MM 流水线,DB 由 API 内部管理)。官方文档依据(141篇):<i>"调用一次 IterateBatch,可以计算出多个 singleCoreM × singleCoreN 大小的 C 矩阵"</i>、<i>"当单次 Matmul 输入 shape 较小时,搬运开销占比大,IterateBatch 批量处理可有效提升带宽利用率"</i>。</p>
|
||||
<h4>② 进入条件的关键差异(IsCapable,cpp L71-126)</h4>
|
||||
<ul class="tight">
|
||||
<li>A、B <b>任一侧</b>非连续转置均拒绝(高阶版没有分支 5 的 3D 兼容逻辑,日志 <i>"Non-contiguous transpose does not support high-level IterBatch."</i>);</li>
|
||||
<li>★ <code>iterBatch = l1Size / inputSizeOneBatch > 1</code>(<b>不除 DB_SIZE</b>),再与每核 batch 数取 min。
|
||||
<b>为什么不除 2</b>(与分支 4/5 的 <code>l1Size/2</code> 对比):batch 维的双缓冲由<b>高阶 API 内部</b>实现(API 在 L1 内对 batch 段做 ping-pong),host 侧只需给出"理论可装多少 batch";DoOpTiling 里两处注释互证——<i>"L1 mm fullLoad, batch 2 DB"</i>(L1 深度 depthA1=stepKa×stepM 不含 batch 因子,batch 的 DB 独立处理)和 <i>"need align to 2 for db in api"</i>(iterBatch 向下对齐 2,保证 API 能拆两份做 DB)。而 basicapi 版 kernel 手工管理 L1 双缓冲,所以 host 必须先除 2 预留;</li>
|
||||
<li>L0 载不了多 batch 时同样 0.8 均衡率检查(此处 min 不含 L1——L1 可装性已由 iterBatch>1 保证)。</li>
|
||||
</ul>
|
||||
<h4>③ DoOpTiling 步骤(cpp L27-69)</h4>
|
||||
<ol class="tight">
|
||||
<li>ResetBase + CalL1Tiling 出初始 baseM/baseN;<code>singleCoreM/N/K = 完整 m/n/k</code>(单核内 M/N 不切分,<b>并行维度只有 batch</b>);</li>
|
||||
<li>多 batch 使能判定:<code>if (baseM ≥ m && baseN ≥ n) { baseM/N = 对齐整块; isEnableMultiBatch = true; }</code>——L1 基本块能完整覆盖 MN 时才允许多 batch 驻留 L0C;</li>
|
||||
<li><code>baseK = min(l0A/2/baseM/dtype, l0B/2/baseN/dtype)</code> 向下 16 对齐;</li>
|
||||
<li>★ <b>singleCoreK 减半条件</b>(L43-45):
|
||||
<pre><code>singleCoreK = (baseK < singleCoreK || iterBatch ≤ 4) ? singleCoreK / 2 : singleCoreK;
|
||||
// 注释:4 avoid issueque</code></pre>
|
||||
两个触发条件:① L0 装不下整条 K(baseK<singleCoreK),K 本就要分多次,先把单核 K 减半配合 L1 DB 流水;② iterBatch ≤ 4:batch 迭代次数太少时,cube 在 batch 循环间的指令发射不足以填满 issue queue,<b>减半 K 让 K 循环与 batch 循环重叠、保持 issue queue 有指令可发</b>。阈值 4 与分支 5 的 mmadCount=8 同源(8÷DB2=4 的直觉),均为源码经验值(056篇仅有队列深度 32 的机制描述);</li>
|
||||
<li>step/depth:<code>stepKa = stepKb = CeilDiv(singleCoreK, baseK)</code>;<code>depthA1 = stepKa×stepM</code>(注释 <i>"L1 mm fullLoad, batch 2 DB"</i>);</li>
|
||||
<li><code>iterBatch = FloorAlign(iterBatch, 2)</code>(API 内部 DB 要求偶数);</li>
|
||||
<li>★ <b>batchOutNum</b>(L0C 一次驻留的输出 batch 数):
|
||||
<pre><code>batchOutNum = isEnableMultiBatch ? min( l0CSize / (baseM×baseN×dbL0C×4B), iterBatch ) : 1;
|
||||
if (batchOutNum == iterBatch) batchOutNum = iterBatch >> 1; // 恰好相等时再减半</code></pre>
|
||||
<b>为什么相等要减半</b>:若 L0C 刚好能把本轮全部 batch 存满,就没有余量做"一半写出、一半继续算"的乒乓;减半后一半容量在计算、一半容量在 fixpipe 搬出,实现输出与计算的流水重叠。这里直接吃到 <b>950 L0C 增大到 256KB 的红利</b>(白皮书 §4.1.1 明言"更大的 L0C Buffer 提供更灵活的 Tiling 策略"——256KB 让更多 batch 的输出能驻留 L0C)。</li>
|
||||
</ol>
|
||||
<h4>④ kernel 实现</h4>
|
||||
<p><code>BatchMatMulMultiBatchKernel</code> + <code>BatchMatMulMultiBatchBaseBlock</code>:主循环 <code>LoopTimes = ceil(batchC / (iterBatch×useCoreNum))</code>,每核每轮处理 iterBatch 个 batch;核心是<b>一次 <code>mm_.IterateBatch(cGlobal, 0,0,0, singleASize, singleBSize)</code> 让 MatmulImpl 内部完成多个 batch 的连续 mmad</b>——batch 维打进 MM 流水线,L0C 按 batchOutNum 攒多 batch 再一次写出(<code>SetNBatchOutNum</code>,该接口 CANN 9.0/9.2 文档均未收录,属未文档化高阶接口,语义以源码为准),相对普通分支"一块一 Iterate/GetTensorC"显著减少指令发射与同步开销。<code>GetMultiBatchInfo(loopIndex)</code> 处理尾轮均衡(前几个核多算一个 batch)。tilingKey 只设 trans + SINGLE_BIAS_MODEL(apiLevel 由 key 默认值决定,落 HIGH_LEVEL)。</p>
|
||||
</div>
|
||||
<div class="branch">
|
||||
<h3 id="b7">5.8 AL1 / BL1_FULL_LOAD_BASIC(优先级 7/8)—— 单边无 batch 的 L1 全载 <span class="tag">AIC_ONLY</span></h3>
|
||||
<p class="src">tiling:arch35/batch_matmul_v3_asw_al1_full_load_basic_tiling.cpp / ..._bl1_...;公共实现:mat_mul_v3/arch35/matmul_v3_basic_aswt_tiling.cpp(DoAL1FullLoad/DoBL1FullLoad);kernel:arch35/batch_mat_mul_v3_asw_{al1,bl1}_full_load_kernel_advanced.h</p>
|
||||
<h4>① 目标 case</h4>
|
||||
<p><b>A 无 batch(batchA=1)且 M≤256</b>、B 有 batch 且总量大(AL1);镜像地 B 无 batch 且 N≤256(BL1)。典型:小 M 激活 / 小 N 权重(MoE 投影层)被大 batch 对端乘。"全载(full load)"的含义:<b>小的一侧矩阵完整常驻 L1,整个 kernel 只从 GM 搬一次</b>,之后所有 batch、所有 N(或 M)块的计算都从 L1 复用,彻底消除该侧的重复 MTE 搬运。</p>
|
||||
<h4>② 进入条件(IsCapable,AL1 为例,cpp L31-72,逐条)</h4>
|
||||
<table>
|
||||
<tr><th>#</th><th>条件</th><th>取值依据(注释原文 / 分析)</th></tr>
|
||||
<tr><td>1</td><td>非连续转置 → 拒绝</td><td>全载要求 GM 一次性 Nd2Nz 大整块搬入,view 转置 stride 不支持</td></tr>
|
||||
<tr><td>2</td><td>dtype 限 fp16/bf16(A/B),C 可 fp16/bf16/fp32;非支持类型且 B 为 NZ → 报错</td><td>基础 API 全载模板的类型约束(fp32 输入只接受 ND)</td></tr>
|
||||
<tr><td>3</td><td><code>batchA > 1</code> → 拒绝</td><td>注释:<i>"matrix A should not have batch when AL1FullLoad"</i>——A 有 batch 时每核要驻留多份 A,"全载"语义不成立。注意:batchB==1 且 A 不转置的情形已在更早的 <code>MergeBatchAndMAxis()</code> 被折叠成 MM(见 §4),走到这里的是 B 有 batch 的真 BMM</td></tr>
|
||||
<tr><td>4</td><td><code>m > 256</code> → 拒绝</td><td>(源码注释 <i>"m should be larger than 256"</i> 与实际逻辑相反,系注释笔误,实际要求 m≤256)。<b>为什么 256</b>:m≤256 时 A 的 M 向恰好一个 baseM(256) 覆盖,stepM=1,单核 M 向无循环——A 全载后每核只需加载一次 A 且各核间 A 完全共享,全载收益最大</td></tr>
|
||||
<tr><td>5</td><td>★ "值得全载"门槛:<code>alignMatBSize < l1Size×aicNum && batchB×CeilDiv(n,256) < 4×aicNum</code> → 拒绝</td><td>注释:<i>"each core needs to loop at least 4 batch for MatB"</i>、<i>"总数据量大于2轮 batch大于4轮"</i>。两个子条件:<br><b>① B 总量 < 全核 L1 之和</b>:B 小到可摊到所有核各存一份、整体只搬一遍,A 全载没有搬运量优势;<br><b>② 每核循环轮数 < 4</b>:A 全载的收益是"A 加载一次、B 流水多轮复用",每核 <4 轮则复用收益抵不过 A 驻留占用的 L1 空间(B 的 stepK/buffer 会被压小)。<br><b>取反语义</b>:拒绝条件是"①且②同时成立",即进入全载 ⟺ <b>B 足够大(必然被反复搬运)或 轮数足够多(每核≥4 轮)</b>,二者满足其一即值得</td></tr>
|
||||
<tr><td>6</td><td><code>alignMatASize × 2 > l1Size</code> → 拒绝</td><td>全载可行性:A 全量 16 对齐数据 ×2(L1 上 A 区开 ping-pong 双 buffer 保持流水)≤ 512KB L1,否则物理装不下</td></tr>
|
||||
</table>
|
||||
<h4>③ DoAL1FullLoad() 完整步骤(matmul_v3_basic_aswt_tiling.cpp L138-210)</h4>
|
||||
<ol class="tight">
|
||||
<li>重置全载负载均衡(tailSplitCnt=1 等,注释 <i>"全载模板需重置负载均衡计算"</i>);</li>
|
||||
<li><code>remainL1Size = l1Size − (aL1Size + biasSize)</code> —— A 全载驻留后剩余的 L1;</li>
|
||||
<li>★ <b>maxBaseN 由两个上限卡出</b>:<code>maxBaseNWithL1 = remainL1 / (baseK×dtype×2)</code>(剩余 L1 给 B 开 DB,每份 baseN×baseK)与 <code>maxBaseNWithL0cDb = l0CSize / (baseM×4B×2)</code>(L0C 双缓冲下 fp32 输出块须装得下),取小并 16 对齐;</li>
|
||||
<li>★ <b>balanceBaseN = maxBaseN(BMM 特有)</b>:单 MatMul 下 balanceBaseN = CeilDiv(n, aicNum)(把 N 均分给各核保均衡);<b>BMM 下负载均衡已由 batch 维提供</b>(总任务 = batch×mCore×nCore,并行度充足),无需再均分 N,直接取硬件允许的最大 baseN 最大化单块计算/搬运效率;</li>
|
||||
<li><code>baseN = min(原baseN, maxBaseN, balanceBaseN)</code>;N 为内轴且 baseN>128B/dtype 时向下 128B 对齐(注释 <i>"N内轴时满足128B对齐"</i>,056篇内轴对齐粒度);</li>
|
||||
<li><b>stepK 上限</b>:<code>maxStepK = min(CeilDiv(k,baseK), remainL1/(baseK×baseN×2×dtype), 4)</code>——注释 <i>"stepK最大不超过4"</i>:限制 L1 上 B 的流水级数,级数过多收益递减且挤压 buffer(4 为源码经验值);</li>
|
||||
<li>★ <b>baseK 翻倍重试</b>(L169-178):当 B 为 K 内轴(isBTrans)且 stepK 被 buffer 上限卡住、且 <code>maxStepK×baseK</code> 不满足 256B 对齐、且 baseK 翻倍后 L0A 装得下(<code>(baseK×2)×baseM×dtype×2 ≤ l0ASize</code>)时:<code>baseK <<= 1</code> 重算。注释:<i>"B矩阵K为内轴,k_bl1不满足256B对齐,尝试baseK放大一倍,提升B矩阵搬运效率"</i>——K 内轴 256B 对齐才能发挥搬运带宽(056篇/950白皮书 §4.3.2 L2 128B Sector);</li>
|
||||
<li><b>stepK 枚举</b>(1→maxStepK),取第一个同时满足的:<code>baseN×baseK×stepK×dtype ≥ 48KB</code>(L1_SINGLE_SIZE_LIMIT,注释 <i>"L1搬运量约束"</i>——单次 L1 搬运量阈值;<b>注意</b>:168篇的官方经验值是"单次搬运 16KB 以上发挥带宽最佳",48KB 为源码更保守的经验值)且 K 内轴时 <code>kL1 % (256B/dtype) == 0</code>(注释 <i>"K内轴时约束kL1 256B对齐,发挥带宽能力"</i>);都不满足则放大到 maxStepK 兜底;</li>
|
||||
<li><b>depth</b>:<code>depthA1 = stepM × stepKa</code>(A 全载:深度 = 整个 A 的 M×K 块数,即 A 的所有块都在 L1 有槽位)、<code>depthB1 = stepKb × 2</code>(B 流水 DB);</li>
|
||||
<li><code>singleCoreM = m</code>(A 全载单核 M 向全覆盖)、<code>singleCoreN = baseN</code>;</li>
|
||||
<li><b>l1BufferNum = 4 或 2</b>:<code>bL1TensorSize×4 + aL1TensorSize ≤ l1Size</code> 则 B 区开 4 buffer(注释 <i>"l1开2db后依然只使用了一半的空间,则开启4 db。该字段仅在基础api场景生效"</i>)。<b>4 buffer 依据</b>:文档无"4 buffer"直接依据(高阶 API 框架 db 只取 1/2;类似思想见 208篇 NBuffer33 模板的 3 份错峰:MTE2 Bound 时错开搬运流水、减少单次搬运量);</li>
|
||||
<li><code>dbL0C = baseM×baseN×4B×2 ≤ l0CSize ? 2 : 1</code>;</li>
|
||||
<li>★ <code>usedCoreNum = min(CeilDiv(n, baseN) × batchC, aicNum)</code> —— 并行任务 = N 向条带数 × batchC,batch 维提供主要并行度;</li>
|
||||
<li><b>CalcTailBasicBlockAL1Full 尾块切分</b>(L105-120):一轮任务 = nCnt 个 N 条带,最后一轮只有 <code>tailCnt = nCnt % aicNum</code> 个核有活;把每个尾块沿 N 向再切成 tailInfo.nCnt 份,使尾轮工作核数 <code>tailCnt × nCnt</code> 逼近 aicNum(条件 <code>(nCnt+1)×tailCnt ≤ aicNum</code>);若 N 是 B 内轴还要求切分后每份内轴字节 > 128B(避免内轴过短搬运效率崩塌)。</li>
|
||||
</ol>
|
||||
<h4>④ 为什么全载能省搬运:官方定量例证(192篇)</h4>
|
||||
<p>192篇(L1 长驻优化)给出精确核算:L1=512K,左矩阵 992K、右矩阵 16K(fp16),无法同时全载。<b>反例</b>(不长驻):A 分 2 块、B 分 2 块,加载顺序"A1→B1→B2→A2→B1→B2",总搬运 <b>6 次</b>(A×2 + B×4,B 被每个 A 块周期重复搬);<b>正例</b>(16K 的 B 全载长驻):循环内只搬 A1、A2,共 <b>3 次</b>(B×1 + A×2)。BMM 把这一收益再放大 batch 倍——被全载侧的复用次数 ×= batchB,这就是为什么条件 5 用"B 总量 vs 全核 L1 之和"和"每核循环 ≥4 轮"两个尺度衡量复用收益。对带宽相对弱的 950PR(1.6TB/s,DT 为 4TB/s),消除重复搬运的价值更高。</p>
|
||||
<h4>⑤ kernel 实现</h4>
|
||||
<p><code>BatchMatMulAswAL1FullLoadKernel</code>:关键类型重定义 <code>A_TYPE_NEW = MatmulL1GmType<TPosition::TSCM, ...></code>——告诉 MatmulImpl 该操作数<b>源在 L1(TSCM) 而非 GM</b>。Process 仅两步:① <code>AswAL1FullLoadKernelCopyInA1()</code> 一次性 Nd2Nz DataCopy 把本核 singleCoreM×Ka 的 A 切片搬入 L1(TQue 深度 1、无 DB——只搬一次无需双缓冲);② 主循环与 ASW 相同(复用同一套滑窗 swizzle),但 <code>mm_.SetTensorA(al1Local, ...)</code> 直接给 L1 地址,并 <code>SetOrgShape(singleCoreM, N, Ka)</code>。与普通 ASW 的本质差异:<b>A 的 GM→L1 搬移次数从"每个基本块一次"降为"整个 kernel 一次"</b>。BL1 完全镜像(B 全载、CalCopyBL1 Nd2Nz/Nz2Nz 区分 GM 格式)。tilingKey:Model=BASIC + <b>FullLoad=A_FULL_LOAD / B_FULL_LOAD</b>。</p>
|
||||
<div class="figbox">
|
||||
<svg viewBox="0 0 980 240" xmlns="http://www.w3.org/2000/svg" font-family="PingFang SC,Microsoft YaHei,sans-serif" font-size="12">
|
||||
<defs><marker id="ar5" markerWidth="8" markerHeight="8" refX="7" refY="3" orient="auto"><path d="M0,0 L7,3 L0,6 Z" fill="#0b6bcb"/></marker></defs>
|
||||
<text x="20" y="22" font-weight="bold" fill="#0b3d73">AL1 全载:A 一次性搬入 L1 常驻,B 按块流水;A 的 GM 搬运次数 = 1</text>
|
||||
<rect x="20" y="45" width="110" height="60" rx="6" fill="#fdeee2" stroke="#b45309"/>
|
||||
<text x="75" y="70" text-anchor="middle">GM:A(小)</text><text x="75" y="88" text-anchor="middle" font-size="10" fill="#5f6b7a">batchA=1,M≤256</text>
|
||||
<rect x="20" y="130" width="110" height="60" rx="6" fill="#fdeee2" stroke="#b45309"/>
|
||||
<text x="75" y="155" text-anchor="middle">GM:B(大)</text><text x="75" y="173" text-anchor="middle" font-size="10" fill="#5f6b7a">batchB 大、N 大</text>
|
||||
<rect x="200" y="40" width="200" height="160" rx="8" fill="#eaf2fb" stroke="#0b6bcb" stroke-width="1.5"/>
|
||||
<text x="300" y="60" text-anchor="middle" font-weight="bold">L1 512KB</text>
|
||||
<rect x="215" y="70" width="170" height="55" rx="5" fill="#e3ecf7" stroke="#0b3d73"/>
|
||||
<text x="300" y="90" text-anchor="middle">A 全载区(×2 DB)</text>
|
||||
<text x="300" y="107" text-anchor="middle" font-size="10" fill="#5f6b7a">depthA1 = stepM×stepKa(整个 A)</text>
|
||||
<rect x="215" y="135" width="170" height="55" rx="5" fill="#dbe7f5" stroke="#0b6bcb"/>
|
||||
<text x="300" y="155" text-anchor="middle">B 流水区(2/4 buffer)</text>
|
||||
<text x="300" y="172" text-anchor="middle" font-size="10" fill="#5f6b7a">stepKb ≤ 4,单次 ≥48KB,K内轴256B对齐</text>
|
||||
<line x1="130" y1="75" x2="196" y2="90" stroke="#0b6bcb" stroke-width="1.6" marker-end="url(#ar5)"/>
|
||||
<text x="140" y="70" font-size="10" fill="#c0392b">仅 1 次</text>
|
||||
<line x1="130" y1="160" x2="196" y2="162" stroke="#0b6bcb" stroke-width="1.6" marker-end="url(#ar5)"/>
|
||||
<text x="135" y="150" font-size="10" fill="#5f6b7a">逐块流水(N 向 × batchB 轮)</text>
|
||||
<rect x="470" y="70" width="120" height="100" rx="6" fill="#eaf2fb" stroke="#0b6bcb"/>
|
||||
<text x="530" y="105" text-anchor="middle">L0A/L0B 64KB</text>
|
||||
<text x="530" y="125" text-anchor="middle" font-size="10" fill="#5f6b7a">从 L1 重载(非 GM)</text>
|
||||
<line x1="400" y1="120" x2="466" y2="120" stroke="#0b6bcb" stroke-width="1.6" marker-end="url(#ar5)"/>
|
||||
<rect x="630" y="70" width="100" height="100" rx="6" fill="#e3ecf7" stroke="#0b3d73"/>
|
||||
<text x="680" y="115" text-anchor="middle">Cube</text>
|
||||
<text x="680" y="135" text-anchor="middle" font-size="10" fill="#5f6b7a">mmad 流水</text>
|
||||
<line x1="590" y1="120" x2="626" y2="120" stroke="#0b6bcb" stroke-width="1.6" marker-end="url(#ar5)"/>
|
||||
<rect x="770" y="70" width="110" height="100" rx="6" fill="#eaf2fb" stroke="#0b6bcb"/>
|
||||
<text x="825" y="105" text-anchor="middle">L0C 256KB</text>
|
||||
<text x="825" y="125" text-anchor="middle" font-size="10" fill="#5f6b7a">maxBaseN 受</text>
|
||||
<text x="825" y="140" text-anchor="middle" font-size="10" fill="#5f6b7a">l0C/(baseM×4×2) 约束</text>
|
||||
<line x1="730" y1="120" x2="766" y2="120" stroke="#0b6bcb" stroke-width="1.6" marker-end="url(#ar5)"/>
|
||||
<text x="20" y="225" font-size="11" fill="#5f6b7a">usedCoreNum = min(N向条带数 × batchC, aicNum);尾轮 N 条带沿 N 再切分逼近满载(CalcTailBasicBlockAL1Full)</text>
|
||||
</svg>
|
||||
<div class="figcap">图 5-5 AL1 全载数据通路(BL1 镜像:B 常驻、A 流水)</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="branch">
|
||||
<h3 id="b8">5.9 ASW_BASIC(优先级 9)与 BASE(999)—— 通用路径:cubeBound 寻优 + 自适应滑窗 <span class="tag">AIC_ONLY</span></h3>
|
||||
<p class="src">tiling:arch35/batch_matmul_v3_asw_basic_tiling.cpp + batch_matmul_v3_asw_tiling.cpp;公共:mat_mul_v3/arch35/matmul_v3_tiling_helper.cpp(ResetBase/GetRebalanceBlock/CalL1TilingDefault/GetAswWindowLen);kernel:batch_mat_mul_v3_asw_{kernel,block}_advanced.h</p>
|
||||
<h4>① 目标 case</h4>
|
||||
<p>前面 9 个特判都不命中的<b>通用 case</b>:batch 量适中、M/N 够大、无广播或复杂广播。ASW_BASIC 是带约束的通用路径(基础/张量 API + cubeBound 模型寻优),BASE(999) 是无条件兜底(高阶 API 默认参数)。ASW = <b>Adaptive Sliding Window(自适应滑窗)</b>,命名佐证见 ops-nn 仓 dual_level_quant_batch_matmul 的 <code>DualLevelQuantBatchMatmulTilingASW</code>(adaptive_sliding_window);滑窗指 kernel 侧块到核映射的"滑窗+蛇形"swizzle(详见第 6 章)。</p>
|
||||
<h4>② ASW_BASIC 进入条件(cpp L31-57)</h4>
|
||||
<ul class="tight">
|
||||
<li><b>A、B 的非连续转置状态必须一致</b>(都连续或都非连续;混合则拒绝——注释 <i>"ASW Basic only supports AB non-contiguous transpose"</i>,混合场景只留给 BASE 的高阶 API);</li>
|
||||
<li>batch 完全相等(不等即广播——广播由 BASE 的 BROADCAST_BATCH_MODEL 或分支 4 承接);batchBias ≤ 1;dtype 同分支 7。</li>
|
||||
</ul>
|
||||
<h4>③ DoOpTiling 三步走:ResetBase → GetRebalanceBlock → CalL1Tiling</h4>
|
||||
<p><b>第 1 步 ResetBase(DAV_3510 默认值及依据)</b>:baseM=256、baseN=256(注释 <i>"256 is better base"</i>——DAV_3510 cube 以 256×256×(128B/dtype) 为最优基本块)、baseK=128B/dtype(L0A/L0B 单次载入内轴的最小高效粒度,056篇 128B)、stepM=stepN=1、iterateOrder=列优先、dbL0C=1、singleCoreK=全 K。</p>
|
||||
<p><b>第 2 步 GetRebalanceBlock:cubeBound 解析模型寻优 baseM/baseN</b>(helper.cpp L387-492)——这是通用路径的"基本块寻优器":</p>
|
||||
<ol class="tight">
|
||||
<li><b>平台指标</b>:hbmBW/l2BW 由频率×核数×平台速率算出;<code>computePower = 单核算力 × aicNum</code>(fp32 时 ÷16);</li>
|
||||
<li>★ <b>cubeBoundEdge 公式</b>(L418-419):
|
||||
<pre><code>cubeBoundEdge = l2BW/computePower // ① 理论阈值:L2 供数速率 ÷ cube 耗数速率
|
||||
+ l2CacheUsage × (1 − l2BW/hbmBW) × cmr // ② L2 装不下工作集时的访存惩罚
|
||||
− (1 + l2BW/hbmBW) / kValue // ③ K 向复用修正
|
||||
cmr = (m+n)/(m×n) l2CacheUsage = max(batch×(m+n)×k×dtype / l2Size, 1.0)</code></pre>
|
||||
逐项含义:cube bound 的判据是"每输出块的相对搬运量 1/baseM+1/baseN ≤ edge";①是纯 L2 供数能力下的理论 edge;②当工作集超 L2(l2CacheUsage>1)时按 HBM 与 L2 带宽差抬高 edge(更容易判成 memory bound,倾向更大基本块);③K 越大 A/B 复用越充分,edge 越小(更容易 compute bound)。进入枚举前 edge 先乘 <b>CUBE_BOUND_RATIO=0.85</b> 预留 15% 余量(防实测达不到理论带宽);</li>
|
||||
<li><b>候选空间与对齐单位</b>:候选上界由 L0 buffer(l0A/2/minKL0 等)、bias table、K 内轴对齐(BMM 固定内轴 512B 对齐,注释 <i>"目前batchmatmul固定内轴512B对齐"</i>;memory bound 且非 batch 为 256B)卡出;<code>fixpBoundEdge = m×n×hbmBW / ((m+n)×l2BW)</code>,k < fixpBoundEdge(fixpipe bound)时 baseN 强制 256B 对齐(注释 <i>"fixpipe bound场景下,要求baseN是256B对齐,发挥搬出带宽"</i>);</li>
|
||||
<li><b>枚举与剪枝</b>:curBaseM 从上界按对齐单位递减、内层 curBaseN 递减;剪枝① balanceRate ≥ 0.9(balanceRateEdge,注释 <i>"用于判断是否取得最优解,进行减枝"</i>)且候选计算访存比既差于当前最优又大于 edge → 跳过;剪枝② fp32 cubeBound 且多轮时 baseM/N < 64(FP32_MIN_BASE_BLOCK)跳过(fp32 小块多轮效率差);</li>
|
||||
<li><b>评分</b>:cubeBoundCond(满足 cubebound 且更均衡)优先;否则 balanceCond 以 <code>cubeBoundParam/balanceRate</code>(单位负载均衡率的搬运代价)为综合分,比值差 < EPSILON(1e-9) 时取更均衡者;命中 cubeBoundCond 后 <b>edge 收紧为当前 param</b>,形成单调收敛;</li>
|
||||
<li><b>尾块感知的 balanceRate</b>(GetBalanceRateWithTail,L223-246):<code>totalRound = batch×mCnt×nCnt</code>,主轮数 mainRound = ⌈totalRound/核数⌉−1,尾轮按二维 √ 拆分估计(totalTailSplit 按 √ 开方分两维);尾块拆分后 <code>baseM×baseN/split < 4096</code>(MIN_TATL_BLOCK_SIZE)则无拆分收益;<b>BMM(batchInfo≠)直接用均值/最忙核的简单比率</b>——batch 维已摊薄尾块效应;</li>
|
||||
<li>收尾 GetBaseK:k ≤ maxBaseK(l0A/2/dtype/max(baseM,baseN))则 K 全载;否则按 256B/128B/64B/32B/16 递减选最大可行对齐;<code>usedCoreNum = min(batch×mCore×nCore, aicNum)</code>;dbL0C/ubDB 按容量置 2/1。</li>
|
||||
</ol>
|
||||
<p><b>第 3 步 CalL1Tiling(CalL1TilingDefault,L45-80)</b>:<code>maxStepK = min(CeilDiv(k,baseK), 8)</code>(注释 <i>"Shape约束 && issue queue约束"</i>——stepK≤8 又是 issue queue 经验值);K 内轴对齐单位 512B/dtype(BMM 固定);stepK 从 1 枚举,容量约束 <code>(aL1+bL1)×2 ≤ totalL1</code> 且 <code>max(aL1,bL1)×4 ≤ l1Size</code>(单边开 DB 不得超 L1 一半);更新条件优先级:<b>首个可行解 > 256B 对齐 > 单次搬运 ≥48KB</b>("优先对齐,对齐不了就把单次搬运量做大");得 stepKa=stepKb、depthA1=depthB1=stepK×2。</p>
|
||||
<h4>④ 收尾:l1BufferNum 与 apiLevel</h4>
|
||||
<ul class="tight">
|
||||
<li><code>l1BufferNum = abL1TensorSize×4 ≤ l1Size ? 4 : 2</code>(注释 <i>"l1开2db后依然只使用了一半的空间,则开启4 db"</i>);</li>
|
||||
<li><b>CheckTensorApiSupport</b>:fp32 非 hf32、双 ND、连续、非全载且 <code>k > 阈值</code>(K>2^28 时阈值 8192,否则 1024;FP32_K_SWITCH_BASE/FP32_SPLIT_K_BASE1/2)判为 splitK 走 BASIC_LEVEL;纯 BatchMatMulV3 节点、连续、非 splitK、未禁用 tensor api → TENSOR_LEVEL(注释 <i>"非切K且连续场景下才允许切换tensor api实现"</i>)。fp32 大 K 单核切 K 的依据:fp32 累加对 K 长度敏感,单核内把 K 分段累加(splitKRound)可降低长 K 的累加误差与 L0C 压力。</li>
|
||||
</ul>
|
||||
<h4>⑤ BASE(999) 兜底</h4>
|
||||
<p>IsCapable 恒 true;DoOpTiling 仅 ResetBase + CalL1Tiling + ubDB 判定——<b>不做 GetRebalanceBlock</b>(用默认 256/256/128B 基块,最保守);apiLevel 兜底为 HIGH_LEVEL(非 BASIC);<code>batchModel = (apiLevel==TENSOR_LEVEL) ? BROADCAST_BATCH_MODEL : BATCH_MODEL</code>——TENSOR_LEVEL 时高阶 API 内建任意 batch 广播支持(枚举注释 <i>"只用于BatchMatMul广播场景"</i>),这保证<b>任意广播形态的 case 都不会漏接</b>(分支 4 只接"单边单轴",其余广播全部汇到这里)。</p>
|
||||
<h4>⑥ ASW 滑窗窗长:GetAswWindowLen(matmul_v3_base_tiling_advanced.h L316-325)</h4>
|
||||
<pre><code>sqrtNum = sqrt(aicNum);
|
||||
for (factor = sqrtNum; factor ≥ 1; --factor)
|
||||
if (aicNum % factor == 0) return factor; // 不超过 √aicNum 的最大因子</code></pre>
|
||||
<p><b>为什么这样取</b>:滑窗是 M 向的分组宽度(kernel 侧 mainWindow = min(aswWindowLen, mCnt),窗内 N 向蛇形扫描,见 §6.1)。窗口越接近正方形,同窗口内 A 行块与 B 列块的 L2 足迹越小、并发核访问局部性越好——√aicNum 是"方形"的直觉;取 aicNum 的因子保证整窗被核数均分、窗口边界不碎。例:aicNum=32 → 窗长 4;25 → 5;24 → 4;质数核数退化为 1(退化为普通行优先)。窗长写入 tilingData.aswWindowLen 供 kernel 使用。</p>
|
||||
</div>
|
||||
|
||||
<h3>5.10 参数取值依据总表(含依据来源分级)</h3>
|
||||
<table>
|
||||
<tr><th>参数 / 阈值</th><th>出现位置</th><th>取值</th><th>依据来源</th></tr>
|
||||
<tr><td>fractal 16 对齐(baseM/N/K)</td><td>全部分支</td><td>16</td><td><b>文档</b>:054篇 cube 一拍 16×16×16;064篇 一个分形恰好 512B</td></tr>
|
||||
<tr><td>L0A/L0B 512B 对齐</td><td>全部分支</td><td>512B</td><td><b>文档</b>:054篇对齐要求;064篇 分形=512B</td></tr>
|
||||
<tr><td>内轴 128B / 256B / 512B 对齐</td><td>baseK、stepK、fixpipe 判定</td><td>128/256/512B</td><td><b>文档</b>:056篇"搬运拆成 128B/256B/512B,非对齐向上取整";950白皮书 §4.3.2 L2 512B CacheLine/128B Sector;源码注释"BMM 固定内轴 512B 对齐"</td></tr>
|
||||
<tr><td>DB ×2</td><td>所有容量公式</td><td>2</td><td><b>文档</b>:067篇 InitBuffer 块数=2 的乒乓机制;624篇 db 只取 1/2</td></tr>
|
||||
<tr><td>batchC ≥ 128(TO_MUL)</td><td>分支1</td><td>2×aivNum</td><td><b>源码注释</b>:"batch数大于等于128(AIV CoreNum *2) 才能开pingpong"</td></tr>
|
||||
<tr><td>N>256B/dtype(TO_MUL)</td><td>分支1</td><td>256B</td><td><b>源码注释</b> + 054篇 Vector 每拍 256B</td></tr>
|
||||
<tr><td>K ≥ max(8192, aic×256B/dtype)(StreamK)</td><td>分支2</td><td>8192 / 256B</td><td><b>逻辑推断</b>:每核 K 段至少一个 256B 内轴搬运块 + 绝对收益门槛</td></tr>
|
||||
<tr><td>fp32 K ≤ 200 万(StreamK)</td><td>分支2</td><td>2×10⁶</td><td><b>源码注释</b>(binary accumulation 精度),具体数值无文档依据</td></tr>
|
||||
<tr><td>batchC×mCnt×nCnt ≤ aicNum/2(StreamK)</td><td>分支2</td><td>1/2</td><td><b>逻辑推断</b>:MN 并行度不足一半核数才切 K;同时保证 kCnt≥2</td></tr>
|
||||
<tr><td>MIN_BATCH_L0 = 4(mergebatch)</td><td>分支3</td><td>4</td><td><b>源码注释</b>:"each aic should process at least 4 batchs"</td></tr>
|
||||
<tr><td>baseK ≤ 64(mergebatch)</td><td>分支3</td><td>64</td><td><b>源码注释</b>("threshold of basek is 64")+ 推断:压 K 深度换 L0B 合并度</td></tr>
|
||||
<tr><td>iterBatchL1 ≥ 2(iterbatch 系)</td><td>分支4/5/6</td><td>2</td><td><b>源码注释</b>:广播/驻留收益的前提是至少 2 batch 复用</td></tr>
|
||||
<tr><td>mmadCount = 8 / iterBatch ≤ 4</td><td>分支5/6</td><td>8 / 4</td><td><b>源码经验值</b>:注释 "cube count which will cause issuequene";文档仅有 056篇"MTE1/MMAD 队列深度 32"的机制描述,8/4 无官方出处</td></tr>
|
||||
<tr><td>fullCopySize = 64KB / L1_SINGLE_SIZE_LIMIT = 48KB</td><td>分支5、全载、CalL1Tiling</td><td>64KB / 48KB</td><td><b>源码经验值</b>:注释 "moving once which can use full of bandwith";168篇官方经验值为 16KB(更宽松)</td></tr>
|
||||
<tr><td>均衡率 0.8 / balanceRateEdge 0.9 / CUBE_BOUND_RATIO 0.85</td><td>分支4/5/6、GetRebalanceBlock</td><td>0.8 / 0.9 / 0.85</td><td><b>源码经验值</b>(注释仅说明用途,无推导)</td></tr>
|
||||
<tr><td>m/n ≤ 256(全载)</td><td>分支7/8</td><td>256 = baseM/baseN</td><td><b>逻辑</b>:单 base 块覆盖全载轴,stepM/stepN=1</td></tr>
|
||||
<tr><td>"值得全载":总量 ≥ L1×aic 或 轮数 ≥ 4/核</td><td>分支7/8</td><td>4 轮</td><td><b>源码注释</b> + 192篇 6→3 次搬运的复用收益模型</td></tr>
|
||||
<tr><td>全载数据 ×2 ≤ L1</td><td>分支7/8</td><td>×2</td><td><b>逻辑</b>:L1 全载区开 ping-pong 双 buffer</td></tr>
|
||||
<tr><td>stepK ≤ 4(全载)/ ≤ 8(通用)</td><td>分支7/8/9</td><td>4 / 8</td><td><b>源码注释</b>:"stepK最大不超过4"、"issue queue约束"(8);4 无进一步文档依据</td></tr>
|
||||
<tr><td>ASW 窗长 = aicNum 的 ≤√aicNum 最大因子</td><td>分支9/BASE</td><td>√aicNum</td><td><b>逻辑推断</b>:方形窗 L2 足迹最小 + 因子保证整窗均分</td></tr>
|
||||
<tr><td>workspace 20MB RPC</td><td>基类</td><td>20MB</td><td><b>源码注释</b>:"20MB workspace for RPC"(核间通信区)</td></tr>
|
||||
<tr><td>ND_FIXPIPE_1_1/1_2 启用条件</td><td>分支5、StreamK、helper</td><td>N 非对齐 + 1C:2V</td><td><b>源码</b>(文档无该枚举说明);机制支撑:054/087篇 fixpipe 随路 NZ2ND、CrossCoreFlag MODE_4</td></tr>
|
||||
</table>
|
||||
<div class="warn"><b>依据分级说明</b>:标<b>文档</b>的参数可在知识库 CANN 文档/950 白皮书中找到直接依据;标<b>源码注释</b>的参数源码注释给出了设计意图但无官方文档佐证;标<b>源码经验值/逻辑推断</b>的参数是调优得出的工程阈值(如 8、4、0.8、0.85、48KB/64KB),对 950PR 满配/降配不同档位未必是最优点,做具体 case 的最优方案决策时建议对阈值两侧的边界 case 实测复核。</div>
|
||||
|
||||
<!-- ============================== 6 ============================== -->
|
||||
<h2 id="sec6">6. Swizzle 专题:核间分块执行顺序的三种实现机制</h2>
|
||||
<p>BMM v3 中"分块执行顺序 / 哪个分块在哪个核上做"由三套机制实现,分别服务于不同的分支族:</p>
|
||||
|
||||
<h3>6.1 ASW 自适应滑窗 + 蛇形(ASW 系 / 全载系 kernel)</h3>
|
||||
<p>核心代码在 <code>BatchMatMulAswBlock::UpdateBasicIndex()</code>(arch35/batch_mat_mul_v3_asw_block_advanced.h):</p>
|
||||
<pre><code>params_.index = newBlockIdx + roundIdx * usedCoreNum; // 块→核:每轮核号错开
|
||||
uint64_t matIndex = params_.index % (params_.mCnt * params_.nCnt); // batch 内 m×n 平面下标
|
||||
uint64_t rowIdx = matIndex / params_.nCnt / params_.mainWindow; // 第几个划窗行
|
||||
if (rowIdx < params_.mainRow) { // 主划窗区
|
||||
params_.mCntIndex = rowIdx * params_.mainWindow + matIndex % params_.mainWindow;
|
||||
params_.nCntIndex = (matIndex / params_.mainWindow) % params_.nCnt;
|
||||
} else { // 尾划窗区(tailWindow)
|
||||
...
|
||||
}
|
||||
if (rowIdx % 2 != 0) { // 奇数划窗行反向扫描(蛇形)
|
||||
params_.nCntIndex = params_.nCnt - 1 - params_.nCntIndex;
|
||||
}</code></pre>
|
||||
<p>机制解读:</p>
|
||||
<ol class="tight">
|
||||
<li><b>块到核映射</b>:<code>index = blockIdx + round×usedCoreNum</code>,第 r 轮核 c 处理第 c + r×usedCoreNum 个基本块,天然错轮;</li>
|
||||
<li><b>划窗</b>:M 方向每 <code>mainWindow = min(aswWindowLen, mCnt)</code> 个 m 块作为一行窗口,遍历顺序是"窗口内先 m 后 n"——同一 A 行窗口内的所有 n 块连续被计算,A 的 GM/L2 数据在一个窗口内被 nCnt 次复用,最大化 L2 命中。窗长由 host 侧 <code>GetAswWindowLen()</code> 计算:<b>取 aicNum 的不超过 sqrt(aicNum) 的最大因子</b>(如 32 核 → 窗长 4,25 核 → 5),写入 tilingData.aswWindowLen。这个选择的直觉是:窗口越接近正方形,同窗口内 A 块与 B 块的 L2 足迹越小、并发核的访问局部性越好;取 aicNum 的因子保证整窗能被核数均分;</li>
|
||||
<li><b>蛇形</b>:奇数窗口行 n 方向反向扫描,相邻行间 n 首尾相接,避免长距离跳址,让上一行窗口末尾的 B 列块与下一行开头的 B 列块相邻,提高 B 的 L2 复用;</li>
|
||||
<li><b>尾窗</b>:mCnt 不能被窗长整除时剩余部分走 tailWindow 分支单独处理。</li>
|
||||
</ol>
|
||||
|
||||
<h3>6.2 对角线错位分核(老路径通用 kernel)</h3>
|
||||
<p><code>BatchMatMulCommonBaseBlock::UpdateBasicIndex()</code>(batch_mat_mul_v3_com_base_block.h):</p>
|
||||
<pre><code>uint64_t newBlockIdx = (GetBlockIdx() + usedCoreNum - params_.blockIdxStart) % usedCoreNum
|
||||
+ roundIdx * usedCoreNum;
|
||||
uint64_t mIdx = newBlockIdx % params_.mCntUse;
|
||||
uint64_t nIdx = (newBlockIdx + newBlockIdx / MMLcm(params_.mCntUse, params_.nCntUse)) % params_.nCntUse;
|
||||
params_.index = mIdx * params_.nCntUse + nIdx;</code></pre>
|
||||
<p>线性 id 先取 m,再用 <code>newBlockIdx / lcm(mCnt,nCnt)</code> 给 n 加相位偏移,使同一时刻各核落在 m×n 平面的<b>不同对角线</b>上,避免多核同时抢同一行 A / 同一列 B 的 GM 流量;配合每个 L2 tile 边界轮转起始核号(blockIdxStart)与 mTile 间蛇形(reverse 翻转 nTile 遍历方向)。</p>
|
||||
|
||||
<h3>6.3 Host 侧 L2 cache tiling:calOrder + mTileBlock/nTileBlock(老路径)</h3>
|
||||
<p>Host 侧 <code>BatchMatmulV3BaseTiling::DoL2CacheAndCalOrderTiling()</code>(batch_mat_mul_v3_base_tiling.cpp)决定核间执行的宏观顺序:</p>
|
||||
<ul class="tight">
|
||||
<li>冲突度量 <code>transConflict = max(CeilDiv(usedCoreNum,mCnt), CeilDiv(usedCoreNum,nCnt))</code>——同一时刻并发核访问同一 A/B 块的最大冲突数;</li>
|
||||
<li>当 <code>nCnt < usedCoreNum < mCnt</code> 时,从 <code>CeilDiv(usedCoreNum,nCnt)×nCnt</code> 递减搜索 newMcnt,直到尾块占比 ≤ 0.5(TAIL_CONFLICT_RATIO)——让第一轮 M 方向无冲突且尾部浪费不超过一半;</li>
|
||||
<li>启用 L2 分块条件:<code>isBigSize(总数据量 > l2Ratio×100MB)且 cBatchDimAll < usedCoreNum 且 transConflict ≤ 6</code>,命中后写 mTileCntL2/mTileBlock/nTileBlock;</li>
|
||||
<li><code>CheckandSetDiagonalConflict()</code>:不做 L2 分块时,比较"错位分核"与"行优先"的冲突数,错位更优才改写分组;</li>
|
||||
<li><code>calOrder</code>(父类 matmul_v3_base_tiling.cpp 注释明确):<b>0 = M 优先(行优先),1 = N 优先(列优先)</b>,由 isNKMsmallK 判定,kernel 侧 <code>params_.rowOrder = tilingL2.calOrder</code> 控制块派发方向。L1 全载模板直接 mTileCntL2=nTileCntL2=1,不做核间 swizzle(全载后 A/B 已在 L1,无 L2 复用问题)。</li>
|
||||
</ul>
|
||||
|
||||
<h3>6.4 batch 组轮询(iterbatch/mergebatch/matmul2mul)</h3>
|
||||
<p>这些分支的调度粒度是"一组 batch"而非 m×n 基本块,核映射为简单轮询 <code>for (tileIdx = curBlockIdx; tileIdx < tileNum; tileIdx += blockNum)</code>,坐标 <code>{0,0,0, tileIdx×iterBatchL1}</code>;mergebatch 额外按主块/主尾块/最终尾块做负载均衡。因为并行维度只有 batch,不存在 m×n 平面上的访存冲突问题,无需滑窗/错位。</p>
|
||||
|
||||
<table>
|
||||
<tr><th>机制</th><th>所在分支</th><th>解决的问题</th><th>核心参数</th></tr>
|
||||
<tr><td>滑窗+蛇形(ASW)</td><td>ASW_BASIC / BASE / 全载系(arch35)</td><td>m×n 基本块遍历的 L2 复用</td><td>aswWindowLen = aicNum 的 ≤√aicNum 最大因子</td></tr>
|
||||
<tr><td>对角线错位</td><td>老路径通用 kernel</td><td>并发核抢同一行 A / 列 B 的 GM 冲突</td><td>lcm(mCnt,nCnt) 相位偏移 + 起始核轮转</td></tr>
|
||||
<tr><td>calOrder 行/列优先 + L2 tile 分组</td><td>老路径 host tiling</td><td>大 shape 下 L2 容量约束的宏观遍历方向</td><td>mTileBlock/nTileBlock/transConflict ≤ 6</td></tr>
|
||||
<tr><td>batch 组轮询</td><td>iterbatch / mergebatch / matmul2mul</td><td>batch 为主并行维度时的均摊</td><td>iterBatchL1 / batchAL1 / 主尾块均摊</td></tr>
|
||||
</table>
|
||||
|
||||
<!-- ============================== 7 ============================== -->
|
||||
<h2 id="sec7">7. tiling_key 编码:7 位段如何锁定一条 kernel 路径</h2>
|
||||
<p>arch35 路径的 tilingKey 由 <code>BatchMatMulV3TilingKey::GetTilingKey()</code> 生成,7 个位段(声明于 <code>op_kernel/arch35/batch_mat_mul_v3_tiling_key.h</code>,低位到高位):</p>
|
||||
<table>
|
||||
<tr><th>位段</th><th>位宽</th><th>取值</th><th>决定什么</th></tr>
|
||||
<tr><td>BATCH_API_LEVEL</td><td>4 bit</td><td>0=HIGH_LEVEL(MatmulImpl 高阶 API)/ 1=BASIC_LEVEL(BasicAPI/Cmct)/ 2=TENSOR_LEVEL(张量级/Blaze)</td><td>用哪一层搬移计算抽象</td></tr>
|
||||
<tr><td>BATCH_A_TRANS / BATCH_B_TRANS</td><td>2+2 bit</td><td>0/1</td><td>layout/分形方向</td></tr>
|
||||
<tr><td>BATCH_ITER_MODEL</td><td>4 bit</td><td>0=FOR_BATCH / 1=ITER_BATCH_SINGLE_BIAS / 2=BATCH_MATMUL_TO_MUL / 3=MERGE_BATCH / 4=FOR_FUSED_BATCH / 5=BROADCAST_BATCH / 6=ITER_BATCH_BROADCAST_A / 7=ITER_BATCH_BROADCAST_B</td><td>batch 处理模式(≈ 分支 id)</td></tr>
|
||||
<tr><td>BMODEL</td><td>4 bit</td><td>0=BASIC / 1=STREAM_K / 2=K_EQUAL_ZERO</td><td>计算模型特判</td></tr>
|
||||
<tr><td>BATCH_FULL_LOAD</td><td>4 bit</td><td>0=NONE / 1=A_FULL_LOAD / 2=B_FULL_LOAD / 3=AB_FULL_LOAD</td><td>L1 全载模式</td></tr>
|
||||
<tr><td>BATCH_L0C2OUT_MODEL</td><td>4 bit</td><td>0=ON_THE_FLY / 1=1V1_ND_ALIG_FIXPIPE / 2=1V2_ND_ALIG_FIXPIPE</td><td>L0C→GM 输出通路(fixpipe)</td></tr>
|
||||
</table>
|
||||
<p>每个合法位段组合通过 <code>ASCENDC_TPL_SEL</code> 宏同时绑定三样东西:<b>kernel 类型</b>(AIC_ONLY / AIV_ONLY / MIX_AIC_1_2)、<b>tiling 结构体</b>(BatchMatMulV3TilingData / ...IterBatchBasicTilingData / ...MergeBatchBasicTilingData / ...ToMulBasicTilingData / MatMulV3KEqZeroBasicTilingData 等)、<b>kernel 模板实例化</b>。编译期为每个 tilingKey 生成确定的 kernel,运行时零分发开销。</p>
|
||||
<p>kernel 入口是一张编译期 <code>if constexpr</code> 分发树(arch35/batch_mat_mul_v3.cpp),节选主干:</p>
|
||||
<pre><code>// tilingKey 位段 → kernel 类(节选)
|
||||
HIGH_LEVEL + BASIC + NO_FULL_LOAD + FOR_BATCH → BatchMatMulAswKernel + BatchMatMulAswBlock(ASW 主分支)
|
||||
BASIC_LEVEL + BASIC + A_FULL_LOAD + FOR_BATCH → MatMulActKernel<..., A_FULL_LOAD_MODE>
|
||||
TENSOR_LEVEL+ BASIC + B_FULL_LOAD + FOR_BATCH → Blaze: MatMulBL1FullLoadKernel
|
||||
TENSOR/BASIC+ STREAM_K + FOR_BATCH → MatMulStreamKKernel(MIX_AIC_1_2)
|
||||
BASIC_LEVEL + BASIC + ITER_BATCH_SINGLE_BIAS → BatchMatMulActIterBatchKernel(Cmct,可带 ND_FIXPIPE_1_2)
|
||||
HIGH_LEVEL + BASIC + ITER_BATCH_SINGLE_BIAS → BatchMatMulMultiBatchKernel(IterateBatch 高阶版)
|
||||
BASIC_LEVEL + BASIC + MERGE_BATCH → BatchMatMulActMergeBatchKernel(Cmct)
|
||||
BASIC_LEVEL + BATCH_MATMUL_TO_MUL(AIV_ONLY) → BatchMatMulToMulActKernel(K=1)
|
||||
BASIC_LEVEL + K_EQUAL_ZERO(AIV_ONLY) → MatMulInputKEqZeroClearOutput(K=0)
|
||||
TENSOR_LEVEL+ BROADCAST_BATCH → Blaze: BatchMatMulBroadcastKernel
|
||||
TENSOR_LEVEL+ ITER_BATCH_BROADCAST_A/B → Blaze: BatchMatMulIterBatchBroadcastKernel</code></pre>
|
||||
<div class="note">注意 <b>IS_BLAZE 分叉</b>:TENSOR_LEVEL 组合在 ASC_DEVKIT ≥ 9.1(Blaze GEMM 框架可用)时走 Blaze 的 GemmUniversal 系 kernel,否则回落到 MatMulActKernel/AswKernel。这是同一位段组合在不同编译环境下的第二维分叉,不改变分支语义。</div>
|
||||
|
||||
<!-- ============================== 8 ============================== -->
|
||||
<h2 id="sec8">8. 分支完备性论证与批判性讨论</h2>
|
||||
<h3>8.1 完备性论证</h3>
|
||||
<ol class="tight">
|
||||
<li><b>覆盖完备</b>:优先级表尾部 BASE(999) 的 IsCapable 恒 true、ASW_BASIC 仅校验 dtype/batch 一致性,任何通过 Validate 的合法输入必然命中某分支;TENSOR_LEVEL 的 BROADCAST_BATCH_MODEL 由高阶 API 内建支持任意广播形态,广播 case 无遗漏。</li>
|
||||
<li><b>特判 ⊂ 通用</b>:前 10 个分支覆盖的 case 用通用 cube 模板也能正确计算(只是更慢),即"任何 case 的最优实现必然落在 {特判分支} ∪ {通用模板} 内",满足"性能最优分支集是完备全集子集"的方法论要求。排除逻辑体现在 IsCapable 的每个拒绝条件上——例如 StreamK 的 <code>batchC×mCnt×nCnt ≤ aicNum/2</code> 保证了只有切 K 收益明确的 case 才分流出去,其余留在通用路径。</li>
|
||||
<li><b>互斥性与优先级无冲突</b>:多数特判条件天然互斥(K=0 与 K=1 互斥;单边广播与 batch 完全相等互斥;mergebatch 要求 batch≥4×核数而 StreamK 要求并行度 ≤ 核数一半……);少数有交集的(如 iterbatch basicapi 与 iterbatch 高阶版)靠优先级顺序取先命中者,语义等价只搬移路径不同。</li>
|
||||
</ol>
|
||||
<h3>8.2 批判性观察(源码未必最优,值得审视的点)</h3>
|
||||
<div class="crit">
|
||||
<ul class="tight">
|
||||
<li><b>经验常数硬编码</b>:mmadCount=8(issue queue)、fullCopySize=64KB(单次搬满带宽)、48KB(L1_SINGLE_SIZE_LIMIT)、均衡率 0.8、transConflict ≤ 6、TAIL_CONFLICT_RATIO=0.5 等都是拍死的经验阈值,与 950PR 满配 32 核/降配 28 核、不同 HBM 档位的实际曲线未必吻合。给定具体 case 做最优方案决策时,这些阈值两侧的边界 case 值得用实测复核。</li>
|
||||
<li><b>cubeBound 解析模型依赖平台带宽参数</b>:GetRebalanceBlock 的 cubeBoundEdge 公式使用 l2BW/hbmBW/computePower 等静态配置,若平台库中 PR 降配版(1.4TB/s)参数不准确,baseM/baseN 寻优结果会系统性偏移。</li>
|
||||
<li><b>ASW 窗长取"aicNum 的 ≤√aicNum 最大因子"是启发式</b>:对 mCnt 很小或 nCnt≫mCnt 的细长 shape,方形窗假设不成立;此时滑窗退化为普通行优先(mainWindow=min(aswWindowLen,mCnt)),L2 复用收益可能不如按 shape 长宽比自适应的窗形。</li>
|
||||
<li><b>TO_MUL 的 N 区间裁剪比较"碎"</b>(n∈(32B,256B]/dtype 排除、n%256B≠0、n≠1……),说明该分支是按已知 case 调出来的,对区间外 N 的 K=1 case 会回落到 cube 路径——存在"K=1 但 N 对齐"的 case 被迫走 mmad 的潜在劣化点。</li>
|
||||
<li><b>StreamK 的 fp32 K≤200 万阈值</b>源于 binary accumulation 精度,属保守设定;对精度不敏感的场景本可放宽,但 tiling 层没有暴露该旋钮。</li>
|
||||
<li><b>iterbatch basicapi 与 iterbatch 高阶版职责重叠</b>:两者目标 case 几乎相同,仅搬移抽象层不同(BasicAPI/Cmct vs MatmulImpl),优先级 5 先于 6,意味着高阶 API 版只在 basicapi 的 L1/L0 条件不满足时才有机会——若某些 case 下高阶 API 实际更优,当前顺序无法表达。</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- ============================== 9 ============================== -->
|
||||
<h2 id="sec9">9. 附:非 arch35 老路径分支(910B 等平台)简述</h2>
|
||||
<p>非 DAV_3510/RESV 平台走 <code>BatchMatmulV3BaseTiling::DoLibApiTiling()</code>(batch_mat_mul_v3_base_tiling.cpp),不是短路遍历而是<b>固定函数序列 + flag 覆盖</b>:</p>
|
||||
<div class="flow">DoLibApiTiling(父类基础 tiling)
|
||||
→ SetBatchDimInfo / CalcBatchDimAll
|
||||
→ CheckNd2NzOnTheFlyLimit()(内轴 > 65535)→ DoUnAlignCommonTiling(mixNd2Nz,直接返回)
|
||||
→ DoCommonTiling()(TuneBaseMKN 选 baseM/N/K,复位全部 TilingEnable flag)
|
||||
→ CheckVectorComputationCondition() → DoVectorTiling(AIV/UB 模式 GEMV,直接返回)
|
||||
→ DoL1FullLoadTiling()(AL1/BL1 全载判定)
|
||||
→ DoL2CacheAndCalOrderTiling()(核间执行顺序/swizzle,见 §6.3)
|
||||
→ DoMultiBatchAndL1FullLoadTiling()(多 batch,命中则把 loadMode 从 AL1/BL1 改写回 BASE——多 batch 优先级实际高于 L1 全载)
|
||||
→ DoTilingKeyCustom():5 个 4-bit flag 编码 tilingKey
|
||||
= {MultiBatchL1FullLoad, MultiBatch, LoadMode(0=BASE/1=AL1/2=BL1/3=VECTOR_FULLLOAD), IsMultiBatchOut, MixNd2Nz}</div>
|
||||
<p>对应 kernel(op_kernel/batch_mat_mul_v3.cpp 分发):Common(对角错位 swizzle)、UnAlignCommon(AIV 做 ND2NZ + AIC cube 的 CV 协同,CrossCoreFlag 双缓冲)、AL1/BL1_FULLLOAD(MatmulCallBackFunc 回调式全载)、MultiBatch / MultiBatchOut、MultiBatchL1FullLoad(多 batch Nd2Nz 一次搬 L1 内层复用)、VECTOR_FULLLOAD(BatchMatmulVectorKernel:UB 流水 Mul+ReduceSum 的 GEMV)。</p>
|
||||
|
||||
<!-- ============================== 10 ============================== -->
|
||||
<h2 id="sec10">10. 参考来源清单</h2>
|
||||
<h4>源码(昇腾NPU知识库/代码仓/ops-nn)</h4>
|
||||
<ul class="tight">
|
||||
<li>matmul/batch_mat_mul_v3/op_host/op_tiling/:batch_mat_mul_v3_tiling.cpp(入口)、batch_mat_mul_v3_base_tiling.cpp/.h(老路径基类:DoLibApiTiling、DoL1FullLoadTiling、DoL2CacheAndCalOrderTiling、CheckandSetDiagonalConflict、DoTilingKeyCustom)</li>
|
||||
<li>matmul/batch_mat_mul_v3/op_host/op_tiling/arch35/:batch_matmul_v3_tiling_strategy.h(分支优先级表)、batch_matmul_v3_tiling_advanced.cpp/.h(batch 提取/校验/MergeBatchAndMAxis)、batch_matmul_v3_tiling_key.cpp/.h,以及 12 个策略 tiling 文件(k_equal_zero / matmul2mul / basic_streamk / mergebatch_basicapi / iterbatch_broadcast_basicapi / iterbatch_basicapi / iterbatch / asw_al1_full_load_basic / asw_bl1_full_load_basic / asw_basic / asw)</li>
|
||||
<li>matmul/batch_mat_mul_v3/op_kernel/:arch35/batch_mat_mul_v3.cpp(if constexpr 分发树)、arch35/batch_mat_mul_v3_tiling_key.h(位段+组合表)、arch35/batch_mat_mul_v3_asw_{kernel,block}_advanced.h(ASW 主 kernel 与滑窗 swizzle)、arch35/batch_mat_mul_v3_asw_{al1,bl1}_full_load_kernel_advanced.h、arch35/batch_mat_mul_v3_iterbatch_{kernel,block}_advanced.h、arch35/batch_mat_mul_v3_{iterbatch,mergebatch}_basicapi_{cmct,block_scheduler}.h、arch35/batch_mat_mul_v3_matmul2mul_*.h、batch_mat_mul_v3.h / batch_mat_mul_v3_com_base_block.h / batch_mat_mul_v3_block.h / batch_mat_mul_v3_vector.h(老路径 kernel)</li>
|
||||
<li>matmul/mat_mul_v3/(公共基类与 kernel 复用):arch35/matmul_v3_tiling_advanced.cpp(主流程)、arch35/matmul_base_tiling.h、arch35/matmul_tiling_registry.h(短路遍历)、arch35/matmul_v3_basic_aswt_tiling.cpp(DoAL1/BL1FullLoad)、mat_mul_pingpong_basic.h / mat_mul_streamk.h / mat_mul_{al1,bl1}_full_load.h / mat_mul_input_k_eq_zero_clear_output.h / mat_mul_v3_full_load_kernel_helper.h</li>
|
||||
<li>matmul/common/cmct/(Cmct GEMM 框架):kernel/kernel_matmul_{iterbatch,merge_batch}.h、kernel/kernel_batch_matmul_to_mul.h、block/block_mmad_{builder,iterbatch,mergebatch,to_mul}.h、epilogue/block_epilogue_{iterbatch,mergebatch}.h</li>
|
||||
<li>ASW 命名佐证:dual_level_quant_batch_matmul/op_host/op_tiling/dual_level_quant_batch_matmul_adaptive_sliding_window_tiling.cpp</li>
|
||||
</ul>
|
||||
<h4>硬件与编程文档(昇腾NPU知识库)</h4>
|
||||
<ul class="tight">
|
||||
<li>00_硬件/昇腾950_NPU架构白皮书.pdf(§3 表3-1 核数/算力;表4-2 存储层级容量 L1 512KB / L0A/B 64KB / L0C 256KB / UB 512KB / L2 128MB;§4.1 微架构特性)</li>
|
||||
<li>00_硬件/Ascend_950PR_产品页.txt、Ascend_950DT_产品页.txt、昇腾产品形态说明.md</li>
|
||||
<li>CANN商用版9.0.0/01_AscendC算子开发/:054_架构规格_NPU架构版本351x.md(AIC/AIV 分离、数据通路变化、分形格式)、126_矩阵编程高阶API基础知识.md、191/192_矩阵计算性能优化.md(L0C 累加、L1 长驻)、067_DoubleBuffer.md</li>
|
||||
</ul>
|
||||
<p class="src">说明:本文所有分支条件、阈值、代码片段均直接引用自上述源码文件;硬件数值均标注来源。资料未覆盖的规格项(MTE 各通路带宽、SSBuffer 容量、芯片频率等)未做编造。</p>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user