ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

多核对齐切分:让矩阵乘法在多核上“各干各的“

多核对齐切分:让矩阵乘法在多核上“各干各的“ 多核对齐切分让矩阵乘法在多核上各干各的【免费下载链接】asc-devkit本项目是CANN 推出的昇腾AI处理器专用的算子程序开发语言原生支持C和C标准规范主要由类库和语言扩展层构成提供多层级API满足多维场景算子开发诉求。项目地址: https://gitcode.com/cann/asc-devkit矩阵越算越慢单核资源明明在空转跑一次大矩阵的 Matmul 却要等上好半天——这是很多算子开发新手第一次接触多核并行时的真实感受。答案其实就藏在多核对齐切分这四个字里把一个大矩阵切成若干小块分给不同的 AI Core 并行计算。本文以 CANN asc-devkit 中的 Matmul 为例带你搞懂多核切分的两种策略、Tiling 参数和核数设置的避坑点。切蛋糕的智慧把大活拆成小活想象你在筹备一场 100 人的晚宴只有一位厨师。他做完 100 份菜需要一整天。但如果把菜谱切成 10 份请 10 位厨师同时开工理论上 1 小时就能全部搞定。唯一的代价是需要先设计好怎么切让每位厨师的工作量均衡、且互不干扰。矩阵乘法C A × B的多核并行就是同一件事。C 矩阵的每个元素都可以独立计算天然适合并行。问题只在于怎么把 A、B 矩阵切开才能让每个核各算一块、最后拼起来正好等于完整结果这就是多核对齐切分要解决的核心问题而切分方案会被记录在多核 Tiling 参数SingleCoreM / SingleCoreN / SingleCoreK里。两种切分策略一张图看懂Matmul 的切分策略有两种不切 K 轴和切 K 轴。它们的本质区别在于要不要因为中间累加而引入跨核通信。策略一只切 M、N 轴不切 K这是最简单直观的做法A 矩阵沿 M 轴切每个核拿到SingleCoreM × K的 A 分块B 矩阵沿 N 轴切每个核拿到K × SingleCoreN的 B 分块每个核独立计算SingleCoreM × SingleCoreN的 C 分块互不依赖、无需通信。每个核都持有完整的 K 维度数据相当于每位厨师都拿到完整的配方自己从头做到尾。缺点也很明显K 很大时每个核都要反复加载整条 K 维数据数据搬运开销大。策略二M、N、K 三个轴都切当 K 维度很大时把 K 也切开A 矩阵切成SingleCoreM × SingleCoreK的块B 矩阵切成SingleCoreK × SingleCoreN的块每个核只算其中一块部分结果 A1×B1或 A2×B2最后把多个核的部分结果累加成完整的 C 分块。形象地说这就像把从头做到尾改成了流水线分工每个核只负责其中一段工序但最后需要把几段工序的产出合起来。因此切 K 轴引入了跨核累加需要额外的同步与归约开销。怎么选K 不大、核数够用 → 只切 M、N简单可靠K 特别大导致单核搬运 K 维数据成为瓶颈 → 切 K用通信换内存带宽。CANN 的 MultiCoreMatmulTiling 会自动帮你算出每种策略下的 SingleCoreM / SingleCoreN / SingleCoreK你只需关心设多少核。关键参数与核数设置SetDim vs SetBlockDim多核 Tiling 有四个关键参数前三个描述怎么切最后一个描述用几个核参数含义谁来算SingleCoreM单核处理的 M 方向大小Tiling 自动计算SingleCoreN单核处理的 N 方向大小Tiling 自动计算SingleCoreK单核处理的 K 方向大小仅切 K 场景出现Tiling 自动计算SetDim / SetBlockDim设置参与计算的核数开发者手动设置参数自动算但核数必须你亲自设而且这里藏着新手最容易踩的坑⚠️SetDim 和 SetBlockDim 完全不是一回事。SetDim设置可用的核数即告诉 Tiling 计算器我有这么多核可以用Tiling 会据此推导 SingleCoreM/N/K。它不决定实际加载哪些核。SetBlockDim设置整个算子实际加载的核数这是真正会生效、会被启动的核数必须设置。简单记SetDim是允许用多少核SetBlockDim是真的用多少核。纯 Cube 模式下你应根据 Tiling 实际计算出的核数来配 SetBlockDim而 MIX 模式Cube 矢量计算的核数规则更复杂建议参考算子实践章节中矩阵编程的核数设置说明。最小可运行示例把 Tiling 串起来下面这段骨架代码展示了多核 Tiling 的完整流程核心步骤只有五步// ① 创建多核 Tiling 对象单核场景用 MatmulTiling多核用 MultiCoreMatmulTiling auto platform platform_ascendc::PlatformAscendCManager::GetInstance(socVersion); matmul_tiling::MultiCoreMatmulTiling tiling(*platform); // ② 设置可用核数把当前 AI 处理器的 Cube 核数全部告诉 Tiling tiling.SetDim(platform.GetCoreNumAic()); // ③ 声明 A、B、C 及可选的 Bias 的类型、存储位置和格式 tiling.SetAType(TPosition::GM, CubeFormat::ND, DataType::DT_FLOAT16); tiling.SetBType(TPosition::GM, CubeFormat::ND, DataType::DT_FLOAT16); tiling.SetCType(TPosition::GM, CubeFormat::ND, DataType::DT_FLOAT); tiling.SetBiasType(TPosition::GM, CubeFormat::ND, DataType::DT_FLOAT); // ④ 传入矩阵形状OrgShape 是原始形状Shape 可用于带 padding 的场景 tiling.SetOrgShape(M, N, K); tiling.SetShape(M, N, K); tiling.EnableBias(isBias); // ⑤ 生成 Tiling 数据返回值 -1 表示生成失败 optiling::TCubeTiling tilingData; int ret tiling.GetTiling(tilingData); // if ret -1, gen tiling failed逐段解释①②先建对象、报出家底可用核数让 Tiling 计算器知道资源上限③④声明数据长什么样类型、格式、形状Tiling 据此推演每种切分策略下的分块大小⑤GetTiling一次性把 SingleCoreM / SingleCoreN / SingleCoreK 等参数填进tilingData供 host 侧使用之后记得按实际使用核数设置SetBlockDim。完整可运行样例可在项目 examples 目录中找到只切 M、N 的场景看01_simd_cpp_api/00_introduction/02_matrix/matmul切 K 的场景看01_simd_cpp_api/03_libraries/00_matrix/matmul_splitk。常见疑问 QAQ1什么时候应该切 K 轴A当 K 特别大、单核反复搬运整条 K 维数据成为性能瓶颈时切 K 能降低单核内存压力代价是需要跨核累加。K 不大时优先只切 M、N。Q2核数到底怎么定A纯 Cube 场景用SetDim报出可用核数Tiling 会给出实际使用的核数再据此配置SetBlockDim。记住二者别混淆。Q3核数设得越多就越快吗A不一定。矩阵尺寸固定时切分越细、单核负载越小可能造成大量核空转甚至通信开销超过收益。设置原则是核数尽量整除矩阵维度、负载均衡。Q4SingleCoreK 只在切 K 场景才有吗A是的。不切 K 时每个核持有完整的 K 维只有切 K 后才有单核处理的 K 分块大小这一概念。总结与延伸多核对齐切分的本质是把一位厨师做一百份菜变成多位厨师分工协作只切 M、N 轴简单无通信切 K 轴以通信换带宽分块大小由 Tiling 自动算出核数则靠你通过 SetDim 与 SetBlockDim 正确设置。搞懂这两对关系多核 Matmul 的性能优化就有了方向。想继续深入可以接着读项目中矩阵编程高阶 API的算子实现章节了解 MIX 模式的核数设置规则动手跑一遍上面提到的 matmul 与 matmul_splitk 样例把单核改造为多核你会对 Tiling 参数的流向有更直观的感受。【免费下载链接】asc-devkit本项目是CANN 推出的昇腾AI处理器专用的算子程序开发语言原生支持C和C标准规范主要由类库和语言扩展层构成提供多层级API满足多维场景算子开发诉求。项目地址: https://gitcode.com/cann/asc-devkit创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表