前言你写一行torch.matmul(x, y)背后发生了什么从 PyTorch 的一次torch.matmul到昇腾 NPU 真正执行矩阵乘法中间经过了 ops-nn、ops-blas、catlass 三层仓库的接力。这篇文章用费曼科普的方式把这条调用链拆干净让你能说清楚每层在干什么。从一次 torch.matmul 说起Python 代码到 NPU 执行importtorch# 这行代码触发的调用链xtorch.randn(16,512)ytorch.randn(512,2048)# 方案1直接 matmulz1torch.matmul(x,y)# 方案2通过 torch_npu昇腾适配z2torch_npu.matmul(x,y)无论哪种写法最终都要落到昇腾 NPU 的硬件上执行。调用链的五层整体架构图┌─────────────────────────────────────────────┐ │ PyTorch / torch_npu │ ← 第一层框架适配 └────────────────────┬──────────────────────┘ │ ┌────────────────────▼──────────────────────┐ │ AscendCLaclnnMatMul │ ← 第二层C API └────────────────────┬──────────────────────┘ │ ┌────────────────────▼──────────────────────┐ │ ops-nnMatMul 融合 │ ← 第三层算子库 └────────────────────┬──────────────────────┘ │ ┌────────────────────▼──────────────────────┐ │ ops-blasGEMM 分块 │ ← 第四层BLAS 库 └────────────────────┬──────────────────────┘ │ ┌────────────────────▼──────────────────────┐ │ catlassGEMM 模板 │ ← 第五层硬件模板 └────────────────────┬──────────────────────┘ │ ┌────────────────────▼──────────────────────┐ │ 昇腾达芬奇 NPUCUBE 单元执行 │ ← 硬件层 └─────────────────────────────────────────────┘每层干什么层级仓库职责关键产物框架适配torch_npuPyTorch → AscendCL算子注册C APIAscendCL统一接口参数校验aclnnMatMul算子库ops-nn融合、融合策略MatMulBiasActBLAS 库ops-blas分块调度、缓存优化GEMM硬件模板catlassCube 指令生成汇编硬件达芬奇 NPU矩阵乘法矩阵结果第三层ops-nn 的 MatMulops-nn 里的 MatMul 是什么ops-nn 是神经网络层算子库它的 MatMul 比 ops-blas 的 GEMM 多了一些东西ops-nn MatMul GEMM BiasAdd Activation# ops-nn MatMul 的融合模式# 原始调用3次 NPU 调用# 1. MatMul# 2. BiasAdd# 3. ActivationGELU/SiLU/ReLU# ops-nn 融合后1次 NPU 调用# 一个 kernel 跑完 MatMul Bias GELU为什么 ops-nn 要融合调用方式Launch 开销数据搬运总耗时分离调用3×1ms3×HBM5ms融合调用1×1ms1×HBM2ms融合后数据只搬一次kernel 只 launch 一次。ops-nn MatMul 的融合配置importcann# 创建融合 MatMulMatMul BiasAdd GELUmatmul_opcann.ops.nn.MatMul(transpose_aFalse,transpose_bFalse,activationGELU,# 激活函数output_dtypefloat16)# 调用outputmatmul_op(input_tensor,weight_tensor,bias_tensor)第四层ops-blas 的 GEMMops-blas 是什么ops-blas 是基础线性代数算子库专注矩阵乘法GEMM GEneral Matrix Multiply。# ops-blas GEMM 的核心参数defgemm(a,b,cNone,alpha1.0,beta0.0,transpose_aFalse,transpose_bFalse,mNone,nNone,kNone): c alpha * op(A) op(B) beta * c A: (m, k) 或 (k, m) 如果转置 B: (k, n) 或 (n, k) 如果转置 C: (m, n) 输出 GEMM 的分块策略为什么要分块因为 Cube 单元的寄存器有限一次算不完整个矩阵。原始矩阵乘法 A (m×k) × B (k×n) C (m×n) ┌────────┐ ┌────────┐ │ │ │ │ │ A │ × │ B │ │ │ │ │ └────────┘ └────────┘ 分块后 A 被切成 m/M 个 tile每块 M×k B 被切成 k/K 个 tile每块 k×n C Σ (A_tile_i × B_tile_i) 每个 tile 刚好放得进 Cube 单元的缓存ops-blas 的缓存优化# ops-blas GEMM 的 L1 Cache 优化策略classGEMMTiler: 把大矩阵切成小 tile充分利用 L1 Cache L1 Cache 大小昇腾 910B 是 64KB def__init__(self,m,n,k,l1_size64*1024):# 计算最优分块大小# 每个 tile 需要存 A 的 M×K 和 B 的 K×N# 2 × M × K × sizeof(half) ≤ 64KBself.block_mmin(512,m)# 输出 tile 大小self.block_kmin(256,k)# 中间维度self.block_nmin(512,n)deftile(self,A,B,C):分块执行form_startinrange(0,A.shape[0],self.block_m):forn_startinrange(0,B.shape[1],self.block_n):# 加载当前 tile 到 L1A_tileA[m_start:m_startself.block_m,:]B_tileB[:,n_start:n_startself.block_n]# Cube 计算C_tileself.cube_matmul(A_tile,B_tile)# 累加到 CC[m_start:m_startself.block_m,n_start:n_startself.block_n]C_tile第五层catlass 模板catlass 是什么catlass 是昇腾算子模板库它给 ops-blas 提供 GEMM 的底层实现。# catlass GEMM 模板的参数化接口# 来自 catlass/gemm/template.hclassGemmTemplate{//矩阵形状 uint32_t m;//A 的行数C 的行数 uint32_t n;//B 的列数C 的列数 uint32_t k;//A 的列数B 的行数//数据类型 DataType dtype_a;//A 的数据类型 DataType dtype_b;//B 的数据类型 DataType dtype_c;//C 的数据类型//分块参数 uint32_t block_m;//M 方向分块 uint32_t block_n;//N 方向分块 uint32_t block_k;//K 方向分块//硬件配置 CubeUnit cube_unit;VectorUnit vector_unit;};catlass 生成的是什么catlass 模板实例化后生成的是昇腾达芬奇 NPU 的汇编代码┌──────────────────────────────────────┐ │ catlass 模板 │ │ ↓ 实例化 │ │ 生成的汇编Vector 指令 Cube 指令│ │ ↓ 编译 │ │ NPU 可执行 kernel │ └──────────────────────────────────────┘完整调用链代码# 完整调用链示例importtorchimporttorch_npu# 1. PyTorch 调用框架层xtorch.randn(16,512).npu()# 移到 NPUwtorch.randn(2048,512).npu()# 移到 NPU# 2. torch_npu 拦截调用 AscendCLAPI 层# torch_npu 内部aclnnMatMul(x, w, output)# 3. AscendCL 调用 ops-nn MatMul算子层# ops-nn 内部matmul ops.blas.gemm()# ops-nn 内部output matmul(x, w)# 4. ops-nn 调用 ops-blas GEMMBLAS 层# ops-blas 内部tiler GEMMTiler(m16, k512, n2048)# ops-blas 内部result tiler.tile()# 5. ops-blas 调用 catlass 模板模板层# catlass 内部生成汇编提交 Cube 指令# 6. 达芬奇 NPU 执行CUBE 单元# 最终计算在硬件上完成outputtorch_npu.matmul(x,w.t())# 最终输出ops-nn MatMul vs ops-blas GEMM什么时候用哪个场景推荐理由Transformer 前向推理ops-nn MatMul融合 BiasActivation省一次数据搬运BERT / GPT 推理ops-nn MatMul多层堆叠融合节省明显裸矩阵乘法无激活ops-blas GEMM跳过融合开销更直接自定义融合模式ops-blas catlass从底层自己拼性能 profiling两层都要看看 ops-nn 的融合是否生效附录ops-nn vs ops-blas vs catlass 的关系图ops-nn ├── MatMul融合版 │ ├── 内部调用 ops-blas │ └── 融合 Bias Activation ├── Conv2d ├── LayerNorm └── Softmax ops-blas ├── GEMM基础版 │ ├── 内部调用 catlass │ └── 分块 缓存优化 ├── Batch GEMM └── Strided GEMM catlass ├── GemmTemplate ├── ConvTemplate └── AttentionTemplate选择建议只需要矩阵乘 激活 → ops-nn需要裸 GEMM → ops-blas需要深度定制 → catlass常见问题 FAQQ1: ops-nn 的融合会自动开启吗是的默认开启。但只有 MatMulBiasActivation 连续调用时才会融合。Q2: 融合后精度掉了检查是否开启了错误的融合模式。用精度对比工具验证。Q3: 为什么有时候 ops-blas 比 ops-nn 更快融合有 overhead。如果只有 MatMul 没有后续操作ops-nn 的融合反而浪费。性能调优实践profiling 工具importcann.profilerasprofiler# profiling MatMul 调用withprofiler.Profile(matmul_profile.pb)asp:for_inrange(1000):resultmatmul_op(x,w)reportp.get_report()# 输出# - MatMul kernel 耗时# - BiasAdd kernel 耗时# - Fusion 融合效果# - L1/L2 Cache 命中率# 如果 Cache 命中率 80%ops-blas 分块参数需要调分块参数调优# catlass 手动分块高级用法tilercann.ops.blas.GEMMTiler(m512,k512,n2048,block_m64,# M 方向分块block_k128,# K 方向分块block_n64# N 方向分块)# L1 Cache 命中率测试forbmin[32,64,128,256]:forbkin[64,128,256]:forbnin[32,64,128]:tiler.block_mbm tiler.block_kbk tiler.block_nbn# 测试性能elapsedbenchmark(tiler)cache_hittiler.get_l1_hit_rate()ifcache_hit0.85andelapsedbest_time:best(bm,bk,bn)精度 vs 性能权衡# 融合后精度对比defcheck_fusion_accuracy():# FP32 基准result_fp32model_fp32(input)# FP16 融合result_fp16model_fp16_fused(input)# 计算相对误差difftorch.abs(result_fp16-result_fp32)/(torch.abs(result_fp32)1e-8)max_rel_errdiff.max()print(fMax relative error:{max_rel_err:.6f})# 误差阈值 1% 通常可接受ifmax_rel_err0.01:print(⚠️ Fusion accuracy degradation detected)else:print(✅ Fusion accuracy OK)总结从torch.matmul到 NPU 执行PyTorch→ torch_npu 拦截AscendCL→ 参数校验、API 分发ops-nn→ 融合 MatMulBiasActops-blas→ GEMM 分块、缓存优化catlass→ 生成 Cube 指令达芬奇 NPU→ 硬件执行理解调用链的价值遇到性能问题你知道该优化哪一层。融合不够去 ops-nn缓存不命中去 ops-blas指令生成有瓶颈去 catlass。仓库地址https://atomgit.com/cann/ops-nn