CANN/cannbot-skills: Matmul布局指南

📅 2026/7/16 17:50:37
CANN/cannbot-skills: Matmul布局指南
Matmul Layout 与数据格式开发指南【免费下载链接】cannbot-skillsCANNBot 是面向 CANN 开发的用于提升开发效率的系列智能体本仓库为其提供可复用的 Skills 模块。项目地址: https://gitcode.com/cann/cannbot-skills本文覆盖 Matmul 算子所有 Layout 与数据格式相关的开发指导包括 ND/DN/NZ/ZN 格式定义、 数据生成流程、LayoutPtn 选型、kernel 适配点和排障。API 参考LayoutPattern 谱系、MakeFrameLayout 签名、Routing 表等 API 级内容详见tensor-api-reference.md。1. 格式定义NZ 是一种分形存储格式。对原始 tensor(dim0, dim1)ND 排布NZ 格式的物理排列为(dim1/C0, dim0/16, 16, C0) 其中C0 32 / sizeof(dtype)fp16/bf16: C016, fp8/int8: C032, fp4: C064。 非对齐时先补 0 到 16 对齐。记矩阵计算的三个维度分别为 M、K、N即 A[M,K] × B[K,N] C[M,N]则 A、B 矩阵的各种格式情况对应的 GM 数据排布及 LayoutPtn 如下。A 矩阵transAformat物理排列LayoutPtnfalseND(M, K)NDExtLayoutPtnfalseNZ(K/C0, M/16, 16, C0)NZLayoutPtntrueND(K, M)DNExtLayoutPtntrueNZ(M/C0, K/16, 16, C0)ZNLayoutPtnB 矩阵transBformat物理排列LayoutPtnfalseND(K, N)NDExtLayoutPtnfalseNZ(N/C0, K/16, 16, C0)NZLayoutPtntrueND(N, K)DNExtLayoutPtntrueNZ(K/C0, N/16, 16, C0)ZNLayoutPtn2. 数据生成流程随机数据生成固定以数学描述 A(M,K)、B(K,N) 生成golden 计算固定为 A B。 数据生成后分两路一路计算 golden 保存为 bin另一路按 kernel 入参要求对 A、B 各自独立做转换后保存为 bin。转换函数def to_nz_format(data, c0): ND (dim0, dim1) → NZ 分形 (dim1/C0, dim0/16, 16, C0) c0 必须按 dtype 显式传入fp16/bf1616, int8/fp832, fp464 dim0, dim1 data.shape dim0_pad ((dim0 15) // 16) * 16 dim1_pad ((dim1 c0 - 1) // c0) * c0 padded torch.zeros((dim0_pad, dim1_pad), dtypedata.dtype) padded[:dim0, :dim1] data b_4d padded.reshape(dim0_pad // 16, 16, dim1_pad // c0, c0) return b_4d.permute(2, 0, 1, 3).contiguous()关键permute(2, 0, 1, 3)产生物理排列(dim1/C0, dim0/16, 16, C0)。 常见错误是写成permute(0, 2, 1, 3)产出(dim0/16, dim1/C0, 16, C0) 与 tensor_api NZ layout 的 stride 不匹配导致全 FAIL。注意c0参数必须按 dtype 显式传入。不同 dtype 的 C0 不同fp16/bf1616, int8/fp832。gen_data 范例def gen_data(m, k, n, dtypetorch.int8, trans_aFalse, trans_bFalse, a_formatnd, b_formatnd): C0 32 // torch.tensor([], dtypedtype).element_size() # 动态计算 C0 # 生成原始数据固定 shape if dtype.is_floating_point: A torch.randn(m, k, dtypedtype) B torch.randn(k, n, dtypedtype) else: A torch.randint(-128, 128, (m, k), dtypedtype) B torch.randint(-128, 128, (k, n), dtypedtype) # golden 计算固定 A B golden (A.float() B.float()).to(torch.bfloat16) # 按 kernel 需求转换 A先 transpose再 to_nz_format if trans_a: A_for_kernel A.T.contiguous() else: A_for_kernel A if a_format nz: A_bin to_nz_format(A_for_kernel, C0) else: A_bin A_for_kernel # 按 kernel 需求转换 B先 transpose再 to_nz_format if trans_b: B_for_kernel B.T.contiguous() else: B_for_kernel B if b_format nz: B_bin to_nz_format(B_for_kernel, C0) else: B_bin B_for_kernel3. LayoutPtn 选择Launcher 直接传 tensor_api pattern 作为模板参数Pattern含义构成 GM shapeAscendC::Te::NDExtLayoutPtn行主序NDA: (M,K); B: (K,N); C: (M,N)AscendC::Te::DNExtLayoutPtn列主序DNA: (K,M); B: (N,K)AscendC::Te::NZLayoutPtnNZ 分形预重排A 或 B 离线重排为 NZ 格式AscendC::Te::ZNLayoutPtnZN 分形预重排A 或 B 离线重排为 ZN 格式转置场景TagToTransPattern在layout_utils.h派生 transA/transBPatterntrans 值NDExtLayoutPtnfalseDNExtLayoutPtntrueNZLayoutPtnfalseZNLayoutPtntrue常见错误新增 transAtrue 但 launcher 里 layoutA 仍硬编码 NDExtLayoutPtn → 编译过但 ≈100% mismatchK 维和 M 维错位。4. L1 layout 自动选择使用L1LayoutHelperLayoutPtn, Type, TransVal统一处理ND/DN 输入L1 按 trans 标志选 NZ/ZN走硬件 ND→NZ 格式转换NZ/ZN 输入L1 与 GM pattern 一致走 NZ→NZ / ZN→ZN 块拷贝省掉格式转换带宽using MakeLayoutAL1 typename L1LayoutHelperLayoutA, AType, transA::type; using MakeLayoutBL1 typename L1LayoutHelperLayoutB, BType, transB::type;L1→L0A/L0B 的 routing 由 tensor_api 根据 DstPatternL0ANZ, L0BZN和 SrcPatternL1 的 pattern自动派发NORMAL 或 TRANS 模式无需手动添加LoadDataTrait{transposedtrue}。5. GM 端 layout 构造GM 端 NZ layout 构造必须使用正确的 C0// ⚠️ 关键FrameLayoutFormat 默认 C016基于 uint16_tint8/fp8 需要 C032 static constexpr uint64_t A_C0 32 / sizeof(AType); static constexpr uint64_t B_C0 32 / sizeof(BType); using MakeLayoutA AscendC::Te::FrameLayoutFormatLayoutA, AscendC::Std::IntA_C0; using MakeLayoutB AscendC::Te::FrameLayoutFormatLayoutB, AscendC::Std::IntB_C0;⚠️ 致命陷阱FrameLayoutFormatNZLayoutPtn默认使用LayoutTraitDefault其 C0 32/sizeof(uint16_t) 16。 当 AType/BType 为 int8 时正确的 C0 应为 32/sizeof(int8_t) 32。 C0 错误会导致 GM 端 NZ layout 的 Shape[Column][1]列块数和 Stride[Column][1]列块间距全部错误CopyGmToCbufAlignV2NZ读取错误的 blockCount/blockLen/srcStride多 tile 场景全 FAIL。 L1 端的L1LayoutHelper已正确按 dtype 计算 C0此问题仅影响 GM 端 layout 构造。6. Host 侧 size 计算ND 格式size dim0 * dim1 * sizeof(dtype)NZ 格式buffer size 按物理维度计算// CalcNzSize(dim0, dim1, c0): 分形物理排列的字节数 uint64_t dim0Blocks (dim0 15) / 16; uint64_t dim1Blocks (dim1 c0 - 1) / c0; return dim1Blocks * dim0Blocks * 16 * c0 * sizeof(dtype);按转换后的物理数据 shape 传入参数A 矩阵golden 固定有 A[M,K]transAformat物理数据 shapeCalcNzSize 参数falseNZ(M, K)CalcNzSize(m, k, c0)trueNZ(K, M)CalcNzSize(k, m, c0)B 矩阵golden 固定有 B[K,N]transBformat物理数据 shapeCalcNzSize 参数falseNZ(K, N)CalcNzSize(k, n, c0)trueNZ(N, K)CalcNzSize(n, k, c0)7. Kernel 端适配点5 个适配点 1 —layout_utils.h新增 NZ 的TagToTrans特化NZLayoutPtn和ZNLayoutPtn各一个IsNzOrZn检测 L1LayoutHelper辅助模板。适配点 2 —matmul_kernel.h/matmul_kernel_fused.hGM 端 layout 构造必须使用正确的 C0详见 §5。适配点 3 —matmul_block_mmad.hMakeLayoutAL1/MakeLayoutBL1改用L1LayoutHelper详见 §4。适配点 4 —matmul_kernel.hSlice 顺序——kernel 层做 M/N-slice保留 fullK strideblock 层只做 K-slice// Kernel 层N-slice保留 fullK strideNZ column stride 依赖 fullK auto gmBlockB gmB.Slice(Coord(0, nPos), Shape(fullK, tileN)); // Block 层K-slicegmBlockB 已经是 N-tile 大小 auto gmTileB gmB.Slice(Coord(kL1Offset, 0), Shape(curKL1, curN));关键NZ 的 column stride C0 × ceil_align(K, 16)依赖 fullK。 Slice 保留父 tensor 的 stride 不重算。如果 block 层同时切 KNCopyGmToCbufAlignV2NZ用 sliced K 计算smallFractalSize但 stride 基于 fullK → 地址错位。适配点 5 — Launcher按 transA/transB format 分发 LayoutPtntransAformatLayoutAtransBformatLayoutBfalsendNDExtLayoutPtnfalsendNDExtLayoutPtntruendDNExtLayoutPtntruendDNExtLayoutPtnfalsenzNZLayoutPtnfalsenzNZLayoutPtntruenzZNLayoutPtntruenzZNLayoutPtn8. 排障速查现象根因≈100% mismatch仅 transA/B 某方向触发launcher 里 layout 硬编码未跟 trans 标志同步B-NZ 路径全错数据生成未按 trans format 规则转换transtrue 时未做 transposeformatnz 时未调to_nz_format或 transpose 顺序错误必须先 transpose 再 to_nz_format或 baseN 未 C0 对齐或TagToTransNZLayoutPtn/TagToTransZNLayoutPtn漏特化或 c0 参数未按 dtype 传入NZ 输入 K≤16 PASSK16 多 N/M-tile FAILSlice 顺序错误——block 层同时切 KN或 KM导致 NZ column stride 不匹配。kernel 层必须先做 N/M-slice保留 fullK strideblock 层只做 K-sliceNZ 输入全 FAIL所有 shape数据生成 NZ 排列顺序错误——应为permute(2,0,1,3)产出(dim1/C0, dim0/16, 16, C0)而非permute(0,2,1,3)NZ 输入多 tile FAIL单 tile PASSGM 端FrameLayoutFormatNZLayoutPtn使用默认 C016但 int8/fp8 需要 C032。修复FrameLayoutFormatNZLayoutPtn, Std::Int32/sizeof(Type)NZ 输入文件大小不匹配Host 侧 sizeA/sizeB 按逻辑维度计算应按 NZ 物理维度CalcNzSize(dim0, dim1, c0)计算NZ 输入非对齐 shape FAIL对齐 shape PASStiling 引擎 baseM/baseN 未按 NZ 内轴/外轴约束对齐。NZ 格式要求外轴 16 对齐、内轴 C0 对齐。详见 §99. Tiling 对齐约束NZ 场景NZ 格式的物理排列为(dim1/C0, dim0/16, 16, C0)其中外轴 dim0需要 16 元素对齐内轴 dim1需要 C0 元素对齐C0 32 / sizeof(dtype)A 矩阵NZ 格式transANZ of外轴(dim0→16)内轴(dim1→C0)false(M,K)MKtrue(K,M)KMB 矩阵NZ 格式transBNZ of外轴(dim0→16)内轴(dim1→C0)false(K,N)KNtrue(N,K)NK各轴约束轴外轴场景→16内轴场景→C0约束baseMA-NZ transAfalseA-NZ transAtrueAlign(baseM, isANz transA ? C0 : 16)baseNB-NZ transBtrueB-NZ transBfalseAlign(baseN, isBNz !transB ? C0 : 16)baseKA-NZ transAtrueB-NZ transBfalseA-NZ transAfalseB-NZ transBtrueAlign(baseK, ((isANz !transA) || (isBNz transB)) ? C0 : 16)Tiling 引擎代码constexpr uint64_t C0 32 / sizeof(AType); runInfo_.baseM Align(runInfo_.baseM, args_.isANz args_.isATrans ? C0 : static_castuint64_t(16)); runInfo_.baseN Align(runInfo_.baseN, args_.isBNz !args_.isBTrans ? C0 : static_castuint64_t(16)); runInfo_.baseK Align(runInfo_.baseK, (args_.isANz !args_.isATrans) || (args_.isBNz args_.isBTrans) ? C0 : static_castuint64_t(16));常见错误用条件表达式如if (isBTrans || !isATrans)控制 baseN 的 C0 对齐在 transAtruetransBfalse 时条件为 falsebaseN 未对齐。此时 B 为 NZ of (K,N)N 是内轴需要 C0 对齐未对齐导致CopyGmToCbufAlignV2NZblock copy 失败。对齐 shape如 N256不受影响因为 N 本身已是 C0 的倍数。【免费下载链接】cannbot-skillsCANNBot 是面向 CANN 开发的用于提升开发效率的系列智能体本仓库为其提供可复用的 Skills 模块。项目地址: https://gitcode.com/cann/cannbot-skills创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考