- 人工智能
- 深度学习
- 算子库
- CANN
- Ascend
【免费下载链接】asc-devkit
本项目是CANN 推出的昇腾AI处理器专用的算子程序开发语言,原生支持C和C++标准规范,主要由类库和语言扩展层构成,提供多层级API,满足多维场景算子开发诉求。
导读
在 CANN asc-devkit 提供的昇腾算子开发语言(Ascend C)中,Matmul 高阶 API 默认由框架自动完成「Global Memory(GM)→ L1 Buffer → L0A/L0B → Cube」的数据搬入流程。而当开发者需要自行掌控矩阵数据在 L1 Buffer 中的摆放与生命周期时,可以使用TSCM(Temp Swap Cache Memory)输入的 Matmul特性:由开发者把 A、B 甚至 Bias 矩阵完整地搬入并驻留在 TSCM(即 L1 Buffer 对应的逻辑内存)中,再通过SetTensorA/SetTensorB将 TSCM 地址直接交给 Matmul 计算。读完本文,你将掌握 TSCM 的逻辑内存含义、适用场景与硬性约束、完整的调用代码范式,以及配套的 Tiling 配置和可运行的仓库样例,能够在算子开发中自主实现"自定义搬入 + 跨 Matmul 对象的全局 Double Buffer"这类高阶调度。
TSCM 是什么:L1 Buffer 的逻辑内存视图
在 AI Core 硬件中,L1 Buffer 是位于 Global Memory 与 L0A/L0B Buffer 之间的通用内部存储,用于暂存 Cube 计算单元需要反复使用的数据,以减少从总线读写的次数。硬件存储单元与搬运单元的详细介绍见基本架构。
TSCM 表示 L1 Buffer 空间对应的逻辑内存。同一个物理内存可能对应多个不同的 TPosition,例如 L1 Buffer 同时映射到 A1、B1、C1 和 TSCM 四个逻辑位置,具体语义取决于当前所服务的计算流程(左矩阵暂存、右矩阵暂存或共享通信),详见数据通路。
从数据结构看,TSCM 是队列机制在目的地址为 TSCM Position 时的别名,与 TQue、TQueBind 属于同一类型结构,定义于 TSCM 简介:
template <TPosition pos, int32_t depth, auto mask = 0> using TSCM = TQueBind<pos, TPosition::TSCM, depth, mask>;其中:
| 模板参数 | 含义 |
|---|---|
pos | 队列逻辑位置,可以为VECIN、GM,表示 TSCM 数据的来源方向 |
depth | 队列深度,表示队列可连续入队/出队的次数。队列深度为 1 时编译器有特殊优化,推荐设置为 1 |
mask | 比特位或TQueConfig配置,控制 VECTOR0 与 VECTOR1 映射到 Cube 上的内存位置是否错开等 |
TSCM 支持AllocTensor/EnQue/DeQue/FreeTensor接口,且必须严格按照AllocTensor → EnQue → DeQue → FreeTensor的顺序配对使用,完整生命周期管理;不支持VacantInQue、HasTensorInQue等查询类接口。
功能与使用场景
功能介绍:开发者可以自行管理 TSCM 以高效利用硬件资源。例如缓存一份 TSCM 数据,在不同使用场景中灵活配置为 Matmul 操作的 A 矩阵、B 矩阵或 Bias 偏置矩阵,实现内存复用与计算效率优化。在 TSCM 输入场景下,用户管理整块 TSCM 内存空间,Matmul 直接使用传入的 TSCM 内存地址,不进行 Global Memory 到 TSCM 的数据搬入(搬入动作由开发者自己完成)。
使用场景:需要自定义数据搬入到 TSCM 及自定义管理的场景,即需要自定义实现数据搬入功能,例如:
- 非连续搬入;
- 对搬入数据进行预处理(如格式转换、填充、量化等)。
用户通过自定义管理 TSCM,可以灵活配置 MTE2 流水(GM → L1 Buffer 方向的搬运单元,参见搬运单元),实现跨 Matmul 对象的全局 Double Buffer。Double Buffer 的核心思想是利用 MTE 指令队列与计算指令队列的独立性,让数据搬运与计算并行执行以隐藏搬运时间,详见 Double Buffer。
约束说明:TSCM 输入必须全载
设置为 TSCM 输入的矩阵必须在 TSCM 中全载。所谓全载,即全部的矩阵数据同时搬入并保持在 TSCM 中,而不是像默认 GM 输入那样按迭代分块搬入。这一点也体现在 SetTensorA 的约束中:若设置 TSCM 首地址,默认矩阵可全载、已经位于 TSCM,Iterate 接口无需再进行 GM → L1 Buffer(A1/B1)搬运。
因此,TSCM 的 Buffer 大小必须一次性覆盖单核参与计算所需的完整矩阵数据量,例如 A 矩阵需要申请M * K * sizeof(元素类型)字节的空间(见下文代码)。
核心调用流程与代码范式
TSCM 输入 Matmul 的调用流程可概括为五步:申请 TSCM Buffer → 自定义搬运 → 入队/出队 → 设置矩阵输入 → 迭代计算 → 释放 Buffer。以下为文档中的核心调用示例(A 矩阵使用 TSCM 输入,B 矩阵与 Bias 使用 GM 输入):
TQue<TPosition::A1, 1> scm; // 队列逻辑位置A1,队列深度为1 pipe->InitBuffer(scm, 1, tiling.M * tiling.Ka * sizeof(A_T)); // A_TYPE的TPosition为TSCM,B_TYPE的TPosition为GM Matmul<A_TYPE, B_TYPE, C_TYPE, BIAS_TYPE> mm1; REGIST_MATMUL_OBJ(&pipe, GetSysWorkSpacePtr(), mm1); mm1.Init(&tiling); // 自定义A矩阵GM到TSCM的搬运 auto scmTensor = scm.AllocTensor<A_T>(); DataCopy(scmTensor, gm_a, tiling.M * tiling.Ka); scm.EnQue(scmTensor); LocalTensor<A_T> scmLocal = scm.DeQue<A_T>(); // A矩阵设置为TSCM输入,B矩阵为GM输入 mm1.SetTensorA(scmLocal); mm1.SetTensorB(gm_b); mm1.SetBias(gm_bias); mm1.IterateAll(gm_c); scm.FreeTensor(scmLocal);对每一步骤逐一说明:
- 申请并初始化 TSCM Buffer:
TQue<TPosition::A1, 1> scm声明队列;pipe->InitBuffer(scm, 1, tiling.M * tiling.Ka * sizeof(A_T))申请深度为 1、大小为整块 A 矩阵数据量(M × Ka 个元素)的 TSCM Buffer。队列深度 1 满足"单次全载、一次性消费"的语义,编译器对该场景有专门优化。 - 创建并注册 Matmul 对象:
Matmul<A_TYPE, B_TYPE, C_TYPE, BIAS_TYPE> mm1创建对象,其中A_TYPE的 TPosition 必须为TSCM;REGIST_MATMUL_OBJ(&pipe, GetSysWorkSpacePtr(), mm1)完成对象初始化注册,mm1.Init(&tiling)传入 Tiling 参数。 - 自定义数据搬入:
scm.AllocTensor<A_T>()从队列分配 tensor,DataCopy(scmTensor, gm_a, tiling.M * tiling.Ka)将 GM 中的 A 矩阵连续搬入 TSCM。此处的 DataCopy 可以由任意自定义搬运逻辑(含预处理)替代,这正是本特性的价值所在。 - 入队/出队完成同步:
scm.EnQue(scmTensor)在搬运完成后将 tensor 入队,scm.DeQue<A_T>()取出用于计算,EnQue/DeQue 之间隐含了搬运完成与计算开始的同步语义(对应 MTE2 与 Cube 流水之间的事件同步)。 - 设置输入并计算:
mm1.SetTensorA(scmLocal)将 TSCM 地址作为 A 矩阵输入;SetTensorB(gm_b)、SetBias(gm_bias)分别设置 GM 来源的 B 矩阵与 Bias;mm1.IterateAll(gm_c)完成单核全量计算并输出到 GM。 - 释放 Buffer:计算完成后
scm.FreeTensor(scmLocal)归还 Buffer,供下一次搬入复用。
模板参数与 Tiling 侧的配套配置
TSCM 输入不仅要在核函数(Kernel)侧设置,还必须保证核函数侧、模板参数侧、Tiling 侧三方配置一致。
MatmulType 模板参数
创建 Matmul 对象时,A、B、C、Bias 的类型信息通过MatmulType定义,包括内存逻辑位置、数据格式、数据类型、是否转置、数据排布和是否开启 L1 复用,参见 Matmul 模板参数与 Matmul 使用说明。TSCM 场景下关键参数:
| MatmulType 参数 | TSCM 输入的要求 |
|---|---|
POSITION | A 矩阵 / B 矩阵可设置为TPosition::TSCM;Ascend 950PR&950DT 系列还支持 Bias 设置为 TSCM |
FORMAT | 输入矩阵设置为 TSCM 时,对应 Format仅支持CubeFormat::NZ(以 Ascend 950PR&950DT 系列为例) |
SRCPOSITION | A/B 矩阵 POSITION 配置为 TSCM 时,必须设置 TSCM 中矩阵数据的来源逻辑位置,默认为TPosition::GM,也可为VECOUT |
在仓库样例中,A 矩阵的完整类型声明为:
AscendC::MatmulType<AscendC::TPosition::TSCM, CubeFormat::NZ, AType>Tiling 侧 SetAType 配置
Tiling 侧通过SetAType设置 A 矩阵的位置、数据格式、数据类型、是否转置等信息,这些信息必须和核函数(Kernel)侧的设置保持一致,参见 SetAType。样例中的 Tiling 配置为:
tilingApi.SetAType( matmul_tiling::TPosition::TSCM, matmul_tiling::CubeFormat::NZ, matmul_tiling::DataType::DT_FLOAT16, IS_TRANS_A); tilingApi.SetBType( matmul_tiling::TPosition::GM, matmul_tiling::CubeFormat::ND, matmul_tiling::DataType::DT_FLOAT16, IS_TRANS_B); tilingApi.SetCType(matmul_tiling::TPosition::GM, matmul_tiling::CubeFormat::ND, matmul_tiling::DataType::DT_FLOAT); tilingApi.SetBiasType(matmul_tiling::TPosition::GM, matmul_tiling::CubeFormat::ND, matmul_tiling::DataType::DT_FLOAT);即 A 矩阵位置为TSCM、格式为NZ、类型为float16,B 矩阵与 Bias 均为 GM + ND。
转置一致性要求
若 A 矩阵MatmulType的ISTRANS参数设置为false,SetTensorA的isTransposeA只能为false;若强行设置为true,精度会有异常。对于非 half、非 bfloat16_t 输入类型,为确保 Tiling 侧与核函数侧 L1 Buffer 空间计算大小保持一致及结果精度正确,核函数侧ISTRANS、SetTensorA的isTransposeA、Tiling 侧SetAType的isTrans三个参数必须同时为 true 或同时为 false,详见 SetTensorA。
仓库完整样例:编译、运行与结果验证
asc-devkit 仓库提供了与本文场景完全对应的完整算子样例matmul_tscm(自定义数据来源为 GM 的 TSCM 输入的 Matmul 算子样例),位于 examples/01_simd_cpp_api/04_advanced_api/00_matmul/matmul_tscm。样例组成如下:
- matmul_tscm.asc:核函数与 Host 侧主程序;
- data_utils.h:二进制数据读写工具;
- CMakeLists.txt:编译配置;
scripts/gen_data.py与scripts/verify_result.py:测试数据生成与结果校验脚本。
样例支持的硬件与软件版本
| 产品 | CANN 软件版本 |
|---|---|
| Ascend 950PR & 950DT 系列产品 | >= CANN 9.1.0 |
| Atlas A3 系列产品 | >= CANN 9.0.0 |
| Atlas A2 系列产品 | >= CANN 9.0.0 |
编译与运行步骤
在样例根目录下执行:
source ${install_path}/cann/set_env.sh # 配置CANN环境变量,install_path为CANN包安装目录,默认 /usr/local/Ascend mkdir -p build && cd build cmake -DCMAKE_ASC_ARCHITECTURES=dav-2201 ..; make -j; # 编译工程,默认npu模式 python3 ../scripts/gen_data.py # 生成测试输入数据 ./demo # 执行编译生成的可执行程序 python3 ../scripts/verify_result.py output/output.bin output/golden.bin # 验证输出结果编译选项说明:
| 选项 | 可选值 | 说明 |
|---|---|---|
CMAKE_ASC_RUN_MODE | npu(默认)、cpu、sim | 运行模式:NPU 运行、CPU 调试、NPU 仿真 |
CMAKE_ASC_ARCHITECTURES | dav-2201(默认)、dav-3510 | NPU 架构:dav-2201 对应 Atlas A2/A3 系列,dav-3510 对应 Ascend 950PR & 950DT 系列 |
使用 CPU 调试或 NPU 仿真模式时添加-DCMAKE_ASC_RUN_MODE=cpu或-DCMAKE_ASC_RUN_MODE=sim参数,例如:
cmake -DCMAKE_ASC_RUN_MODE=cpu -DCMAKE_ASC_ARCHITECTURES=dav-2201 ..; make -j; # cpu调试模式 cmake -DCMAKE_ASC_RUN_MODE=sim -DCMAKE_ASC_ARCHITECTURES=dav-2201 ..; make -j; # NPU仿真模式注意:切换编译模式前需清理 cmake 缓存,可在 build 目录下删除
CMakeCache.txt后重新 cmake。
执行结果输出test pass!表示精度对比成功。CANN 环境安装方式可参考 quick_start。
样例代码中的 TSCM 使用要点
样例核函数MatmulKernel::Process中完整呈现了"自定义搬入 + TSCM 输入"的实现(matmul_tscm.asc):
// Copy aMatrix from gm to tscm AscendC::TSCM<AscendC::TPosition::GM, 1> scm; pipe->InitBuffer(scm, 1, tiling.M * tiling.Ka * sizeof(AType)); auto scmTensor = scm.AllocTensor<AType>(); DataCopy(scmTensor, aGlobal, tiling.M * tiling.Ka); scm.EnQue(scmTensor); AscendC::LocalTensor<AType> scmLocal = scm.DeQue<AType>(); matmulObj.SetTensorA(scmLocal, IS_TRANS_A); matmulObj.SetTensorB(bGlobal, IS_TRANS_B); if (tiling.isBias) { matmulObj.SetBias(biasGlobal); } matmulObj.IterateAll(cGlobal); matmulObj.End(); scm.FreeTensor(scmLocal);与文档示例略有差异的是,样例使用TSCM<TPosition::GM, 1>声明队列(来源为 GM),Buffer 大小按整块 A 矩阵M * Ka元素全载申请,并在尾块场景通过matmulObj.SetTail(tailM, tailN)处理多核切分的非对齐边界。样例同时给出了多核切分的偏移计算方式(CalcOffset,按 M 方向优先切分 core 索引)与 Host 侧 Tiling 生成、ACL 内存申请、kernel launch 的完整流程,可作为自研算子的直接模板。
底层数据通路与设计要点
TSCM 输入在数据流中的位置
默认 Matmul 的 Cube 计算数据流为GM → L1 → L0A/L0B → Cube → L0C → FixPipe → GM。TSCM 输入场景下,GM → L1(TSCM)这一段由开发者通过DataCopy(MTE2 流水)自行完成,Matmul 只负责TSCM(L1)→ L0A/L0B → Cube → L0C → GM的后续环节。同时需要注意,NPU 架构版本 3510(Ascend 950PR & 950DT)删除了 GM 到 L0A/L0B 的直接数据通路,L1 Buffer 成为矩阵计算输入的必经中转,TSCM 管理的价值在该架构上更为突出,详见数据通路。
TSCM Buffer 数量与资源限制
由于 TSCM 分配的 Buffer 中存储着同步事件 eventID,且该结构与 Cube 类高阶 API(Matmul 高阶 API、宏函数调用方式)共同使用,因此同一个 TPosition 上 TSCM Buffer 的数量与硬件的同步事件 eventID 以及 Matmul 对象数量有关。具体限制如下(见 TSCM 简介):
- L1 Buffer(TSCM)从 Unified Buffer(UB,VECIN)发起的 Buffer 块数量与 Matmul 对象数量之和最大为 10 个;
- 不允许申请的 TSCM Buffer 超出规格限制,超出规格可能引起未定义行为;
- 从 UB 发起的 L1 Buffer(TSCM)队列内部实现会占用核间同步 flagId,若与
CrossCoreSetFlag/CrossCoreWaitFlag同时使用需注意避免 flagId 冲突。
生命周期与内存复用建议
- TSCM 队列的
AllocTensor → EnQue → DeQue → FreeTensor必须严格配对执行,EnQue返回 false 表示队列已满、入队失败(见 EnQue),DeQue返回队列中取出的 LocalTensor(见 DeQue)。 - 推荐队列深度为 1;深度为 1 时编译器有特殊优化,性能通常更好。
- 开发者可缓存一份 TSCM 数据,在不同使用场景中灵活配置为 A 矩阵、B 矩阵或 Bias 矩阵,实现内存复用;配合自定义 MTE2 搬运节奏,可以实现跨 Matmul 对象的全局 Double Buffer,将数据搬运时间隐藏在计算流水之后。
小结
TSCM 输入的 Matmul 是 Ascend C 中"内存管理下放给开发者"的关键特性:它以 TSCM 逻辑位置封装 L1 Buffer,让矩阵数据在进入 Cube 之前完全处于开发者的掌控之下。使用时需要把握三个核心点:矩阵必须在 TSCM 全载(Buffer 大小按整块数据申请)、核函数侧/Tiling 侧配置严格一致(POSITION=TSCM、Format=NZ、isTrans 对齐)、队列生命周期严格配对(AllocTensor → EnQue → DeQue → FreeTensor)。在此基础上,开发者即可自由实现非连续搬入、数据预处理以及跨 Matmul 对象的全局 Double Buffer 等高阶调度,相关完整范式可直接参考仓库中的 matmul_tscm 样例。
- 人工智能
- 深度学习
- 算子库
- CANN
- Ascend
【免费下载链接】asc-devkit
本项目是CANN 推出的昇腾AI处理器专用的算子程序开发语言,原生支持C和C++标准规范,主要由类库和语言扩展层构成,提供多层级API,满足多维场景算子开发诉求。
相关推荐
Ascend C 算子开发:Matmul VECOUT 输入直调实战——基于 asc-devkit 自主管理 Unified Buffer 的矩阵乘样例解析
Ascend C 算子开发:Matmul VECOUT 输入直调实战——基于 asc devkit 自主管理 Unified Buffer 的矩阵乘样例解析 本
人工智能深度学习算子库CANNAscendasc-devkit Matmul 自定义 TSCM 输入直调样例详解:从 L1 Buffer 自主管理到编译运行全流程
asc devkit Matmul 自定义 TSCM 输入直调样例详解:从 L1 Buffer 自主管理到编译运行全流程 本篇文章围绕 asc devkit(C
人工智能深度学习算子库CANNAscendasc-devkit ND_ALIGN 格式矩阵乘输出实战:Matmul API 实现 N 方向 32 字节对齐的 C 矩阵输出
asc devkit ND_ALIGN 格式矩阵乘输出实战:Matmul API 实现 N 方向 32 字节对齐的 C 矩阵输出 本文基于 asc devkit
人工智能深度学习算子库CANNAscend
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考