Files
matmul-analysis/BMM算子优化分析_Release/BMM算子优化分析_v0.92.md

47 KiB
Raw Blame History

BMM 算子优化分析v0.92

目标芯片:昇腾 950PRDAV_3510。所有分支进入条件只含 case 形状参数B、M、N、K、dtype与芯片规格参数。


一、算子功能与接口说明

完成带 batch 的矩阵乘:C = A @ B + bias

  • 左矩阵 A[BatchA, M, K]dtype典型 ND可带转置
  • 右矩阵 B[BatchB, K, N]dtype典型 ND可带转置
  • 偏置 bias[B, 1, N],固定 ND可为空
  • 输出 C[BatchC, M, N]BatchC = broadcast(BatchA, BatchB)

二、符号与芯片参数约定

符号 含义 950PR 取值
C AIC 核数aicNum 32
Q₁₆ 单核 Cube BF16 峰值算力 486/C ≈ 15.2 TFLOPS
Q_AIV AIV 向量求和吞吐64 核合计) 64×128 fp32/拍×1.65GHz ≈ 13.5 Tops/s
L1 每核 L1 Buffer 512KB
L0A / L0B 每核 L0A / L0B 64KB / 64KB
L0C 每核 L0CFP32 累加4B/元素) 256KB
L2 L2 Cache 容量 128MB
W_L2 L2 读写带宽 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 芯片数值可能略有差异,换芯片时逻辑结构不变、只换常数表。


三、最优实现分析

3.1 性能模型

BMM 的执行是核内多级硬件流水的并行——Cube 计算MMAD、GM/L2→L1MTE2、L1→L0MTE1、L0C 写出Fixpipe各流水级时延可被双缓冲相互掩盖


T_{total} = \max\big(T_{MMAD},\; T_{MTE2},\; T_{MTE1},\; T_{Fixpipe}\;[,\;T_{Reduce}]\big)

总时延 = 最慢一级流水,优化的关键是对瓶颈级的优化。由此得到设计自由度——瓶颈交换搬移是瓶颈时可牺牲算力冗余计算换搬移效率计算是瓶颈时可牺牲搬移重复读取换计算效率。MergeBatch 是前者的典型ASW_Basic 切 M/N 是后者的典型。

case 固有算存比与 16bit 位宽平衡点:


AI = \frac{2MN}{M+N},\qquad AI_{full} = \frac{2MNK}{MK+KN+MN}

R_{16} = \frac{\text{Cube 峰值算力}}{\text{GM 带宽} / \text{元素字节数}} = \frac{486\ \text{TFLOPS}}{1.6\ \text{TB/s} \,/\, 2\ \text{B}} \approx 607.5\ \text{FLOP/元素}

其中 486 TFLOPS 是乘加各计一次后的标称算力;分母中的 2B 是 16bit 元素字节数,作用是把 GM 带宽折算成元素速率。

AI < R_{16} → 访存 Bound瓶颈在 MTE2反之计算 Bound瓶颈在 MMAD

3.2 实现本质逻辑

BMM 的实现本质是把数据分块tile由全部 AIC 核并行 + 串行完成这些分块的计算,再组合成最终结果:


C[B,M,N] = \Big\{\,C[B_u, M_i, N_j] = \sum_k A[B_u, M_i, K_k] \cdot B[B_u, K_k, N_j]\,\Big\}

分块有 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 L25.2TB/s vs GM 1.6TB/s+ swizzle 大部分吸收;切 K 的归约流量 ∝ grid_K×输出量且引入核间同步是结构性代价。整条分支决策树就是:按价格从低到高购买并行度,买不够才加价。

3.3 分支推导

  1. 问题规约BatchA=1 或 BatchB=1 → 折叠转普通 Matmul转MatmulK=0 / K=1 → Cube 无用,走 AIV 向量通路(特殊分支,与切分正交的前置判断);
  2. 先买免费的 BB ≥ C 时切 B 可满核。单 batch M×N 够大 → 逐 batch 算IterBatchM×N 太小 → 多 batch 合并成大 tile 算冗余算力换搬移效率MergeBatch
  3. B 买不满,加价买 M/N:切 M/N 或混合切,重复读交给 L2 + swizzleASW_Basic
  4. B/M/N 都买不满,才买昂贵的 K:核间切 K + 归约StreamK

由此得 6 大分支:转Matmul、特殊分支、IterBatch、MergeBatch、ASW_Basic、StreamK。重叠区(如 B≥C 且 M×N 中等时 MergeBatch 与 IterBatch 都合法)由端到端时延模型 T_{total} 仲裁;分支体系保证候选集完备无冗余。


四、可转 Matmul 分支

进入分支条件


BatchA = 1 \;\lor\; BatchB = 1

解释:单边 batch=1 的 BMM 与 Matmul 只差一个维度标签,直接复用 Matmul 的成熟优化体系。

实现方案

  • BatchB=1:左矩阵 [B,M,K] 的 batch 维与 M 维在 ND 下内存相邻紧排,直接视图为 [B·M,K],输出布局逐元素一致——零重排、零 split免费转换
  • BatchA=1:右矩阵折叠为 [K, B·N] 需一次真实转置重排O(B·K·N)),且输出存在置换需 scatter——有代价。A 较小($MK\cdot\text{dtype} \le L1$)时优先 A 常驻 L1、留在 BMM 分支内广播友好形态A 较大时按广播扩展后分别预估"BMM 分支"与"重排+转Matmul"的时延,择优。

五、MergeBatch 分支

核间切 B每核 b_{core} 个 batch核间无同步核内把 b 个 batch 合并计算:$[b,M,K]@[b,K,N] \Rightarrow [bM,K]@[K,bN]=[bM,bN]$BlockTrace 取块对角线得 $[b,M,N]$。交叉项被算出但丢弃(浪费比例 (b1)/b——进入该分支的 case 必然访存 Bound条件 5 保证),浪费的算力被搬移时延掩盖。

进入分支条件(汇总)

同时满足b0 = 单次合并计算的 batch 数下限b0 ≥ 2

  1. BatchA = BatchB \;\land\; b_{core} = B/C \ge 2b_0
  2. 2 \cdot (b_0 M)(b_0 N) \cdot 4\text{B} \le L0C
  3. b_{core} \cdot (MK + KN) \cdot \text{dtype} \ge min\_DatamountPerCore
  4. \max(MK,\; KN) \cdot \text{dtype} \ge min\_TileSize
  5. \dfrac{2MN}{M+N} < \dfrac{R_{16}}{b_0}

逐条解释

  1. batch 关系与每核份额:无广播才能逐 batch 对应合并;每核至少分到 2b_0 个 batch——b_0 是合并搬移有收益的最小合并数(取 22 组起步才能构成合并组间乒乓流水(即 $b_{core} \ge 4$)。

    MergeBatch vs IterBatch 分界建模

    执行模型两分支的核间切分相同——B 维切分到 C 核,每核 b_{core} = B/C 个 batch核内不切 M/N。差异在核内 K 维处理:

    • IterBatch(形态 d每 batch 切 K 为 n_K = \lceil K/k_{L1} \rceil逐块流水MTE2 搬 $A[M,k_{L1}]+B[k_{L1},N]$→L1→L0→Cube 部分和累加 L0C最后一块算完 fixpipe 排空 L0C→GM
    • MergeBatchb_0 个 batch 合并为 $[b_0M, K]@[K, b_0N]$,切 K 为 n_K^m 块,同理流水

    符号

    符号 含义 表达式
    k_{L1} L1 级 K 分块粒度(双缓冲) \min\big(K,\; L1/(2(M{+}N)\cdot\text{dtype})\big)
    n_K K 分块数 \lceil K/k_{L1} \rceil
    T_{load} 每 K 分块搬移时延 k_{L1}(M{+}N)\cdot\text{dtype}/BW_{pc}
    T_{comp} 每 K 分块计算时延 2MN \cdot k_{L1}/Q_{16}
    T_{write} 单 batch 输出写回时延 MN \cdot outB/W_{GM}
    T_{bd} batch 边界固定开销 fixpipe 启动握手(详见下文物理成因)
    BW_{pc} 单核 GM 带宽份额 W_{GM}/C

    端到端时延模型对每核执行过程建模。稳态流水unitflag 交叠 batch 间 drain/startup加上 batch 边界固定开销 $T_{bd}$(每边界一次):

    T_{iter} = \underbrace{b_{core} \cdot n_K \cdot \max(T_{load},\, T_{comp})}_{\text{稳态流水}} + \underbrace{T_{comp} + T_{write}}_{\text{末 batch drain}} + \underbrace{b_{core} \cdot T_{bd}}_{\text{边界开销}} T_{mb} = \underbrace{b_{core} \cdot n_K \cdot \max(T_{load},\, b_0 T_{comp})}_{\text{稳态流水}} + \underbrace{b_0(T_{comp} + T_{write})}_{\text{末合并 batch drain}} + \underbrace{\frac{b_{core}}{b_0} \cdot T_{bd}}_{\text{边界开销}}

    其中 MergeBatch 合并后 $k_{L1}^m = \min(K,; k_{L1}/b_0)$——L1 绑定($k_{L1}<K$)时 $k_{L1}^m = k_{L1}/b_0$K 截断($k_{L1}=K$)时 k_{L1}^m = K 不减半。以 L1 绑定为例。

    条件 5 保证合并后仍访存 Bound$T_{load} > b_0 T_{comp}$),两式相减:

    \Delta = T_{mb} - T_{iter} = \underbrace{(b_0-1)(T_{comp} + T_{write})}_{\text{drain 惩罚}} - \underbrace{b_{core}\Big(1-\frac{1}{b_0}\Big) T_{bd}}_{\text{边界节省}}

    分界条件MergeBatch 优于 IterBatch 当且仅当 $\Delta < 0$

    $$b_{core} > \frac{b_0 \cdot \text{penalty}}{T_{bd}},\qquad \text{penalty} = \begin{cases} (b_0-1)(T_{comp} + T_{write}) & \text{L1 绑定(}k_{L1} \lt K\text{} \ (b_0^2-1)T_{comp} + (b_0-1)T_{write} & \text{K 截断(}k_{L1} = K\text{} \end{cases}$$

    penalty 两种情形的含义drain 暴露 = 末合并batch 最后一个 K 分块的计算时延 + 输出写回时延——这部分没有下一块搬移可交叠,是流水线的 drain 尾部。

    • L1 绑定$k_{L1} < K$L1 容量限制了 K 分块粒度MergeBatch 合并后 tile 更大 → k_{L1} 减半 → 每分块计算量 b_0 倍 → drain 惩罚 $= (b_0-1)(T_{comp}+T_{write})$
    • K 截断$k_{L1} = K$):整个 K 装入 L1 一块MergeBatch 合并后 k_{L1} 不减半(仍等于 K→ 每分块计算量 b_0^2 倍(b_0^2 MN 输出 \times K 深度)→ drain 惩罚 $= (b_0^2-1)T_{comp} + (b_0-1)T_{write}$,更严格。

    小 MN 时 T_{comp} 小 → penalty 小 → MergeBatch 更容易赢;大 B 时 b_{core} 大 → 边界节省多 → MergeBatch 更容易赢。条件 1$b_{core} \ge 2b_0$)是该分界在典型 T_{bd} 下的保守近似,精确边界依赖 T_{bd} 实测标定。

    T_{bd} 的物理成因batch 边界处硬件必须完成的固定动作):

    从 IterBatch 的 kernel 侧源码(cmct/block/block_mmad_iterbatch.h)可直接观察到 batch 边界的 Set/Wait 同步序列:

    // 当前 batch 最后一个 K 分块结束后:
    SetFlag<M_FIX>(l0cEventID_ & 0x1);     // Cube → fixpipe: "L0C 可以排空"
    WaitFlag<M_FIX>(l0cEventID_ & 0x1);    // 等 fixpipe 确认开始 ← 串行握手,不可掩盖
    CopyOut(cTensor, c1Local_[...]);        // fixpipe 排空 L0C
    SetFlag<FIX_M>(l0cEventID_ & 0x1);     // fixpipe → Cube: "L0C 已空"
    // 下一 batch 第一个 K 分块:
    WaitFlag<FIX_M>(l0CEventID_ & 0x1);    // 等 L0C 可用(被 L0C 双缓冲掩盖)
    

    T_{bd} 由三个部分组成:

    1. Fixpipe 启动握手WaitFlag<M_FIX>fixpipe 从收信号到启动排空的延迟——地址/步长寄存器重配置(每 batch 输出写往 GM 不同区域)。串行、不可掩盖,是 T_{bd} 的主要成分;
    2. L0C 缓冲区切换WaitFlag<FIX_M>L0C 双缓冲——Cube 写一半、fixpipe 读另一半。被双缓冲掩盖,不贡献 $T_{bd}$
    3. Cube 流水线重建systolic array 在新 batch 首个 K 分块时需重新填充——fill 开销与 tile 尺寸无关,是固定成本。

    证据来源

    证据 文件 内容
    MMAD→Fixpipe 同步开销存在 CANN社区版9.2.0-beta.1/02_API参考/AscendC_API/180_..._UnitFlag.md "未开启UnitFlag功能时MMAD和FIXPIPE是指令级别的同步……流水串行";实测 7.3% 改善M=128,N=30720,K=64
    编译器默认插入同步指令 CANN社区版9.2.0-beta.1/01_AscendC算子开发/048_..._算子编译基本用法.md "默认会插入内存同步指令……这些同步指令会带来性能开销"
    同步机制是架构级关注点 00_硬件/昇腾950_NPU架构白皮书.pdf §4.1.6 新增 BufferID 同步机制替代 set_flag/wait_flag——"降低了同步的复杂度"

    注:非融合场景下 IterBatch/MergeBatch 仅使用 AICCubeAIV 不参与(kernel_matmul_iterbatch.h line 295-297if (!enableFusion) { if ASCEND_IS_AIV { return; } }),故不引用 AIV 侧同步文献。

    T_{bd} ≈ fixpipe 启动握手延迟(寄存器写入 + DMA 启动),量级估计为数十 ns。知识库未给出精确数值需实测标定。

    这些开销与 batch 的计算量无关,是每次 batch 切换的固定成本。大 batch 时被大量计算分摊,占比可忽略;小 batch 时每个 batch 绝对时延短边界开销占比显著。MergeBatch 把 b_0 个 batch 并为一次大矩阵计算,边界数降 b_0 倍——这是 MergeBatch 的结构性收益来源

    MergeBatch vs IterBatch 优势总结

    维度 IterBatch MergeBatch 差异来源
    稳态搬移吞吐 相同 相同 总搬移量、每块大小、总块数相同
    batch 边界开销 b_{core} \cdot T_{bd} b_{core}/b_0 \cdot T_{bd} MergeBatch 少 b_0 ← 核心优势
    drain 暴露 T_{comp} + T_{write} b_0(T_{comp} + T_{write}) IterBatch 少 b_0 倍 ← 核心劣势
    L0C 利用率 MN \cdot 4\text{B} b_0^2 MN \cdot 4\text{B} 访存 Bound 下不影响时延

    净收益 = 边界节省 - drain 惩罚 = $b_{core}(1-\frac{1}{b_0})T_{bd} - (b_0-1)(T_{comp}+T_{write})$。大 Bb_{core} 大)且小 MNT_{comp} 小)时 MergeBatch 最优。

    L0C 利用率说明:小 MN 时 IterBatch 的 L0C tile$MN \cdot 4\text{B}$)远小于 L0C 容量MergeBatch 合并后更接近满载。但访存 Bound 下计算被搬移掩盖L0C 利用率不影响总时延——不构成 MergeBatch 的优势

  2. L0C 容量:合并 b_0 个 batch 的输出块 $[b_0M, b_0N]$FP32 累加、双缓冲两份)必须放得下 L0C连最小合并都放不下合并无从谈起。

  3. 单核搬移总量:单核搬移数据总量低于 min_DatamountPerCore 时GM 带宽利用率上不去(重要性第 2 位的经验约束)。

  4. 搬移 tile 大小:单 batch 单矩阵的最大连续搬移块须达到 min_TileSize合并是在此之上进一步放大不是替代。

  5. 访存 Bound:合并把单次计算的算存比放大 b_0 倍后仍须低于 16bit 对应位宽算存比 $R_{16}$,保证瓶颈留在搬移侧,冗余算力被掩盖而非成为新瓶颈。

实现方案

Step 1合并数 bL0C + 算存比双上限)


b \le \sqrt{\frac{L0C}{2 \cdot MN \cdot 4\text{B}}},\qquad b < \frac{R_{16}(M+N)}{2MN}

b = \min\big(\text{两上限},\; b_{core}\big),\quad b_{L0} = b

b 尽量取 b_{core} 的因子(每次合并数均匀,负载与功耗更优)。

Step 2L0 级 K 粒度 $k_{L0}$


k_{L0} = \min\Big(\frac{L0A}{2\,bM\cdot\text{dtype}},\; \frac{L0B}{2\,bN\cdot\text{dtype}}\Big)\ \text{向下 16 对齐,且 } k_{L0}\cdot\text{dtype} \ge 128\text{B}

解释L0A/L0B 各 64KB、双缓冲两份装入合并后 bM 行(bN 列)× k_{L0} 的 fractal末项是 dValue 下限。

Step 3L1 级 $k_{L1}$、$b_{L1}$


k_{L1} \ge \min\big(k_{L0\_max},\; 256\text{B}/\text{dtype}\big),\qquad
2\,b_{L1}(M k_{L1} + k_{L1} N)\cdot\text{dtype} \le L1,\qquad
b_{L1} = \min(b_{L1\_max},\; b_{core}) \;\ge\; b

解释:k_{L1} 是 GM→L1 的 K 向粒度,按 dValue 推荐值 256B 取L1 双缓冲两份,每份驻留 b_{L1} 个 batch 的 A/B 各一块;b_{L1} \ge b 保证合并不断供。


六、IterBatch 分支

核间切 B每核 b_{core} \ge 1 个 batch核间无同步核内逐个 batch 做标准 Matmul 分块计算。无算力浪费、无跨 batch 依赖,是"切 B"最朴素的形态。

进入分支条件(汇总)

同时满足:

  1. BatchA = BatchB \;\land\; b_{core} = \lceil B/C \rceil \ge 1
  2. B \bmod C = 0 \;\lor\; B \bmod C \ge minCoreNum
  3. L1 容量约束四选一Step 为 >1 的整数):
    • a) b_{core}=1 \;\land\; (MK+KN)\cdot\text{dtype} \le L1
    • b) b_{core}>1 \;\land\; 2(MK+KN)\cdot\text{dtype} \le L1
    • c) \big(MK + 2\cdot\tfrac{KN}{Step}\big)\cdot\text{dtype} \le \dfrac{L1}{\min(b_{core},\,2)} \;\;\lor\;\; \big(KN + 2\cdot\tfrac{MK}{Step}\big)\cdot\text{dtype} \le \dfrac{L1}{\min(b_{core},\,2)}
    • d) 2\cdot\tfrac{K}{Step}(M+N)\cdot\text{dtype} \le L1
  4. c/d 切分后:搬移分块 \ge min\_TileSize \;\land\; dValue \ge 128\text{B}

逐条解释

  1. batch 关系与每核份额:无广播;每核至少 1 个 batch。

  2. 负载均衡:切 B 核间零共享零依赖,唯一系统性风险是负载不均。整除时完全均衡;不整除时尾波活跃核数须 ≥ minCoreNum保证尾波仍有足够核并发搬移重要性第 1 位的约束是核数)。

  3. L1 容量——核心要求:单 batch 计算不重复读

    重复读发生在单 batch 内部L1 放不下单 batch 完整的 M、N 维输入K 维允许切段放入kL1<K核内切 M 会在 K 循环中重复读 B、切 N 会重复读 A。IterBatch 进入与否不由算存比判定——即使 case 是计算 Bound重复读引入的额外搬移也可能把它重新拖回访存 Bound。所以进入条件直接由 L1 驻留形态刻画:

    • a) 每核 1 batch左右矩阵同时驻留 L1零重复读
    • b) 每核多 batchL1 同时放下 2 个 batchbatch 间直接乒乓;
    • c) 一侧驻留 + 对侧切 Kb_{core} \ge 1 统一式):驻留侧只搬一次、零重复读;对侧 K 段在预算内双缓冲(故系数 2预算分档由 batch 间流水掩盖决定b_{core}=1 时无 batch 边界、全量 L1 可用;b_{core} \ge 2 时预算减半L1/2另一半用于在计算当前 batch 期间预取下一 batch 的驻留侧——定量分析见下文"batch 间流水掩盖"
    • d) 两侧都切 KA/B 按 K 段成对流水(双缓冲故系数 2无驻留侧、batch 边界天然无缝,是驻留侧放不进 L1/2 时的兜底。
  4. 搬移效率下限:切分把粒度切小,必须守住 min_TileSize 与 dValue否则切分本身把带宽打崩。

batch 间流水掩盖分析c/d 的分工)

先明确流水结构fixpipe 开 unitflag 后,写出由硬件随路完成(每个 16×16×16 fractal 算完即自动搬出),写出侧不需要软件排流水;要掩盖的只有"读入MTE2: GM→L1↔ 计算Cube"。

  • (c) 且 $b_{core}=1$:每核只有 1 个 batch不存在 batch 边界;驻留侧全程复用,对侧 K 段双缓冲,段间无缝。
  • (c) 且 $b_{core} \ge 2$——关键问题:驻留侧在 batch 边界要整体换入,怎么掩盖? 若不减预算(全量 L1 给当前 batch到 batch 边界时下一 batch 的驻留侧(如 A$MK\cdot\text{dtype}$)必须整体换入,而当前 batch 尾部只剩几个 K 段的计算——掩盖不了整个驻留侧的换入,产生气泡。修正做法:每 batch 只占 L1/2驻留侧 + 对侧 K 段双缓冲都在这一半),另一半 L1 在计算当前 batch 期间预取下一 batch 的驻留侧。上一 batch 做最后一个 K 段时,下一 batch 的驻留侧已就绪、首个 K 段随即搬入——边界无气泡。无气泡的定量条件:驻留侧换入量 MK\cdot\text{dtype} \le 当前 batch 总搬入量 $(MK+KN)\cdot\text{dtype}$,恒成立;访存 Bound 下计算时间 ≥ 搬入时间,尾部窗口必然足够。
  • (d)(两侧都切 KK 段槽位在 batch 间完全同质连续——上一 batch 最后一段计算时搬下一 batch 第一段,天然无缝。当驻留侧单矩阵放不进 L1/2 时 (c) 不成立,由 (d) 接管,代价是两侧都有 K 段级重复读。

结论:(c) 统一了原 (c)/(d)——同一族"一侧驻留 + 对侧切 K",预算按 b_{core} 分档(b_{core}=1 全量 L1、b_{core} \ge 2 减半以容纳下一 batch 驻留侧预取);驻留侧超 L1/2 时由 (d) 接管。

实现方案

(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):一侧驻留 + 对侧切 K。假设驻留左矩阵右矩阵搬入的 K 向长度:


k_{L1\_b} = \min\Big(\frac{L1_{budget} - MK\cdot\text{dtype}}{N\cdot\text{dtype}},\; K\Big),\qquad L1_{budget} = \frac{L1}{\min(b_{core},\,2)}

b_{core} \ge 2 时另一半 L1 在计算期间预取下一 batch 的驻留侧,实现 batch 间无气泡衔接fixpipe 开 unitflag。

(d):两侧都切 K 段,k_{L1} 取满足容量与 dValue 的最大值L0C 按 batch 乒乓(各占 L0C/2batch 边界由硬件 fixpipe 自动排空、下一 batch 立即在另一半 L0C 累加。


七、StreamK 分支

进入分支条件(汇总)

  1. P = \dfrac{B \cdot MN \cdot 4\text{B}}{L0C} \le \dfrac{C}{2}
  2. $\dfrac{K}{grid_K} \ge \dfrac{256\text{B}}{\text{dtype}}$,其中 grid_K = \Big\lfloor \dfrac{C}{\lceil P \rceil} \Big\rfloor
  3. $K > \dfrac{grid_K^{,2}}{grid_K-1}\cdot\theta_c$\theta_c = \dfrac{Q_{16}}{2}\Big(\dfrac{8\text{B}}{W_{L2}}+\dfrac{1}{Q_{AIV}}\Big) \approx 12
  4. 工程约束:确定性等级 ≤ 1核间归约顺序不定ND 格式

逐条解释

  1. 并行缺口P 以"L0C 满载的输出基本块"为粒度估计不切 K 的最大并行度——基本块按 L0C 最大利用率取($M^t N^t \cdot 4\text{B} = L0C$),免去预先估计 M/N 具体切分。阈值取 C/2 而非 CStreamK 的定义就是 grid_K ≥ 2至少 2 路切 K且同一 batch 内所有输出块共享同一个 grid_Kgrid_K=2 时每块需要 2 个核,总核数需求 = ⌈P⌉ × 2 ≤ C即 P ≤ C/2等号成立时 grid_K=2 恰好填满 C 核)。若 P > C/2切 2 路就超核数2⌈P⌉ > C不切又浪费核——由降核 ASW_Basic 承接更合适。P ≤ C/2 意味着不切 K 时至多一半核有事做K 是唯一剩余的并行维度。

  2. 单核 K 段下限:每核 K 段内轴连续长度不小于 dValue 推荐值 256BBF16 为 128 元素),保证段内搬移效率不崩。

    grid_K 取值P 个输出 tile 各需 grid_K 个核,总核数 ⌈P⌉ × grid_K ≤ C → grid_K = ⌊C/⌈P⌉⌋最大化 K 并行度。例P=10, C=32 → grid_K=⌊32/10⌋=3P=5, C=32 → grid_K=⌊32/5⌋=6P=16, C=32 → grid_K=⌊32/16⌋=2。

  3. 归约代价可接受。StreamK 切 K 引入归约串行尾,收益判据——切 K 后芯片级总时延须小于不切 K降核 ASW

    降核 ASW 的芯片级→核级映射P < C/2 时 B×M×N 填不满 C 核,以 L0C 满载粒度切为 P 个输出 tile每 tile 尺寸 $M^t N^t = L0C/4\text{B}$\lceil P \rceil 个核各处理一个 tile。芯片级总时延 T_{alt} = 单 tile 流水时延 $T_{pipe}$(所有核并行)。$T_{pipe} = \max(T_{MMAD}^t,,T_{MTE2}^t)$,其中:

    
    T_{MMAD}^t = \frac{2 M^t N^t K}{Q_{16}} = \frac{2K}{Q_{16}}\cdot\frac{L0C}{4\text{B}},\qquad
    T_{MTE2}^t = \frac{K(M^t+N^t)\cdot\text{dtype}}{BW_{pc}}
    

    StreamK 的芯片级→核级映射:同样 P 个 tile每 tile 由 grid_K 个核共同完成(各算 K/grid_K 段)。流水时延 $T_{pipe}/grid_K$,归约时延 T_{Reduce}^t 串行追加。芯片级总时延:

    
    T_{SK} = \frac{T_{pipe}}{grid_K} + T_{Reduce}^t
    

    收益判据 T_{SK} < T_{alt} ⟺ $T_{Reduce}^t < T_{pipe}\left(1-\dfrac{1}{grid_K}\right)$。等价于 $T_{pipe} > \dfrac{grid_K}{grid_K-1}\cdot T_{Reduce}^t$——α = grid_K/(grid_K-1) 由流水分析导出grid_K=2 时 α=2非经验值。

    T_{Reduce}^t 构成(每 tile部分和驻留 L2、AIV 归约;$M^t N^t = L0C/4\text{B}$,符号定义见 §二):

    
    T_{Reduce}^t = \underbrace{\frac{grid_K \cdot M^t N^t \cdot 4\text{B}}{W_{L2}}}_{\text{AIC 写部分和}} + \underbrace{\frac{grid_K \cdot M^t N^t \cdot 4\text{B}}{W_{L2}}}_{\text{AIV 读回}} + \underbrace{\frac{grid_K \cdot M^t N^t}{Q_{AIV}}}_{\text{AIV 求和}} + \underbrace{\frac{M^t N^t \cdot outB}{W_{L2}}}_{\text{写回}}
    

    K 闭式阈值推导——分两种瓶颈情形:

    计算 Bound$T_{pipe} = T_{MMAD}^t$),代入判据:

    
    \frac{2K}{Q_{16}}\cdot\frac{L0C}{4\text{B}}\cdot\frac{grid_K-1}{grid_K} > grid_K\cdot\frac{L0C}{4\text{B}}\Big(\frac{8\text{B}}{W_{L2}}+\frac{1}{Q_{AIV}}\Big) + \frac{L0C}{4\text{B}}\cdot\frac{outB}{W_{L2}}
    

    两边除以 $L0C/4\text{B}$tile 输出元素数),tile 尺寸消去——K 阈值不依赖 M、N 的具体值:

    
    K > \frac{grid_K^2}{grid_K-1}\cdot\theta_c,\qquad
    \theta_c = \frac{Q_{16}}{2}\Big(\frac{8\text{B}}{W_{L2}}+\frac{1}{Q_{AIV}}\Big)
    

    代入数值($Q_{16}$=15.2 TFLOPS$W_{L2}$=5.2 TB/s$Q_{AIV}$≈13.5 Tops/s

    
    \theta_c = \frac{15.2\times10^{12}}{2}\Big(\underbrace{\frac{8}{5.2\times10^{12}}}_{1.54\,\text{ps/元素}} + \underbrace{\frac{1}{13.5\times10^{12}}}_{0.07\,\text{ps/元素}}\Big) = 7.6\times10^{12} \times 1.61\times10^{-12} \approx 12
    

    L2 读写1.54 ps/元素是主导项AIV 求和0.07)仅占 5%。grid_K=2→K>494→K>668→K>112。

    访存 Bound$T_{pipe} = T_{MTE2}^t$),同理(M^t N^t/(M^t+N^t) 不消去,但阈值远低于计算 Bound

    
    K > \frac{grid_K^2}{grid_K-1}\cdot\frac{M^t N^t}{M^t+N^t}\cdot\theta_m,\qquad
    \theta_m = \frac{BW_{pc}}{\text{dtype}}\Big(\frac{8\text{B}}{W_{L2}}+\frac{1}{Q_{AIV}}\Big) \approx 0.04
    

    访存 Bound 阈值远低于计算 Bound——两情形阈值比值:

    
    \frac{\theta_m \cdot M^t N^t/(M^t+N^t)}{\theta_c} = \frac{2\cdot BW_{pc}}{Q_{16}\cdot\text{dtype}}\cdot\frac{M^t N^t}{M^t+N^t} = \frac{M^t N^t/(M^t+N^t)}{304}
    

    M^t N^t/(M^t+N^t) 的范围tile 面积 M^t N^t \le L0C/4\text{B} = 65536 元素L0C 容量上限)。由均值不等式 $M^t+N^t \ge 2\sqrt{M^t N^t}$,比值上界为 $\sqrt{M^t N^t}/2 \le \sqrt{65536}/2 = 128$(正方形 tile 取到);极端长宽比($M^t{=}16, N^t{=}4096$)时下界 $\approx 16$。StreamK case 的 tile 通常接近正方形 → 比值 \sim O(64128),上界 128。无论取何值$128/304 \approx 0.42 < 1$——访存 Bound 阈值恒低于计算 Bound。直觉:访存 Bound 时 $T_{pipe} = T_{MTE2}^t > T_{MMAD}^t$,瓶颈时延更大,归约预算更充裕。汇总条件取计算 Bound 阈值(保守,同时覆盖两种情形)。

    注意 θ_c 对 workspace 落点敏感:部分和落 GM 时读写带宽从 5.2TB/s 降到 ~0.64TB/sθ_c 升至约 97。设计时应优先保证 workspace 驻留 L2。

  4. 工程约束:归约顺序不定引入浮点非确定性,确定性等级 2/3 的业务禁用。

源码对照batch_matmul_v3_basic_streamk_tiling.cpp 中 K 的固定门槛为 CeilAlign(K,256) ≥ max(8192, aicNum×256B/dtype)

  • aicNum×256B/dtype = 32×128 = 4096BF16= dValue 下限256B在最大 grid_K=C 下的保障——对应条件 2
  • 8192 = 32×256 = C×512BBF16= dValue 推荐值512B在最大 grid_K=C 下的保障——同为条件 2取推荐值而非下限。

max 取更严格的 8192。修正后的归约阈值θ_c≈12grid_K=32 时 K>396远低于 8192说明 8192 的绑定约束是 dValue条件 2不是归约代价条件 3。源码不动态计算 grid_K用固定阈值同时覆盖条件 2 的最保守情形和条件 3是两条条件的保守合并近似。

实现方案

核间组织:先按 B/M/N 切出输出块,剩余核预算折成 K 向份数:


blocksPerBatch = \Big\lfloor \frac{C}{B} \Big\rfloor,\qquad
grid_K = \frac{blocksPerBatch}{mCnt \cdot nCnt}

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 写出。

归约

  • workspace + AIV 归约(确定性):部分和写 workspace每核 256×256×4B另加 20MB 核间通信区),workspace 优先驻留 L2——归约读写走 5.2TB/s 的 L2 口而非 GMAIV 从 L2 读回各段部分和、在 UB 内求和后写回(数据流 GM/L2→UB→AIV→UB→L2/GMAIC:AIV=1:2
  • AtomicAdd非确定性:部分和直接原子累加到输出 GM省一遍读回确定性等级 >1 禁用。

参数搜索grid_K 从 2 起按 2 的幂递增,取同时满足条件 2/3 的最小值;都不满足则退为降核 ASW_Basic。


八、ASW_Basic 分支

进入分支条件(汇总)

  1. P = \dfrac{B \cdot MN \cdot 4\text{B}}{L0C} \ge C
  2. 无 batch 结构限制BatchA=BatchB、交叉广播均可典型进入路径$B < C$(切 B 买不满核),或 B \ge C 但 IterBatch/MergeBatch 条件不满足时的兜底
  3. 降核模式P < C 且不满足 StreamK 进入条件 → 只用 \lceil P \rceil 个核,其余核闲置

逐条解释

  1. 并行度补齐:以 L0C 满载为基本块粒度B×M×N 能切出至少 C 个独立输出块,则切 M/N或混合切并行度够用。切 M/N 的固有代价是共享矩阵被多核重复读,但共享部分驻留 128MB L2 时重复读以 5.2TB/s 命中 L2 而非 1.6TB/s 的 GM代价大部分被吸收。
  2. 兜底性质ASW_Basic 是实践中最常命中的分支——B 可大可小可等 1交叉广播也由此承接对广播侧做 L1/L2 驻留,共享关系与切 M/N 同构)。
  3. 降核模式P < C 且 K 也不够格走 StreamK 时,并行度凑不满核。此时与其强行把 M/N 切得更碎tile 跌破 min_TileSize、dValue 跌破 128B搬移效率崩塌反而更慢不如只用 ⌈P⌉ 个核、每核承担一个完整输出块L0C 满载粒度),其余核闲置。这类 case 的时延绝对值小,继续切分引入的调度与搬移效率损失大于并行收益——降核是理性选择而非偷懒。

实现方案

1、核间切分维度选择按共享代价从低到高:切 B零共享先试→ 切 M右矩阵 KN\cdot\text{dtype} \le L2 则驻留 L2→ 切 N对称→ 混合切(靠 swizzle + L2 切分管理)→ 降核(见第 6 条)。

2、swizzleASW 滑窗蛇形

问题:核间切 M/N 后,同一时刻 C 个核各算一个输出块,它们所需的 A 行块与 B 列块集合就是当前"活跃工作集"。若按行优先顺序朴素分配,一波 C 个块横跨的 A 行、B 列很宽,活跃工作集超过 L2 就回 GM 读1.6TB/s重复读代价真实发生。swizzle 要做的就是编排输出块的执行顺序,把每一波核的活跃工作集压到最小。

做法:把 M 向每 W 个基本块划为一个"窗口",遍历顺序为"窗口内先扫 M、扫满 W 行再进下一列 N一个窗口扫完再进下一个窗口",且奇数窗口行 N 向反向(蛇形)。效果有二:

  • 同一波 C 个核的块集中在同一个窗口内 ⇒ 活跃 A 行块只有 W 个、活跃 B 列块只有 C/W 条带;
  • 蛇形反向使相邻窗口行首尾相接——上一窗口末尾的 B 列带与下一窗口开头的 B 列带是同一条,跨窗口切换时工作集增量最小。

W 怎么取:一波 C 个块的 L2 足迹约为


footprint \approx \big(W \cdot M^t K + \tfrac{C}{W} \cdot K N^t\big) \cdot \text{dtype}

由均值不等式,W + C/WW = \sqrt{C} 处取最小——窗口越接近"方形"W 行 × C/W 列),足迹越小。同时 W 须整除 C保证每个窗口恰好被整数波核覆盖、窗口边界不把波次切碎。合起来即


W = \max\{\,d \mid d \mid C,\; d \le \lfloor\sqrt{C}\rfloor\,\}

C=32 时 $\sqrt{32} \approx 5.66$,因子 {1,2,4,8,…} 中不超过它的最大者是 4故 W=4。

实例C=32W=4M̃=8Ñ=8数字为块的全局执行顺序一波 32 块):

窗口0N 正向)                窗口1N 蛇形反向)
      ν0  ν1  ν2 …  ν7               ν0   ν1  …  ν7
 μ0    0   4   8 …  28          μ4   60   56  …  32
 μ1    1   5   9 …  29          μ5   61   57  …  33
 μ2    2   6  10 …  30          μ6   62   58  …  34
 μ3    3   7  11 …  31          μ7   63   59  …  35

块 31 = (μ3, ν7),块 32 = (μ4, ν7)——相邻两个块共用同一条 B 列带ν7窗口切换几乎零增量。对比朴素行优先Ñ=16 时):一波横跨 2 个 A 行块 + 16 条 B 列带,足迹 (2·M^t + 16·N^t)·K·dtype滑窗为 (4·M^t + 8·N^t)·K·dtype——M^t≈N^t 时足迹缩小 1/3。

窗内为什么不蛇形(对上例中"块 3=(μ3,ν0) → 块 4=(μ0,ν1) 而非 (μ3,ν1)"的说明):蛇形的收益来自"相邻遍历段共享边界数据",要分两种边界看:

  • 窗内列间边界ν0→ν1相邻两段共享的是同一组 A 行块W 个),它们在整个窗口期间全程驻留 L2,无论按什么顺序扫,工作集不变——窗内蛇形零收益;
  • 窗口行边界窗口0→窗口1A 行整体换血μ0..3 → μ4..7),此时 B 列带的连续性决定换血成本——不蛇形则下一窗口从 ν0 开始LRU 上最久未用、早已被挤出 L2 的冷带),蛇形则延续上一窗口末尾的 ν7最热线带蛇形只标在窗口行号上(源码 BatchMatMulAswBlock::UpdateBasicIndex:仅 rowIdx 为奇时 n 反向,窗内 m 最快序不反向),正是这个收益结构的直接实现。

3、L2 切分(工作集超 L2 时)

切的是什么L2 切分切的是输出平面——把 M×N 平面切成 mL2TileNum × nL2TileNum 个大矩形块(每块 = 若干基本块的集合),使"该块所需的 A 行带 + B 列带"输入工作集 ≤ L2 可用读入空间;块内所有输出基本块算完再进下一块,输入只在跨块时换一次。

为什么需要它:滑窗压缩的只是"同一波"的足迹;若整个工作集超 128MB L2跨波次复用落空——上一波窗口的 A 行早被挤出,下一波又得回 GM 读。且 L2 是读写共用的:输出经 fixpipe 写出时若驻留 L2dirty会压缩读入可用空间若直写 GM则占用与读共享的 1.6TB/s 总线。所以 L2 切分必须与写出策略联合决策。记输入总量 $S_{in} = B(MK+KN)\cdot\text{dtype}$,输出总量 $S_{out} = B \cdot MN \cdot outB$。

两个不变量(一切分析的起点):

  • GM 流量下界 $= S_{in} + S_{out}$:输入至少读一遍、输出最终至少要写一遍到 GM与 L2 策略无关;
  • L2 读入可用空间:$L2_{read} = L2 - S_{out}^{resident}$S_{out}^{resident} 为驻留 L2 的输出量)——写出驻留 L2 会压缩读入空间,这是写出策略影响读入复用的通道。

重复读倍率r_{in} = GM 输入流量 / $S_{in}$。r_{in} = 1 表示每个输入数据从 GM 只读一遍(后续复用全在 L2 命中)——这是 GM 输入流量的下界L2 管理的全部目标就是让 r_{in} 尽量接近 1。

先判定写出会不会 Bound。平均写出带宽需求:


BW_{out} = \frac{S_{out}}{T_{MMAD}} = \frac{B \cdot MN \cdot outB}{2BMNK\,/\,(C \cdot Q_{16})} = \frac{C \cdot Q_{16} \cdot outB}{2K}

只与 K、outB 有关K 越小单位时间输出越密。例BF16 输出C·Q₁₆=432 TFLOPSK=512 → 844 GB/sK=256 → 1.69 TB/s已超 GM 总线——此时任何策略都写出 BoundL2 缓冲只能削峰fixpipe 以 5.2TB/s 写 L2 吸收突发),平均速率仍受总线限制,应预期 Fixpipe 成为 T_{total} 的 max 项。

分场景决策

场景 A$S_{in} + S_{out} \le L2$(全驻留)。输入读一遍($r_{in}=1$),输出驻留 L2dirty异步回写 GM——写出走 5.2TB/s L2 写口,不与读争,也削平了 GM 写突发。无需切分。

场景 BS_{in} \le L2 但 $S_{in} + S_{out} > L2$(输入能驻留,加上输出超了)。策略:输入驻留、输出直写 GM。理由链:

  1. S_{in} \le L2 ⇒ 全部输入可驻留 L2跨波次复用全部命中 ⇒ $r_{in} = 1$GM 输入流量达到下界 $S_{in}$
  2. 输出在本算子内只写不读、零复用收益;若输出也驻留 L2dirty超出 L2 的部分会把输入挤出——被挤出的输入后续得回 GM 重读 ⇒ $r_{in} > 1$GM 流量超出下界;
  3. 故让输出直写 GMfixpipe L0C→GM不占 L2把 128MB 全部留给输入,保住 $r_{in} = 1$——GM 总流量保持下界 $S_{in} + S_{out}$
  4. 代价是输出即刻占用 GM 写带宽(与读共享总线),须校验总线不爆:$(S_{in} + S_{out})/T_{MMAD} \le W_{GM}$。

B=8、M=N=4096、K=512、BF16——$S_{in}$≈67MB ≤ L2$S_{out}$≈268MB 直写 GMT_MMAD≈318µs总流量速率 (67+268)MB/318µs ≈ 1.05TB/s < 1.6TB/s ✓。

场景 C$S_{in} > L2$(输入本身超)。必须 L2 切分。输出直写 GM 以最大化 $L2_{read}$,切分数满足


mL2TileNum \times nL2TileNum \;\ge\; \frac{S_{in}}{L2_{read}}

每块输入工作集 ≤ $L2_{read}$,逐块计算——块内滑窗复用充分,块间只发生一次性换入。块内分配用错位分核(对角线分配):线性块号先取 mn 方向叠加随块号递增的相位偏移,使同一时刻各核落在 M×N 平面的不同对角线上——避免多核同一拍并发读同一行 A / 同一列 B 的同一地址同地址并发读会串行化等效带宽打折。例8 核、4×4=16 个基本块k0~k7 为核号):

行优先(不错位):              错位分核(对角线):
      n0  n1  n2  n3                  n0  n1  n2  n3
 m0   k0  k1  k2  k3             m0   k0  k4  .   .
 m1   k4  k5  k6  k7             m1   .   k1  k5  .
 m2   .   .   .   .              m2   .   .   k2  k6
 m3   .   .   .   .              m3   k7  .   .   k3

同一波A 行带 m0 被 k0~k3 同读     同一波:每行带、每列带
4 路同地址冲突)                  最多 2 核同读(冲突 4→2

冲突度量与优选规则:


transConflict = \max\big(\lceil C / mCnt \rceil,\; \lceil C / nCnt \rceil\big) \le 6

即同一时刻并发核访问同一 A/B 块的最大冲突数不超过阈值(经验值 6切分方案中优先选尾波不满载占比小拖尾 < 一半)的。遍历大方向由 calOrder 决定0=M 优先、1=N 优先),按形状选共享矩阵更能驻留 L2 的方向。例B=64、M=N=2048、K=1024、BF16——$S_{in}$≈537MB > L2输出直写$L2_{read}$=128MB切 3×2=6 块(每块输入约 89MB ≤ 128MB总流量约 1.07GBT_MMAD≈1.27ms,速率 844GB/s < 1.6TB/s ✓。

补充:若输出会被后续算子立即消费(融合场景),输出驻留 L2 让下游读命中,场景 B/C 的策略反过来;本文按单算子边界分析。

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 全载)。

6、降核模式实现tiling 时 usedCoreNum = ⌈P⌉(不强制 C基本块在 L0C 容量内取最大($M^t N^t \cdot 4\text{B} \le L0C$每核按标准核内流水L1→L0→Cube→L0C→Fixpipe处理自己的输出块核间无共享无依赖无需 swizzle 与 L2 切分。降核后 GM 并发搬移核数若 < minCoreNum带宽利用率上限被压低——这正是降核区 case 时延的瓶颈所在,也是"时延绝对值小、不再继续优化"的定量注脚。


九、特殊分支

  • K=0无任何计算C = bias 或 0纯 AIV 写值;
  • K=1:退化为逐元素乘 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。

十、case 遍历:各分支的覆盖区域

方法论说明

采样方式B ∈ [1, 2048] 全量遍历2048 个值M/N/K ∈ [1, 10240] 对数网格采样 60 点/维值按指数增长1, 2, 3, 5, 7, 10, 14, 19, 26, 35, ...)。总 case 数 3.2 亿,耗时约 4 分钟。

为什么不做全量遍历:全量 = 2048 × 10240³ ≈ 2.2×10¹⁵ 个 case。Python 分类器约需 1400 万小时C 实现约需 6 万小时——完全不可行。对数采样在小值区密集、大值区稀疏,恰好覆盖了分支边界集中的区域。

分布依赖测度:对数均匀采样和线性均匀采样给出的分支占比不同。对数采样在小值区密集特殊分支K=0/1、降核 ASWP 小、StreamKK 大但 M/N 小)的占比被放大;线性采样被大 shape 主导M/N/K > 512 占 [1,10240] 的 95%+ASW_Basic 占比显著升高。本文遍历的目的是验证覆盖性(无空洞),不是统计真实工作负载的分布。

分支覆盖统计对数采样B 全量 2048M/N/K 60 点/维,共 3.2 亿 case

分支 case 数 占比 B 范围 区域特征
ASW_Basic 1.75 亿 54.1% 2 ~ 2048 通用B<C 且 P≥C或 B≥C 但 L1 四形态不满足M/N 大)
MergeBatch 6303 万 19.5% 97 ~ 2048 b_{core}\ge 4MN \le 8192 等五条全过
降核 ASW_Basic 4051 万 12.6% 2 ~ 2048 P<C 且 K 不满足 StreamK 阈值 → 只用 ⌈P⌉ 核
IterBatch 3804 万 11.8% 32 ~ 2048 B≥C、负载均衡、L1 四形态之一满足
特殊分支 597 万 1.9% 任意 K=0 / K=1
StreamK 25 万 0.08% 2 ~ 128 P<C/2 且 K≥8192
转Matmul 15 万 0.05% B=1 单边 batch=1

对照:线性等距采样M/N/K 步长 256B 全量,共 1.3 亿 case下 ASW_Basic 占比升至 92.1%MergeBatch 降至 3.6%——因为线性采样被大 shape 主导。两种测度的结论一致:无空分支、无覆盖空洞,只是占比不同。

IterBatch 四形态命中分布(对数采样)

形态 命中数 占比 说明
b) 双 batch 乒乓 2684 万 70.5% 最多B 大且单 batch 较小
d) 两侧切 K 741 万 19.5% 次之K 可切段的通用兜底
c) 一侧驻留+对侧切 K 370 万 9.7% 单侧可驻留(含 b_core≥2 的半预算预取档)
a) 单 batch 全驻留 9 万 0.2% B=C 附近的窄区

典型边界 case

B M N K 分支 说明
1 2048 2048 2048 转Matmul 单 batch 纯 Matmul
128 64 64 512 MergeBatch 五条全过:$b_{core}$=4MN=4096≤8192单核搬移 512KB≥480KBAI=64<304
128 64 64 256 IterBatch 与上行仅 K 不同:单核搬移 256KB < 480KB条件 3 不满足 → 落 IterBatch形态 b
512 128 128 128 IterBatch MN=16384 > 8192MergeBatch 条件 2 不满足 → 落 IterBatch
32 4096 4096 4096 ASW_Basic L1 四形态均不满足M/N 太大),切 M/N
2 8192 8192 1024 ASW_Basic B<CP=2048 ≥ 32
16 256 256 128 降核 ASW P=4 < 32 且 K=128 不满足 StreamK → 用 4 核,其余闲置
4 128 128 10240 StreamK P=0.25 < 32K≥8192
2048 1024 1024 512 IterBatch 大 batch形态 b
8 512 512 512 ASW_Basic B<CP=32 恰好满核
64 64 64 8192 IterBatch 小 M×N 但 K 大,形态 d

遍历结论

  1. 七个分支全部有真实 case 命中无空分支覆盖矩阵无空洞P<C 且 K 小的残余由降核 ASW_Basic 兜底——此时时延绝对值小,调度开销主导,分支选择不敏感);
  2. MergeBatch 的区域由条件 2$MN \le 8192$)与条件 3min_DatamountPerCore夹出:小 M×N 且单核搬移量足够的大 batch case两个条件缺一不可。典型分界对照B=128/M=N=64 时 K=256 → 单核搬移 256KB 不达标落 IterBatchK=512 → 512KB 达标进 MergeBatch
  3. IterBatch 与 ASW_Basic 的分界就是 L1 四形态是否满足:单 batch 输入 (MK+KN)\cdot\text{dtype} 相对 L1 的比例决定归属——这正是"核内零重复读"原则的定量体现;
  4. StreamK 的区域为 P<C/2 且 K≥8192B 小、M/N 小、K 大的"细长" caseC/2 ≤ P < C 且 K 大的 case 由降核 ASW_Basic 承接(切 2 路 K 会超核数,不切又不满核,不如直接用 ⌈P⌉ 核);
  5. 降核 ASW_Basic 是 P<C 且 K 小区域的理性归宿(占 12.6%):并行度凑不满、切 K 又不划算时,只用 ⌈P⌉ 个核、每核一个 L0C 满载输出块比强行碎切tile 跌破搬移效率下限)更快。