当前位置: 首页 > news >正文

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: C0=16, fp8/int8: C0=32, fp4: C0=64)。 非对齐时先补 0 到 16 对齐。

记矩阵计算的三个维度分别为 M、K、N(即 A[M,K] × B[K,N] = C[M,N]),则 A、B 矩阵的各种格式情况对应的 GM 数据排布及 LayoutPtn 如下。

A 矩阵

transAformat物理排列LayoutPtn
falseND(M, K)NDExtLayoutPtn
falseNZ(K/C0, M/16, 16, C0)NZLayoutPtn
trueND(K, M)DNExtLayoutPtn
trueNZ(M/C0, K/16, 16, C0)ZNLayoutPtn

B 矩阵

transBformat物理排列LayoutPtn
falseND(K, N)NDExtLayoutPtn
falseNZ(N/C0, K/16, 16, C0)NZLayoutPtn
trueND(N, K)DNExtLayoutPtn
trueNZ(K/C0, N/16, 16, C0)ZNLayoutPtn

2. 数据生成流程

随机数据生成固定以数学描述 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/bf16=16, int8/fp8=32, fp4=64 """ dim0, dim1 = data.shape dim0_pad = ((dim0 + 15) // 16) * 16 dim1_pad = ((dim1 + c0 - 1) // c0) * c0 padded = torch.zeros((dim0_pad, dim1_pad), dtype=data.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/bf16=16, int8/fp8=32)。

gen_data 范例

def gen_data(m, k, n, dtype=torch.int8, trans_a=False, trans_b=False, a_format="nd", b_format="nd"): C0 = 32 // torch.tensor([], dtype=dtype).element_size() # 动态计算 C0 # 生成原始数据(固定 shape) if dtype.is_floating_point: A = torch.randn(m, k, dtype=dtype) B = torch.randn(k, n, dtype=dtype) else: A = torch.randint(-128, 128, (m, k), dtype=dtype) B = torch.randint(-128, 128, (k, n), dtype=dtype) # 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_kernel

3. LayoutPtn 选择

Launcher 直接传 tensor_api pattern 作为模板参数:

Pattern含义构成 GM shape
AscendC::Te::NDExtLayoutPtn行主序(ND)A: (M,K); B: (K,N); C: (M,N)
AscendC::Te::DNExtLayoutPtn列主序(DN)A: (K,M); B: (N,K)
AscendC::Te::NZLayoutPtnNZ 分形预重排A 或 B 离线重排为 NZ 格式
AscendC::Te::ZNLayoutPtnZN 分形预重排A 或 B 离线重排为 ZN 格式(转置场景)

TagToTrans<Pattern>layout_utils.h派生 transA/transB:

Patterntrans 值
NDExtLayoutPtnfalse
DNExtLayoutPtntrue
NZLayoutPtnfalse
ZNLayoutPtntrue

常见错误:新增 transA=true 但 launcher 里 layoutA 仍硬编码 NDExtLayoutPtn → 编译过但 ≈100% mismatch(K 维和 M 维错位)。

4. L1 layout 自动选择

使用L1LayoutHelper<LayoutPtn, Type, TransVal>统一处理:

  • ND/DN 输入:L1 按 trans 标志选 NZ/ZN(走硬件 ND→NZ 格式转换)
  • NZ/ZN 输入:L1 与 GM pattern 一致(走 NZ→NZ / ZN→ZN 块拷贝,省掉格式转换带宽)
using MakeLayoutAL1 = typename L1LayoutHelper<LayoutA, AType, transA>::type; using MakeLayoutBL1 = typename L1LayoutHelper<LayoutB, BType, transB>::type;

L1→L0A/L0B 的 routing 由 tensor_api 根据 DstPattern(L0A=NZ, L0B=ZN)和 SrcPattern(L1 的 pattern)自动派发NORMAL 或 TRANS 模式,无需手动添加LoadDataTrait{transposed=true}

5. GM 端 layout 构造

GM 端 NZ layout 构造必须使用正确的 C0:

// ⚠️ 关键:FrameLayoutFormat 默认 C0=16(基于 uint16_t),int8/fp8 需要 C0=32 static constexpr uint64_t A_C0 = 32 / sizeof(AType); static constexpr uint64_t B_C0 = 32 / sizeof(BType); using MakeLayoutA = AscendC::Te::FrameLayoutFormat<LayoutA, AscendC::Std::Int<A_C0>>; using MakeLayoutB = AscendC::Te::FrameLayoutFormat<LayoutB, AscendC::Std::Int<B_C0>>;

⚠️ 致命陷阱FrameLayoutFormat<NZLayoutPtn>默认使用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特化(NZLayoutPtnZNLayoutPtn各一个)+IsNzOrZn检测 +L1LayoutHelper辅助模板。

适配点 2 —matmul_kernel.h/matmul_kernel_fused.h:GM 端 layout 构造必须使用正确的 C0(详见 §5)。

适配点 3 —matmul_block_mmad.hMakeLayoutAL1/MakeLayoutBL1改用L1LayoutHelper(详见 §4)。

适配点 4 —matmul_kernel.h:Slice 顺序——kernel 层做 M/N-slice(保留 fullK stride),block 层只做 K-slice:

// Kernel 层:N-slice(保留 fullK stride,NZ column stride 依赖 fullK) auto gmBlockB = gmB.Slice(Coord(0, nPos), Shape(fullK, tileN)); // Block 层:K-slice(gmBlockB 已经是 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 层同时切 K+N,CopyGmToCbufAlignV2NZ用 sliced K 计算smallFractalSize,但 stride 基于 fullK → 地址错位。

适配点 5 — Launcher:按 transA/transB + format 分发 LayoutPtn:

transAformatLayoutAtransBformatLayoutB
falsendNDExtLayoutPtnfalsendNDExtLayoutPtn
truendDNExtLayoutPtntruendDNExtLayoutPtn
falsenzNZLayoutPtnfalsenzNZLayoutPtn
truenzZNLayoutPtntruenzZNLayoutPtn

8. 排障速查

现象根因
≈100% mismatch,仅 transA/B 某方向触发launcher 里 layout 硬编码未跟 trans 标志同步
B-NZ 路径全错数据生成未按 trans + format 规则转换(trans=true 时未做 transpose,format=nz 时未调to_nz_format);或 transpose 顺序错误(必须先 transpose 再 to_nz_format);或 baseN 未 C0 对齐;或TagToTrans<NZLayoutPtn>/TagToTrans<ZNLayoutPtn>漏特化;或 c0 参数未按 dtype 传入
NZ 输入 K≤16 PASS,K>16 + 多 N/M-tile FAILSlice 顺序错误——block 层同时切 K+N(或 K+M)导致 NZ column stride 不匹配。kernel 层必须先做 N/M-slice(保留 fullK stride),block 层只做 K-slice
NZ 输入全 FAIL(所有 shape)数据生成 NZ 排列顺序错误——应为permute(2,0,1,3)产出(dim1/C0, dim0/16, 16, C0),而非permute(0,2,1,3)
NZ 输入多 tile FAIL,单 tile PASSGM 端FrameLayoutFormat<NZLayoutPtn>使用默认 C0=16,但 int8/fp8 需要 C0=32。修复:FrameLayoutFormat<NZLayoutPtn, Std::Int<32/sizeof(Type)>>
NZ 输入文件大小不匹配Host 侧 sizeA/sizeB 按逻辑维度计算,应按 NZ 物理维度CalcNzSize(dim0, dim1, c0)计算
NZ 输入非对齐 shape FAIL,对齐 shape PASStiling 引擎 baseM/baseN 未按 NZ 内轴/外轴约束对齐。NZ 格式要求外轴 16 对齐、内轴 C0 对齐。详见 §9

9. 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)MK
true(K,M)KM

B 矩阵(NZ 格式)

transBNZ of外轴(dim0→16)内轴(dim1→C0)
false(K,N)KN
true(N,K)NK

各轴约束

外轴场景(→16)内轴场景(→C0)约束
baseMA-NZ && transA=falseA-NZ && transA=trueAlign(baseM, isANz && transA ? C0 : 16)
baseNB-NZ && transB=trueB-NZ && transB=falseAlign(baseN, isBNz && !transB ? C0 : 16)
baseKA-NZ && transA=true;B-NZ && transB=falseA-NZ && transA=false;B-NZ && transB=trueAlign(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_cast<uint64_t>(16)); runInfo_.baseN = Align(runInfo_.baseN, args_.isBNz && !args_.isBTrans ? C0 : static_cast<uint64_t>(16)); runInfo_.baseK = Align(runInfo_.baseK, (args_.isANz && !args_.isATrans) || (args_.isBNz && args_.isBTrans) ? C0 : static_cast<uint64_t>(16));

常见错误:用条件表达式(如if (isBTrans || !isATrans))控制 baseN 的 C0 对齐,在 transA=true+transB=false 时条件为 false,baseN 未对齐。此时 B 为 NZ of (K,N),N 是内轴需要 C0 对齐,未对齐导致CopyGmToCbufAlignV2NZblock copy 失败。对齐 shape(如 N=256)不受影响因为 N 本身已是 C0 的倍数。

【免费下载链接】cannbot-skillsCANNBot 是面向 CANN 开发的用于提升开发效率的系列智能体,本仓库为其提供可复用的 Skills 模块。项目地址: https://gitcode.com/cann/cannbot-skills

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

http://www.jsqmd.com/news/1201533/

相关文章:

  • Python数据分析实战:从数据清洗到可视化全流程
  • Tessera:从零构建LLM蒸馏与推理引擎的全栈实践指南
  • 每天一个知识点——怎么调用接口
  • 终极iOS设备调试解决方案:如何快速解决Xcode无法识别设备的完整指南
  • Nori-30M性能深度解析:96个回归任务中超越基础版的关键原因
  • Tess-4-27B-OptiQ-4bit进阶技巧:自定义量化配置与模型微调
  • GRU API调用实战:从原理到生产环境部署全解析
  • 聊聊服务编排:从概念辨析到落地实践
  • SD 卡 SDIO 电压切换全链路分析:从 3.3V 默认模式到 1.8V UHS-I 的时序约束与硬件死锁排查
  • 2026宝鸡漏水检测维修优选:正规防水补漏团队 TOP5推荐-卫生间/厨房/屋顶/阳台/外墙/飘窗/等渗漏水免砸砖维修 - 筑宅安
  • 从自然语言SOP到自动化故障处置:LLM Agent如何将纸面化Runbook转化为可执行操作序列的全链路拆解
  • AngularAMD源码解析:深入理解ngload插件的工作原理
  • ft_wl_fwk事件循环机制:如何实现高性能的Wayland服务器
  • ECharts 大数据量渲染:千万级数据点的降采样与聚合策略
  • 【企业级代码质量防火墙】:用Claude实现PR自动拦截+漏洞溯源+合规审计三合一
  • 从实验室到生产线:USAF1951分辨率板在光学系统全周期评估中的实战指南
  • Dob与TypeScript的完美配合:类型安全的状态管理方案
  • LayerZero V2消息库(MessageLib)详解:自定义DVN和Executor实现指南
  • Altium元器件库高效获取与管理全攻略
  • 2026重庆涪陵汽车贴膜推荐哪家?哪家专业靠谱?众多车主评价的标杆门店追光汽车贴膜(涪陵店)上榜 - zhouzhou12321
  • Python数据类型转换实战:从input()到eval()的进阶应用
  • Deepseek V4实战指南:从API集成到本地部署的AI编程助手应用
  • 2026年7月江阴靠谱装修公司哪家好?本地口碑家装品牌测评与避坑指南 - 装修新知
  • 程序员早知道:算法技术要点有哪些?
  • STM32F103智能小车开发实战:硬件搭建与核心算法
  • 如何快速实现支付宝二维码支付:使用Alipay Ruby Gem集成alipay.trade.precreate API的完整指南
  • XMRig NVIDIA功耗管理:终极指南教你平衡性能与能效
  • 从零到一:Quartus II与ModelSim联合仿真的完整配置与实战
  • CANN/asc-devkit AI CPU调试打印API
  • AI重构编译器:用提示工程替代传统代码实现