news 2026/5/25 2:21:09

Cube MatMul:为什么矩阵乘法选了 Cube 而不是 Vector

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Cube MatMul:为什么矩阵乘法选了 Cube 而不是 Vector

本文基于昇腾CANN和昇腾NPU,围绕 Cube MatMul 矩阵乘法技术展开。

想象你在一个巨大的停车场里搬箱子。方案 A:一次搬一个箱子,走 100 趟——这是 Vector 的做法。方案 B:用叉车一次叉起 16×16 个箱子,一趟搞定——这是 Cube 的做法。

AI 计算的核心是矩阵乘法——95% 以上的浮点运算都花在 GEMM 上。一个 Attention 层里的 Q×K^T、Attention×V、FFN 的两次 MatMul——全是 GEMM。硬件优化的第一优先级就是让 GEMM 跑得尽可能快。这就是为什么达芬奇 Core 里专门塞了一个 Cube Unit——它生来只干一件事:16×16×16 的 FP16 乘累加。


Cube Unit 怎么算 GEMM

Cube Unit 每 cycle 做一次 C[16,16] += A[16,16] × B[16,16] 的矩阵乘累加。用 FP16 计算,FP32 累加——输入低精度省内存和带宽,中间累积用高精度保留数值稳定性。

一次 Attention 的 Q×K^T 计算:Q 是 [B, H, S, D],K^T 是 [B, H, D, S]。Cube Unit 把 M=S、K=D、N=S 的 GEMM 切成一堆 16×16 的小块,一块一块算。每个小块独立在 Cube Unit 上完成,结果累积到 C 矩阵。

GEMM C[M,N] = A[M,K] × B[K,N] 在 Cube Unit 上的分解: ┌───────────────┐ ┌───────────┐ ┌───────────────┐ │ A (M×K) │ │ B (K×N) │ │ C (M×N) │ │ │ × │ │ = │ │ │ 切成 M_TILE× │ │ 切成 K_TILE│ │ 切成 M_TILE× │ │ K_TILE 小块 │ │ ×N_TILE │ │ N_TILE 小块 │ └───────────────┘ └───────────┘ └───────────────┘ 每个 M_TILE×N_TILE 的小块 C: for k_step in range(K / K_TILE): C_tile += A_tile[k_step] @ B_tile[k_step] // 16×16×16 的 MAC

Tile 分块为什么是核心

Cube Unit 一次只算 16×16。一个 4096×4096 的 GEMM 要分解成 (4096/16)² = 65536 个小块——但不需要全存在 L1 上同时算。

Tiling 的关键在 K 维度上的循环累积。A 和 B 沿着 K 维度切段,每次载入一段到 L1、Cube 算完这段的乘累加、结果加到 C 上——然后载入下一段。C 一直在 L1 上不动,A 和 B 轮流上场。

这样 L1 只需要装下:一段 A [M_TILE × K_TILE]、一段 B [K_TILE × N_TILE]、和 C [M_TILE × N_TILE]。三个块加起来几十到一百多 KB——刚好塞进 192KB 的 L1。


昇腾NPU的 GEMM 融合

单纯的 MatMul 后面往往跟着 Bias Add、Activation、Residual Add。CANN 的算子库把这些操作融进 MatMul——不是"先算 GEMM 再调 Activation",而是在 GEMM 的最后一个 K-step 还没结束时,Vector Unit 就已经开始处理前面累积好的 C 元素做 Activation。

融合 MatMul + Bias + GELU 的执行流: Cube Unit: [GEMM K-step 0] [GEMM K-step 1] ... [GEMM 最后 K-step] Vector Unit: [Bias Add] [GELU] Scalar Unit: [地址计算] [循环控制] ... [地址计算] [循环控制] 三个单元在不同 K-step 上并行——融合的 GEMM 比"先 GEMM 再 Activation" 快约 30%,因为省掉了 GEMM 输出写回 DDR 和 Activation 从 DDR 读入。
// Ascend C 里的融合 MatMul 模板——简化版classFusedMatMulGELU:publicAscendC::Kernel{__aicore__voidProcess()override{// Tiling 参数constexprintM_TILE=128,N_TILE=256,K_TILE=32;LocalTensor<fp16>a_tile,b_tile,c_tile;LocalAlloc(a_tile,M_TILE*K_TILE);LocalAlloc(b_tile,K_TILE*N_TILE);LocalAlloc(c_tile,M_TILE*N_TILE);for(intm=0;m<M;m+=M_TILE){for(intn=0;n<N;n+=N_TILE){SetZero(c_tile);for(intk=0;k<K;k+=K_TILE){DataCopy(a_tile,gm_a[m][k],M_TILE*K_TILE);DataCopy(b_tile,gm_b[k][n],K_TILE*N_TILE);MatMul(c_tile,a_tile,b_tile,M_TILE,N_TILE,K_TILE);// 累加:C += A @ B(Cube Unit 原生支持 FP32 累加)}// GEMM 完成后,Vector Unit 直接对 C 做 GELU——不写回 DDRGELU(c_tile,c_tile,M_TILE*N_TILE);DataCopy(gm_c[m][n],c_tile,M_TILE*N_TILE);}}}};

参考仓库

ops-blas 高性能 GEMM

ops-nn 神经网络算子

catlass 算子模板库

CANN 学习中心

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/25 2:20:20

Arm嵌入式开发中的代码覆盖率分析实践

1. 在Arm开发环境中实现代码覆盖率分析的核心思路对于嵌入式开发者而言&#xff0c;代码覆盖率分析是验证测试完备性的重要手段。Arm Toolchain for Embedded&#xff08;ATfE&#xff09;基于LLVM工具链&#xff0c;提供了完整的代码覆盖率解决方案。与传统的gcov方案相比&…

作者头像 李华
网站建设 2026/5/25 2:19:52

Ubuntu 20.04上源码编译ROS2 Humble,我踩过的那些坑和最终解决方案

Ubuntu 20.04源码编译ROS2 Humble避坑指南&#xff1a;从崩溃边缘到完美运行 当你在Ubuntu 20.04上尝试源码编译ROS2 Humble时&#xff0c;官方文档看起来就像是一份完美的食谱——直到你真正开始动手。作为一名经历过无数次失败才最终成功的开发者&#xff0c;我想分享那些官方…

作者头像 李华
网站建设 2026/5/25 2:12:52

2026年最值得用的10款免费AI写作工具推荐

AI写作工具在2026年迎来了爆发式增长&#xff0c;越来越多的免费工具让普通人也能高效创作。本文整理了10款最值得用的免费AI写作工具&#xff0c;涵盖博客、营销文案、学术写作等多个场景。一、AI Writing Assistant - 全能型免费写作工具AI Writing Assistant&#xff08;use…

作者头像 李华
网站建设 2026/5/25 2:01:03

2026年元届象GEO优化服务,真实口碑如何?

2026年&#xff0c;AI搜索已彻底渗透商业。当客户习惯问DeepSeek、豆包“推荐哪家装修公司”时&#xff0c;你的品牌能否出现在前三条&#xff1f;这已不是“要不要做”的问题&#xff0c;而是“谁能精准做到”的竞争。在此背景下&#xff0c;一款名为【元届象GEO】的系统&…

作者头像 李华