CANN 中 GEMM 详解(通用矩阵乘,昇腾Cube核心计算)

一、基础定义 GEMM

GEMM = General Matrix Multiplication,通用矩阵乘法,标准公式:
C=α⋅A×B+β⋅CC = \alpha \cdot A \times B + \beta \cdot CC=αA×B+βC

  • AAA:M×K 输入矩阵
  • BBB:K×N 权重矩阵
  • CCC:M×N 输出矩阵
  • α、β:缩放系数(深度学习常用 α=1, β=0)

深度学习里卷积、全连接、Transformer注意力本质都是批量GEMM,是昇腾NPU算力消耗最大的算子,Cube单元专门为GEMM加速设计。

二、昇腾硬件与GEMM对应关系

  1. Cube Core(矩阵乘专用硬件)
    昇腾AI Core内置Cube单元,硬件原生执行FP16/FP32/INT8矩阵乘,算力远高于Vector单元。
    • 910B Cube标准分块:16×16×16(16M × 16K × 16N),单次流水线计算16*16输出
    • 数据必须放在 L0A / L0B(Cube专属Local缓存),不能直接用L1
  2. 存储层级流程(结合你之前学的Global/Local/Block)
Global Memory(A/B/C)
    ↓ SDMA DataCopy
L0A(存A分块) + L0B(存B分块) → Cube计算 → L1缓存中间结果
    ↓ SDMA DataCopy
写回 Global Memory(C)
  1. Block并行分工
  • Task Block(每个AI Core)负责M维度分片:比如M=1024,32个AI Core,每个Block处理M=32
  • 单核内部做Tiling:K维度切小块循环搬运到L0,解决L0容量不足问题

三、CANN两种GEMM开发方式

方式1:调用内置高性能GEMM API(推荐业务开发)

CANN提供封装好的Cube矩阵乘接口,经过厂商极致优化,性能高于手写循环:

  1. Ascend C 内核接口 CubeMatMul
// L0A(M,K), L0B(K,N) → L1 C(M,N)
CubeMatMul<half, half, half>(l0_a, l0_b, l1_c, M, K, N);
  1. 上层ACL/GE内置GEMM算子,直接调用无需手写核函数

方式2:手写Tiling循环GEMM(自定义算子深度优化场景)

步骤固定:

  1. 根据BlockIdx拆分M维度,拿到当前AI Core负责的A分片
  2. 对K维做tiling,每次取一小块K_len,满足L0A/L0B容量限制
  3. DataCopy从Global分批搬运A_block到L0A、B_block到L0B
  4. 调用CubeMatMul完成矩阵乘,累加至L1输出缓存
  5. 全部K分片计算完成后,将L1的C结果拷贝回Global

四、GEMM性能核心瓶颈(msprof可观测)

1. 访存瓶颈(最常见)

  • 现象:msprof中CubeUtilization低、HBM带宽打不满、SDMA搬运次数多
  • 原因:K维Tiling块太小,频繁Global↔L0来回拷贝;A/B访存不连续
  • 优化:扩大K分块尺寸,最大化复用L0缓存,合并DataCopy传输

2. Cube流水线气泡

  • 现象:PipeUtilization流水线利用率低
  • 原因:数据搬运和Cube计算没有重叠,等数据来了才开始计算
  • 优化:双缓冲乒乓L0,一边搬运下一块,一边计算当前块

3. Block负载不均

  • 现象:各AI Core Block执行时长差距大,部分核空闲
  • 优化:M维度均分,避免某一个Block处理更多行

4. L0容量限制

L0A/L0B空间极小,若tiling尺寸设置过大直接报Local Memory溢出;过小则搬运开销暴涨。

五、关键配套概念区分

  1. L0 vs L1
  • L0A/L0B:Cube专用Local内存,只能放GEMM输入A、B
  • L1:Vector通用缓存,存放GEMM输出C、激活、偏置等
  1. GEMM与Vector运算
  • Cube:高算力矩阵乘,只支持固定小分块16x16x16
  • Vector:逐元素计算(加法、激活、归一化),算力远低于Cube
  1. Roofline屋顶图(msprof)判断GEMM瓶颈
  • 顶点靠左:访存受限(带宽不够,GEMM没吃饱)
  • 触顶右边界:算力受限(Cube跑满,无优化空间)

六、极简GEMM执行流程总结

  1. Host将矩阵A、B、C传入Global Memory
  2. 调度器分配多个Block(AI Core)并行处理M分片
  3. 单核循环切K维度小块,SDMA搬运至L0A/L0B
  4. Cube硬件批量矩阵乘,结果存在L1
  5. 全部分片计算完成,SDMA把L1的C写回Global
  6. Host读取Global的输出矩阵C

需要我给一段最简Ascend C GEMM模板代码(包含Block分片、K维Tiling、CubeMatMul、乒乓搬运)吗?

Logo

鲲鹏昇腾开发者社区是面向全社会开放的“联接全球计算开发者,聚合华为+生态”的社区,内容涵盖鲲鹏、昇腾资源,帮助开发者快速获取所需的知识、经验、软件、工具、算力,支撑开发者易学、好用、成功,成为核心开发者。

更多推荐