diff --git a/BMM算子优化分析_Release/BMM算子优化分析_v0.6.html b/BMM算子优化分析_Release/BMM算子优化分析_v0.6.html new file mode 100644 index 0000000..d2d3051 --- /dev/null +++ b/BMM算子优化分析_Release/BMM算子优化分析_v0.6.html @@ -0,0 +1,331 @@ + + +
+ + +基于 issue#3 扩展。目标芯片:昇腾 950PR(DAV_3510)。所有分支进入条件只含 case 形状参数(B、M、N、K、dtype)与芯片规格参数。+
完成带 batch 的矩阵乘:C = A @ B + bias。
[BatchA, M, K],dtype,典型 ND,可带转置[BatchB, K, N],dtype,典型 ND,可带转置[B, 1, N],固定 ND,可为空[BatchC, M, N],BatchC = broadcast(BatchA, BatchB)| 符号 | 含义 | 950PR 取值 |
|---|---|---|
| C | AIC 核数(aicNum) | 32 |
| L1 | 每核 L1 Buffer | 512KB |
| L0A / L0B | 每核 L0A / L0B | 64KB / 64KB |
| L0C | 每核 L0C(FP32 累加,4B/元素) | 256KB |
| L2 | L2 Cache 容量 / 带宽 | 128MB / 5.2TB/s |
| W_GM | GM 带宽(读写共享) | 1.6TB/s |
| R₁₆ | 16bit 对应位宽算存比 | ≈607.5 FLOP/元素 |
| dValue | 单数据块内数据连续排布长度 | 推荐 256B/512B,不建议 <128B |
| min_TileSize | 确保高带宽利用率的单块搬移数据量最小值 | 16KB |
| min_DatamountPerCore | 确保高带宽利用率的单核搬移数据量最小值 | 480KB |
| minCoreNum | 确保高带宽利用率的并行搬移核数最小值 | ≈0.8C = 26 |
以上数值基于 950PR 实测分析总结;对搬移带宽利用率的影响重要性排序为:核数 > 单核搬移总数据量 > 单分块大小 > dValue。不同 NPU 芯片数值可能略有差异,换芯片时逻辑结构不变、只换常数表。
+BMM 的执行是核内多级硬件流水的并行——Cube 计算(MMAD)、GM/L2→L1(MTE2)、L1→L0(MTE1)、L0C 写出(Fixpipe),各流水级时延可被双缓冲相互掩盖:
+总时延 = 最慢一级流水,优化的关键是对瓶颈级的优化。由此得到设计自由度——瓶颈交换:搬移是瓶颈时可牺牲算力(冗余计算)换搬移效率;计算是瓶颈时可牺牲搬移(重复读取)换计算效率。MergeBatch 是前者的典型,ASW_Basic 切 M/N 是后者的典型。
+case 固有算存比与 16bit 位宽平衡点:
+$AI < R_{16}$ → 访存 Bound(瓶颈在 MTE2);反之计算 Bound(瓶颈在 MMAD)。
+BMM 的实现本质是:把数据分块(tile),由全部 AIC 核并行 + 串行完成这些分块的计算,再组合成最终结果:
+分块有 4 个维度:B、M、N、K。核间怎么分这 4 个维度,就是分支划分的第一性问题(核内分块是第二性问题,属于各分支内部 tiling)。
+四个维度的核间切分特征(后续一切推导的基石):
+| 切分维度 | 读入特征 | 计算特征 | 写出特征 |
|---|---|---|---|
| 切 B | 核间零重复读(每个数据块只被 1 个核读取);核内是否重复读另有条件——若 L1 放不下单 batch 完整的 M、N 维输入(K 维可切段放入,kL1<K),单 batch 计算中切 M 会重复读 B、切 N 会重复读 A | 每个输出块由 1 个核独立完成,无核间依赖 | 只写最终结果,无中间结果 |
| 切 M / 切 N | 切 M 则同一右矩阵块被多核重复读;切 N 则同一左矩阵块被多核重复读 | 每个输出块由 1 个核独立完成,无核间依赖 | 只写最终结果,无中间结果 |
| 切 K | 每个数据块只被固定的 1 个核读取,零重复读 | 每个输出块由多核共同完成,存在核间依赖 | 有中间结果写出,需核间 Reduce 归约 |
差异的根本原因:B 维在数学上独立(逐 batch 独立矩阵乘),切 B 核间天然零重复、零依赖;K 维有 L0C 累加机制——核内切 K 时多轮 mmad 在 L0C 原地累加、中间结果不出核,一旦切到核间,部分和必须写出 workspace 再归约——切 K 是唯一同时破坏"累加不出核"和"输出独占"的切法。
+4 维的任意非空子集共 $2^4-1=15$ 种切分组合,任何实现方案必属其一(完备):
+| 组 | 组合 | 共同特征 | 优化重心 |
|---|---|---|---|
| 纯 B | {B} | 核间零重复读(核内重复读取决于 L1 驻留形态);无中间写出 | 搬移效率 / 计算效率 |
| 含 M/N 不含 K | {M},{N},{M,N},{B,M},{B,N},{B,M,N} | 核间可能有重复读 | 重复读尽量少(L2+swizzle 吸收)+ 搬移/计算效率 |
| 含 K | 其余 8 种 | 有中间结果写出 + 归约 | 计算效率,且归约时延不能成为新瓶颈 |
核间切分价格严格排序:cost(切 B) = 0 < cost(切 M/N) ≪ cost(切 K)。切 B 核间免费;切 M/N 的重复读可被 128MB L2(5.2TB/s vs GM 1.6TB/s)+ swizzle 大部分吸收;切 K 的归约流量 ∝ grid_K×输出量且引入核间同步,是结构性代价。整条分支决策树就是:按价格从低到高购买并行度,买不够才加价。
+由此得 6 大分支:转Matmul、特殊分支、IterBatch、MergeBatch、ASW_Basic、StreamK。重叠区(如 B≥C 且 M×N 中等时 MergeBatch 与 IterBatch 都合法)由端到端时延模型 $T_{total}$ 仲裁;分支体系保证候选集完备无冗余。
+解释:单边 batch=1 的 BMM 与 Matmul 只差一个维度标签,直接复用 Matmul 的成熟优化体系。
+[B,M,K] 的 batch 维与 M 维在 ND 下内存相邻紧排,直接视图为 [B·M,K],输出布局逐元素一致——零重排、零 split,免费转换;[K, B·N] 需一次真实转置重排(O(B·K·N)),且输出存在置换需 scatter——有代价。A 较小($MK\cdot\text{dtype} \le L1$)时优先 A 常驻 L1、留在 BMM 分支内(广播友好形态);A 较大时按广播扩展后分别预估"BMM 分支"与"重排+转Matmul"的时延,择优。核间切 B(每核 $b_{core}$ 个 batch,核间无同步);核内把 b 个 batch 合并计算:$[b,M,K]@[b,K,N] \Rightarrow [bM,K]@[K,bN]=[bM,bN]$,BlockTrace 取块对角线得 $[b,M,N]$。交叉项被算出但丢弃(浪费比例 (b−1)/b)——进入该分支的 case 必然访存 Bound(条件 5 保证),浪费的算力被搬移时延掩盖。
+同时满足(b0 = 单次合并计算的 batch 数下限,b0 ≥ 2):
+Step 1:合并数 b(L0C + 算存比双上限)
+b 尽量取 $b_{core}$ 的因子(每次合并数均匀,负载与功耗更优)。
+**Step 2:L0 级 K 粒度 $k_{L0}$**
+解释:L0A/L0B 各 64KB、双缓冲两份,装入合并后 $bM$ 行($bN$ 列)× $k_{L0}$ 的 fractal;末项是 dValue 下限。
+**Step 3:L1 级 $k_{L1}$、$b_{L1}$**
+解释:$k_{L1}$ 是 GM→L1 的 K 向粒度,按 dValue 推荐值 256B 取;L1 双缓冲两份,每份驻留 $b_{L1}$ 个 batch 的 A/B 各一块;$b_{L1} \ge b$ 保证合并不断供。
+核间切 B(每核 $b_{core} \ge 1$ 个 batch,核间无同步);核内逐个 batch 做标准 Matmul 分块计算。无算力浪费、无跨 batch 依赖,是"切 B"最朴素的形态。
+同时满足:
+重复读发生在单 batch 内部:L1 放不下单 batch 完整的 M、N 维输入(K 维允许切段放入,kL1<K)时,核内切 M 会在 K 循环中重复读 B、切 N 会重复读 A。IterBatch 进入与否不由算存比判定——即使 case 是计算 Bound,重复读引入的额外搬移也可能把它重新拖回访存 Bound。所以进入条件直接由 L1 驻留形态刻画:
+先明确流水结构:fixpipe 开 unitflag 后,写出由硬件随路完成(每个 16×16×16 fractal 算完即自动搬出),写出侧不需要软件排流水;要掩盖的只有"读入(MTE2: GM→L1)↔ 计算(Cube)"。
+结论:(c)(d) 是同族(一侧驻留 + 对侧切 K),仅 L1 预算不同——$b_{core}=1$ 用全量 L1,$b_{core}>1$ 用 L1/2 换 batch 间乒乓;两者不宜合并(合并会丢失 batch 边界预取这一关键差异),(e) 在驻留侧超 L1/2 时接管。
+(a):每核 1 batch 直接搬入 L1。L1→L0 先看 L0C 能否放下完整单 batch 输出:
+if (L0C >= M*N*4B): # L0C 放得下完整输出:不切 M/N,只切 K
+ BaseM = M; BaseN = N
+ BaseK = min(align(L0A/M, 16), align(L0B/N, 16))
+else: # L0C 放不下:按较小维切
+ if M < N: BaseM = align(M,16); BaseN = floor(L0C/4B / BaseM)
+ else: BaseN = align(N,16); BaseM = floor(L0C/4B / BaseN)
+ BaseK = min(floor_align(L0A/BaseM,16), floor_align(L0B/BaseN,16))
+(b):L1 双 batch 乒乓,核内 GM→L1→L0→Cube→L0C→GM/L2 流水;L1→L0 分块同理,但各级预算减半(L0C/L0A/L0B 按 2 份)。
+(c)/(d):一侧驻留 + 对侧切 K。假设驻留左矩阵,右矩阵搬入的 K 向长度:
+(d) 中另一半 L1 在计算期间预取下一 batch 的驻留侧,实现 batch 间无气泡衔接;fixpipe 开 unitflag。
+(e):两侧都切 K 段,$k_{L1}$ 取满足容量与 dValue 的最大值;L0C 按 batch 乒乓(各占 L0C/2),batch 边界由硬件 fixpipe 自动排空、下一 batch 立即在另一半 L0C 累加。
+其中 $Q_{16}$ = 单核 BF16 算力 ≈ 13.5 TFLOPS;归约流量 = grid_K 份部分和写出 + 读回归约共 2 遍,$W_{eff}$ 取 GM 有效带宽 ≈ 0.4 × 1.6TB/s(归约是多核小块读写,达不到满带宽)。要求 $T_{MMAD/core} \ge \alpha \cdot T_{Reduce}$——安全系数 α=10 的含义是归约新增流水级的占比压到 ~10% 以内、不改变瓶颈归属。代入得:
+数值上:grid_K=2 → K≥6.8K;4 → K≥27K;8 → K≥108K;32 → K≥1.7M。对 K 的要求随 grid_K 平方增长——grid 搜索自然淘汰归约过重的配置。
+源码对照:batch_matmul_v3_basic_streamk_tiling.cpp 中 K 的固定门槛为 CeilAlign(K,256) ≥ max(8192, aicNum×256B/dtype)。其含义:aicNum×256B/dtype(2048bit/核)= 全部 AIC 参与切 K 时每核至少分到 256B 的 K 向内轴数据——正是条件 2 的 dValue 推荐值;8192 = C×256 元素是绝对下限,保证每核至少 256 个 K 元素(BF16 512B),摊薄切 K 的固定开销(workspace 建立、归约同步)。源码用固定门槛,是条件 2/3 的保守近似;本文的 grid_K 平方式给出随切份数变化的解析门槛,更细。
核间组织:先按 B/M/N 切出输出块,剩余核预算折成 K 向份数:
+mCnt、nCnt 收拢为 blocksPerBatch 的因子(避免碎核尾块);由条件 1 知 $mCnt \cdot nCnt \le blocksPerBatch/2$,故 $grid_K \ge 2$。归约组内核 c 负责 K 段 $[cK/grid_K,\; (c{+}1)K/grid_K)$。
+核内流水:对自己的 K 段做标准分块流水(MTE2→L1→L0→mmad),段内多轮在 L0C 原地累加;段完部分和经 Fixpipe 写出。
+归约:
+参数搜索:$grid_K$ 从 2 起按 2 的幂递增,取同时满足条件 2/3 的最小值;都不满足则退为降核 ASW_Basic。
+1、核间切分维度选择(按共享代价从低到高):切 B(零共享,先试)→ 切 M(右矩阵 $KN\cdot\text{dtype} \le L2$ 则驻留 L2)→ 切 N(对称)→ 混合切(靠 swizzle + L2 切分管理)→ 降核(P 远小于 C 且 StreamK 也不满足时,宁可部分核闲置)。
+2、swizzle:ASW 滑窗蛇形。M 向按窗口 W 分组,窗内 N 向蛇形遍历:
+同一时刻 C 个核的活跃工作集被压缩到"W 个 A 行块 + 一条 B 列块带",L2 足迹最小。窗取 $\lfloor\sqrt C\rfloor$ 的最大因子:窗越接近方形两侧足迹之和越小,且因子性保证整窗被核数均分、边界不碎。
+3、L2 切分:工作集超 128MB 时按 mL2TileNum×nL2TileNum 切块,块内错位分核(对角线分配),避免多核同时抢同一地址的读读冲突,优先选拖尾小的方案。
+4、核内 tiling:$M^t N^t \cdot 4\text{B} \cdot DB \le L0C$;$M^t K^t \cdot \text{dtype} \cdot 2 \le L0A$、$K^t N^t \cdot \text{dtype} \cdot 2 \le L0B$;内轴按 dValue 256B/512B 对齐;L1 按容量开双缓冲,余量充足开 4 buffer。
+5、内部特化(参数极限,不是独立分支):单边无 batch 且该侧矩阵小($M \le 256$、$MK\cdot\text{dtype}\cdot 2 \le L1$、对侧每核循环 ≥4 轮)时小侧整个常驻 L1、只搬一次(L1 全载)。
+C = A ⊙ B,无累加深度,Cube 的 16×16×16 粒度浪费 15/16,走 AIV 向量通路(GM→UB→Mul→GM)优于 Cube 通路。触发需 $B \ge 2 \times 64$(AIV 核数×2,开 UB 乒乓)且单 batch 输入输出能驻留 UB。对 $B \in [1, 2048]$、$M,N,K \in [1, 10240]$ 按对数网格采样 20736 个 case,严格按上述进入条件分类(BF16),结果:
+| 分支 | case 数 | 占比 | B 范围 | 区域特征 |
|---|---|---|---|---|
| ASW_Basic | 7290 | 35.2% | 2 ~ 2048 | 通用:B<C 且 P≥C;或 B≥C 但 L1 五形态不满足(M/N 大) |
| IterBatch | 4544 | 21.9% | 32 ~ 2048 | B≥C、负载均衡、L1 五形态之一满足 |
| 降核 ASW_Basic | 3190 | 15.4% | 2 ~ 128 | P<C 且 K 不满足 StreamK 阈值(小 case,时延绝对值小) |
| 特殊分支 | 1728 | 8.3% | 任意 | K=0 / K=1 |
| MergeBatch | 1674 | 8.1% | 128 ~ 2048 | $b_{core}\ge 4$ 且 $MN \le L0C/(2b_0^2\cdot4\text{B})=8192$ 等五条全过 |
| 转Matmul | 1584 | 7.6% | B=1 | 单边 batch=1 |
| StreamK | 726 | 3.5% | 2 ~ 128 | P<C 且 K≥8192 |
| 形态 | 命中数 | 说明 |
|---|---|---|
| b) 双 batch 乒乓 | 1787 | 最多:B 大且单 batch 较小 |
| e) 两侧切 K | 1456 | 次之:K 可切段的通用兜底 |
| a) 单 batch 全驻留 | 528 | B=C 附近 |
| d) 半容量驻留+预取 | 522 | 多 batch 且单 batch 偏大 |
| c) 单侧驻留 | 251 | 最少(b_core=1 且单侧可驻留的窄区) |
| B | M | N | K | 分支 | 说明 |
|---|---|---|---|---|---|
| 1 | 2048 | 2048 | 2048 | 转Matmul | 单 batch 纯 Matmul |
| 128 | 32 | 128 | 64 | IterBatch | MergeBatch 条件 3 不满足(单核搬移仅 80KB < 480KB) |
| 32 | 4096 | 4096 | 4096 | ASW_Basic | L1 五形态均不满足(M/N 太大),切 M/N |
| 2 | 8192 | 8192 | 1024 | ASW_Basic | B<C,P=2048≥32 |
| 16 | 256 | 256 | 128 | 降核 ASW | P=4<32,K=128 不满足 StreamK |
| 4 | 128 | 128 | 10240 | StreamK | P=0.25<32,K≥8192 |
| 2048 | 1024 | 1024 | 512 | IterBatch | 大 batch,形态 b |
| 8 | 512 | 512 | 512 | ASW_Basic | B<C,P=32 恰好满核 |
| 64 | 64 | 64 | 8192 | IterBatch | 小 M×N 但 K 大,形态 e |