发布时间:2026/8/3 1:27:23
KuDNN的矩阵乘之对接KML_BLAS_GEMM接口实现详解 KuDNN的矩阵乘之对接KML_BLAS GEMM接口实现详解 本文将围绕KuDNN的GEMM基本原理展开介绍GEMM接口分别对接了KML BLAS的cblas_gemm接口和JIT code本次主要介绍KML BLAS这一算法分支的原理和实现。 1. 概述 本文将围绕KuDNN的GEMM基本原理展开介绍GEMM接口分别对接了KML BLAS的cblas_gemm接口和JIT code本次主要介绍KML BLAS这一算法分支的原理和实现。 KuDNN源码可参考 https://gitcode.com/kunpengcompute/kudnn 2. 接口介绍include/operations/kudnn_gemm.hpp中声明了Gemm接口类方法主要是Gemm构造函数和Run执行计算两个成员函数以及指向真正Gemm实现GemmImpl类的pImpl指针。class KUDNN_API_PUBLIC Gemm final { public: Gemm(const TensorInfo aInfo, const TensorInfo bInfo, const TensorInfo cInfo, const TensorInfo biasInfo, int numThreads 0) noexcept(false); void Run(const void *a, const void *b, void *c, const void *bias, float alpha 1.0f, float beta 0.0f, int numThreads 0) const noexcept(false); private: std::unique_ptrDetail::GemmImpl pImpl; };kudnn的所有接口实现均采用Pointer to Implement机制将类的实现细节从其对象表示中移除通过不透明指针将它们放置在单独的类中。此技术用于构建具有稳定 ABI 的 C 库接口并减少编译时依赖。因为类的私有数据成员参与其对象表示影响大小和布局并且因为类的私有成员函数参与重载决议在成员访问检查之前进行所以对这些实现细节的任何更改都需要重新编译类的所有用户。PImpl 消除了这种编译依赖对实现的更改不会导致重新编译。因此如果库在其 ABI 中使用 PImpl则新版本的库可以更改实现同时与旧版本保持 ABI 兼容。Gemm的必要参数为A,B,C张量计算过程为CA*BC。tensorInfo描述了张量信息包括张量的维度大小(dims)数据类型布局(layout)和步长(stride); 观察接口会发现相比BLAS的标准GEMM接口少量LDA、LDB和LDC参数其实这些参数可以用stride表示实现。# 3. 算法流程## 3.1 实例化Gemm从接口层的Gemm类只提供接口定义其实现完全由Detail::GemmImpl来完成// all public methods just call corresponding pImpl implemnentation Gemm::Gemm(const TensorInfo aInfo, const TensorInfo bInfo, const TensorInfo cInfo, const TensorInfo biasInfo, int numThreads) noexcept(false): pImpl(new Detail::GemmImpl(aInfo, bInfo, cInfo, biasInfo, numThreads)){}Detail::GemmImpl类中有成员GemmInfo记录gemm参数信息其类成员就是srcweidstbias等参数; 同时将impl设置为nullptrGemmImpl(const TensorInfo srcInfo, const TensorInfo weiInfo, const TensorInfo dstInfo, const TensorInfo biaInfo, int numThreads) noexcept(false) : gemmImplInfo(srcInfo, weiInfo, dstInfo, biaInfo), impl(nullptr)在GemmImpl构造函数中①先校验输入参数的合法性②如果是鲲鹏920判断输入输出张量类型是否满足使用JIT的条件如果满足会进一步调用FindSolution查找合适的解决方案 并赋值给类成员impl指针否则保持impl为nullptr表示后续回退到BLAS计算gemm。{ Service::ThrowOnStatus(Gemm::ValidateInput(srcInfo, weiInfo, dstInfo, biaInfo, numThreads), “GEMM”); bool srcJIT weiJIT dstJIT false; #ifdef KUDNNL_920Pro srcJIT srcJIT || (gemmImplInfo.srcInfo.GetType() Element::TypeT::S8); weiJIT weiJIT || (gemmImplInfo.weiInfo.GetType() Element::TypeT::S8); … dstJIT dstJIT || (gemmImplInfo.dstInfo.GetType() Element::TypeT::S32); #endif // KUDNNL_920Pro … if (srcJIT weiJIT dstJIT) { impl FindSolution(gemmImplInfo, Threading::GetMaxNumThreads()); } }FinSolution函数会生成JITcode具体分析将在另一篇《kudnn的JIT》文章中详细展开这里不再赘述。至此不用JIT的情景下的Gemm实例构造的过程结束下面看Gemm的计算过程。## 3.2. Gemm计算Gemm计算的整体架构是GemmImpl::Run (入口) ↓ BatchedExtendedGemm (回退BLAS GEMM算法) ↓ ChooseImpl (类型匹配与转换) ↓ GEMMCaller::Call (参数准备) ↓ GEMMWrapper (底层BLAS调用)### 3.2.1. GemmImpl::Run调用Gemm.Run接口输入需要计算的张量数据指针a,b,c实现计算。Gemm类的Run函数同样只是简单转调GemmImpl的Run函数。GemmImpl的Run实现如下判断impl是否为nullptr如果非空指针则跳转到JIT模块执行。否则调用BatchedExtendedGemm函数采用传统BLAS接口计算。void Run(const void *a, const void *b, void *c, const void *bias, float alpha, float beta, int numThreads){ if (impl ! nullptr) { if (impl-GetNThreads() ! Threading::GetMaxNumThreads()) { impl FindSolution(impl-GetTask(), Threading::GetMaxNumThreads()); } impl-Run({a, b, c, bias, alpha, beta}); } else { GemmHelpers::BatchedExtendedGemm(gemmImplInfo, a, b, c, bias, alpha, beta, numThreads); } }gemmImplInfo是中保存有GEMM的维度大小和布局等元数据信息结合Run接口输入的计算数据可组合参数调用KBLAS完成GEMM计算。### 3.2.2. BatchedExtendedGemmBatchedExtendedGemm函数完成3件事依次介绍①布局标准化 判断输入张量的布局是否符合满足标准ABX布局先通过ReorderLayer转换。下面介绍一下什么是ABX以及为什么要转换。 ABX布局是矩阵乘法中的一种标准内存布局约定它规定了输入矩阵A、B和输出矩阵X在内存中的排列方式。从ABX的代码可知其采用行优先布局,维度从高到低依次排列。Layout GetStandardABXLayout() const { switch (dims.GetNumDims()) { case DIM_1: { return Layout::A; } … case DIM_5: { return Layout::ABCDE; } } }ABX布局转换的核心目的是消除布局差异所有矩阵统一内存排列最大化BLAS性能使用最优的NoTrans路径简化地址计算在batch处理中避免stride语义混乱提升cache效率保证连续内存访问② 5D广播auto broadcastedDimsA Service::BroadcastTo5D( Service::GetShapeAccordingToLayout(gemmInfoExec.srcInfo.GetDims(), gemmInfoExec.srcInfo.GetLayout())); auto broadcastedDimsB Service::BroadcastTo5D( Service::GetShapeAccordingToLayout(gemmInfoExec.weiInfo.GetDims(), gemmInfoExec.weiInfo.GetLayout())); Shape broadcastedDims {std::max(broadcastedDimsA[IDX_0], broadcastedDimsB[IDX_0]), std::max(broadcastedDimsA[IDX_1], broadcastedDimsB[IDX_1]), std::max(broadcastedDimsA[IDX_2], broadcastedDimsB[IDX_2]), 1, 1}; auto reorderedStridesA Service::BroadcastTo5D(Service::GetShapeAccordingToLayout(gemmInfoExec.srcInfo.GetStrides(), gemmInfoExec.srcInfo.GetLayout()), false); auto reorderedStridesB Service::BroadcastTo5D(Service::GetShapeAccordingToLayout(gemmInfoExec.weiInfo.GetStrides(), gemmInfoExec.weiInfo.GetLayout()), false); …③ 三重循环批处理 因为BLAS的gemm一次计算一个2D的矩阵运算对于kudnn最高支持5维张量的情景需要个三重循环依次遍历计算GEMM。for (SizeType i0 0; i0 broadcastedDims[IDX_0]; i0) { for (SizeType i1 0;i1 broadcastedDims[IDX_1]; i1) { for (SizeType i2 0; i2 broadcastedDims[IDX_2]; i2) { const std::byte *aPtr static_castconst std::byte *(aExec) (i0 * reorderedStridesA[IDX_0] i1 * reorderedStridesA[IDX_1] i2 * reorderedStridesA[IDX_2]) * gemmInfoExec.srcInfo.GetType().GetSize(); const std::byte *bPtr … std::byte *cPtr … const std::byte *biasPtr static_castconst std::byte *(biasExec); if (biasExec) { biasPtr … } ChooseImpl(gemmInfoExec, aPtr, bPtr, cPtr, biasPtr, alpha, beta, numThreads); } } }### 3.2.3. ChooseImpl实现了三级回退策略第1级精确类型匹配 ↓ (失败) 第2级扩展精度匹配 ↓ (失败) 第3级全FP32转换所有的Gemm精度类型组合列表如下,由floatfp16bf16int8uint8之间组合而成每一项都是一个特化的GEMMCaller模板类类中实现了对应数据类型的cblas_gemm函数调用共有11种类型组合。static std::vectorstd::shared_ptr g_gemmImpls { std::make_sharedGEMMCallerfloat, float, float, float(), std::make_sharedGEMMCaller__fp16, __fp16, __fp16, __fp16(), std::make_sharedGEMMCaller__fp16, __fp16, float, float(), … std::make_sharedGEMMCallerstd::int8_t, std::int8_t, std::int32_t, std::int32_t(), … };第1级 - 精确匹配如果输入的src、wei、dst和bias的类型与既定的g_gemmImpls里的数据类型完全匹配就直接调用对应的GEMMCaller方法。// check for exact match for (auto gemmImpl : g_gemmImpls) { bool isTypeMatch (gemmImpl-GetSrcDt() srcType) (gemmImpl-GetWeiDt() weiType) (gemmImpl-GetDstDt() dstType) (gemmImpl-GetBiaDt() biaType); if (isTypeMatch) { gemmImpl-Call(gemmInfo, src, wei, dst, bia, alpha, beta, numThreads); return; } }第2级 - 扩展精度匹配 通过GetWiderType部分获取扩展的目标数据类型然后选择dst为widerType类型的GEMMCaller作为目标函数将其余参数扩展转换为目标函数的参数类型然后调用Call函数继续执行。如果其余参数有比目标函数的参数更宽的数据类型则继续回退到全FP32类型。// try to convert to wider type and check if there’s implementation with wider type auto widerType GetWiderType(srcType, weiType, dstType, biaType);for (auto gemmImpl : g_gemmImpls) { bool isWiderType ((gemmImpl-GetDstDt() widerType) (dstType widerType)) ((srcType.GetSize() gemmImpl-GetSrcDt().GetSize()) (srcType.IsSigned() gemmImpl-GetSrcDt().IsSigned())) … if (isWiderType) { MatrixConverter s(srcType, gemmImpl-GetSrcDt(), src, m * k); MatrixConverter w(weiType, gemmImpl-GetWeiDt(), wei, k * n); MatrixConverter b(biaType, gemmImpl-GetBiaDt(), bia, biasM * biasN); gemmImpl-Call(gemmInfo, s.Get(), w.Get(), dst, b.Get(), alpha, beta, numThreads); return; } }GetWiderType函数实现了类型扩展如果存在BF16则返回BF16如果输入全是FP或integer类型就返回位数最宽的那个参数的类型其他情况返回FP32类型至于为什么是FP32是因为FP32是kudnn支持的最宽数据类型对其他数据类型兼容性最好。第3级 - 全FP32回退 将所有输入类型全部转换为FP32类型调用FP32的GEMMCaller计算最后将计算输出dst转换为原来转换前的数据类型作为最终输出。// some integer combinations can’t be supported when all the types are converted to wider type MatrixConverter s(srcType, Element::TypeT::F32, src, m * k); … GEMMCallerfloat, float, float, float {}.Call(gemmInfo, s.Get(), w.Get(), d.Get(), b.Get(), alpha, beta, numThreads); if (dstType ! Element::TypeT::F32) { switch (dstType) { case Element::TypeT::F16: { Service::ConvertFp32ToFp16(static_castconst float *(d.Get()), static_cast__fp16 *(dst), m * n); break; } … } }### 3.2.4. GEMMCaller::CallGEMMCaller负责从gemmInfo中获取矩阵乘所需的M、N、K、lda、ldb、ldc、transa、transb等参数结合src、wei、dst、bias和offsetC得到cblas_gemm所需的全部参数再通过调用BlasSetNumThreadsLocal设置BLAS线程数最后调用GEMMWrapper完成计算### 3.2.5. GEMMWrapperGEMMWrapper特化模板函数调用具体的cblas_?gemm接口完成计算。template typename SrcDt, typename WeiDt, typename DstDt, typename BiaDt void GEMMWrapper(…)以void GEMMWrapperfloat, float, float, float为例将矩阵乘法分为三种情况优化 ① n1: GEMV矩阵-向量乘选取合适的布局后调用 cblas_sgemv ② m1: GEMV向量-矩阵乘 ③ 其他: GEMM直接调用 cblas_sgemm最后通过 AddBias 添加偏置项

相关新闻

2026/8/3 1:27:23

解放双手!三月七小助手:星穹铁道玩家的智能自动化伙伴

解放双手!三月七小助手:星穹铁道玩家的智能自动化伙伴 【免费下载链接】March7thAssistant 崩坏:星穹铁道全自动 三月七小助手 项目地址: https://gitcode.com/gh_mirrors/ma/March7thAssistant 还在为《崩坏:星穹铁道》中…

2026/8/3 1:27:23

团队转型实战:赛马局机制如何提升协作效率

1. 从"路人"到"战友"的蜕变实录去年第三季度,我们部门经历了一场堪称教科书级的团队转型。原本各自为战的12个成员,通过一场精心设计的"赛马局"活动,在短短两个月内实现了从"点头之交"到"背靠背…

2026/8/3 1:27:23

深入解析C++内存模型:从竞争条件到无锁编程实战

1. 项目概述:为什么我们需要深入理解C内存模型?如果你写过C多线程程序,并且经历过那种“明明逻辑都对,但程序就是会偶尔崩溃或者结果不对”的诡异时刻,那你大概率已经和内存模型打过照面了。这不是一个简单的语法问题&…

2026/8/3 2:17:25

WordPress编辑器对比与全站编辑指南

自 WordPress 引入古腾堡(Gutenberg)区块编辑器以来,关于“经典编辑器 vs 古腾堡”以及“全站编辑(FSE)”的讨论一直是社区的热门话题。对于进行 wordpress建站 的企业、内容创作者与开发者而言,选择匹配自…

2026/8/3 2:17:25

OPD爆火:大模型蒸馏,从抄知识变成抄判断力

文章目录1. 先唠唠:为啥传统蒸馏不够用了1.1 以前的玩法,本质就是抄答案1.2 现在卷的方向,早就变了2. 捋一捋:大模型对齐的进化之路2.1 从认字到会说话2.2 从会说话到说人话2.3 最后到学霸直接带飞3. 说人话:OPD到底是…

2026/8/3 2:17:25

SpringBoot高校超市管理系统开发实战

1. 项目背景与核心价值高校超市作为校园生活服务的重要场景,传统管理模式普遍存在三个痛点:手工记账效率低下、库存管理混乱、销售数据分析缺失。我去年为某高校改造的超市系统,上线后人力成本降低40%,库存周转率提升25%&#xff…

2026/8/3 2:17:25

UE5 CommonUI框架实战:构建现代化游戏菜单系统

1. 项目概述:为什么需要一个“现代化”的菜单系统?如果你用UE5做过几个项目,尤其是涉及到手柄操作或者需要频繁切换界面的游戏,大概率已经对传统的UMG菜单系统感到头疼了。按钮焦点乱跳、返回逻辑需要手动绑定、界面层级一复杂就难…

2026/8/3 2:17:25

解决UE5.2.1中Quixel Bridge的uAsset不可用错误:从诊断到修复

1. 项目概述:当Quixel Bridge在UE5.2.1中“罢工”如果你正在使用虚幻引擎5.2.1,并且试图通过Quixel Bridge将那些令人惊叹的Megascans资产拖入你的项目,却迎面撞上“下载失败:uAsset格式不可用”这个冰冷的错误提示,相…

2026/8/3 2:12:25

三相异步电动机核心公式解析:从原理到实战应用

1. 三相异步电动机:从“知其然”到“知其所以然”干了这么多年电气和自动化,我发现一个挺有意思的现象:很多工程师,包括一些经验丰富的老师傅,对三相异步电动机这个“工业心脏”的熟悉程度,可能还停留在“接…

2026/8/2 0:02:18

如何用免费工具突破游戏窗口限制:SRWE完整使用指南

如何用免费工具突破游戏窗口限制:SRWE完整使用指南 【免费下载链接】SRWE Simple Runtime Window Editor 项目地址: https://gitcode.com/gh_mirrors/sr/SRWE 你是否遇到过这样的困扰?想为心爱的游戏截图,却发现游戏不支持自定义分辨率…

2026/8/2 1:52:02

实测才敢推 AI论文网站 2026最新测评与推荐

2026年真正好用的AI论文网站,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。一、综…

2026/8/1 0:03:49

2026必备!AI论文网站测评:最新推荐与深度对比

2026年真正好用的AI论文网站,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。 一、…

2026/8/2 8:56:50

摆脱论文困扰!盘点2026年全网爆红的的AI论文写作工具

一天写完毕业论文在2026年已不再是天方夜谭。2026年最炸裂、实测能大幅提速的AI论文写作工具,覆盖选题构思、文献整理、内容生成、格式排版等核心场景,真正帮你高效搞定论文难题。 一、全流程王者:一站式搞定论文全链路(一天定稿首…