CANN ops-nn 量化分组矩阵乘累加算子 QuantMatmulReduceSum 深度解析与 aclnn 调用实战
人工智能算子库深度学习CANNAscend【免费下载链接】ops-nn本项目是CANN提供的神经网络类计算算子库实现网络在NPU上加速计算。项目地址https://gitcode.com/cann/ops-nn点击查看免费下载QuantMatmulReduceSum 是 CANN ops-nn 神经网络算子库中实现「量化分组矩阵乘 跨 batch 累加」融合计算的算子它对 INT8 量化的多组矩阵分别做矩阵乘按行/列缩放后把所有组的计算结果累加为一张 (M, N) 输出矩阵属于典型的「Matmul ReduceSum」二合一融合算子。本文基于 matmul/quant_matmul_reduce_sum 目录下的官方 README、aclnn 接口文档与完整源码系统讲解其产品支持范围、数学语义、输入输出约束、两段式 aclnn 接口用法并深入 op_def、InferShape、Tiling 与 Kernel 源码帮助你在 Atlas A2/A3 系列 NPU 上正确、高效地完成 INT8 量化矩阵乘累加计算。产品支持情况QuantMatmulReduceSum 算子以及对应的 aclnnQuantMatmulReduceSumWeightNz 接口在不同产品系列上的支持情况如下表所示产品是否支持Ascend 950PR/Ascend 950DT×Atlas A3 训练系列产品/Atlas A3 推理系列产品√Atlas A2 训练系列产品/Atlas A2 推理系列产品√Atlas 200I/500 A2 推理产品×Atlas 推理系列产品×Atlas 训练系列产品×也就是说当前该算子仅面向 Atlas A2 / Atlas A3 训练与推理系列产品Ascend 910B 及后续同源平台使用前请先确认目标设备的型号归属。这一支持范围同样可以从算子注册代码中印证在 op_host/quant_matmul_reduce_sum_def.cpp 中QuantMatmulReduceSum的 AICore 配置仅通过AddConfig(ascend910b, ...)与AddConfig(ascend910_93, ...)添加其余平台未注册。功能说明量化分组矩阵乘再求和算子语义QuantMatmulReduceSum 完成「量化的分组矩阵计算」随后把所有组的矩阵计算结果相加后输出。以 batch 组量化矩阵为输入每组内部执行一次 INT8 矩阵乘乘积累加结果经x1Scale行缩放与x2Scale列缩放双重缩放后在 batch 维度上求和最终只输出一张 (M, N) 矩阵。计算公式$$ out \sum_{i0}^{batch}(x1_i x2_i) * x1Scale * x2Scale $$其中$x1$ 是维度为 $(batch, M, K)$ 的左矩阵INT8$x2$ 是维度为 $(batch, K, N)$ 的右矩阵INT8$out$ 是维度为 $(M, N)$ 的输出矩阵$x1Scale$ 是维度为 $(batch, M)$ 的缩放向量FLOAT32对矩阵乘结果做行缩放$x2Scale$ 是维度为 $(N,)$ 的缩放向量BFLOAT16对矩阵乘结果做列缩放。从公式可以直观理解该算子的典型落地场景多 batch 的量化矩阵乘结果需要在 batch 维上归约例如多头注意力中多 head 计算结果累加、批量样本的聚合输出等在 NPU 上若拆分为「先逐组 Matmul 再 ReduceSum」会引入多次 Kernel 启动与中间张量搬移而 QuantMatmulReduceSum 将其融合为一次 Kernel 完成显著降低调用开销。广播语义两个缩放向量在实际计算时会被广播为 (batch, M, N) 维度后参与乘算x1Scale形状为 (batch, M)广播为 (batch, M, N) 后对每组的矩阵乘结果按行缩放x2Scale形状为 (N,)广播为 (batch, M, N) 后对每组的矩阵乘结果按列缩放。该语义在 docs/aclnnQuantMatmulReduceSumWeightNz.md 的参数说明中明确给出。参数说明下表是算子层IR/Graph 层的输入输出定义其中 x1、x2、dims 为必选输入bias、x1_scale、x2_scale、y_scale、x1_offset、x2_offset、y_offset、x2_table 均为可选输入当前版本下这些可选参数在实际调用时均需传空参数名输入/输出/属性描述数据类型数据格式x1输入矩阵乘运算中的左矩阵。INT8NDx2输入矩阵乘运算中的右矩阵。INT8FRACTAL_NZx1Scale输入对矩阵乘结果进行行缩放的一维向量。FLOAT32NDx2Scale输入对矩阵乘结果进行列缩放的一维向量。BFLOAT16NDout输出结果矩阵。FLOAT32ND说明上表出自算子 README其中out的数据类型为 FLOAT32 是 IR 层原型早期的描述当前仓库实际实现中输出y/out的数据类型为BFLOAT16见下方「源码级实现解析」中 op_def 与 InferShape 的分析以及 aclnn 接口文档中 out 的 BF16 说明。实际使用请以 aclnn 接口文档与当前源码为准。对应的 IR 层完整输入输出定义包含所有预留可选输入可以在 op_host/quant_matmul_reduce_sum_def.cpp 中查看必选输入x1INT8/ND、x2INT8/FRACTAL_NZ、dimsINT64/NDreduce 维度可选输入biasBF16、x1_scaleFLOAT、x2_scaleBF16、y_scaleUINT64、x1_offsetBF16、x2_offsetBF16、y_offsetBF16、x2_tableINT8输出yBF16/ND属性dtype输出数据类型必填、compute_type默认 -1、transpose_x1默认 false、transpose_x2默认 false、group_size默认 -1、keep_dims默认 false。约束说明使用 QuantMatmulReduceSum 算子时需满足以下约束不支持空 Tensorx1、x2、x1Scale、x2Scale、out 均不能为空左右矩阵不支持非连续 TensortransposeX1、transposeX2当前版本仅支持falsekeepDims当前版本仅支持false输出不保留 batch 维度恒为 (M, N)dimsreduce 维度当前版本仅支持[0]即固定在第 0 维batch 维做 ReduceSumgroupSize为预留参数当前版本需传入 0yScale、x1Offset、x2Offset、yOffset、bias为预留参数当前版本需传入 nullptr空 Tensor 或空指针确定性说明aclnnQuantMatmulReduceSumWeightNz 默认非确定性实现可通过aclrtCtxSetSysParamOpt开启确定性。aclnn 调用说明与两段式接口调用方式该算子对外提供 aclnn 单算子调用接口支持通过aclnnQuantMatmulReduceSumWeightNz等方式调用。样例代码位于 examples/test_aclnn_quant_matmul_reduce_sum_weight_nz.cpp另有小 shape 边界样例 examples/test_aclnn_quant_matmul_reduce_sum_small_case.cpp接口详细说明见 docs/aclnnQuantMatmulReduceSumWeightNz.md。两段式接口aclnn 单算子调用采用 CANN 标准的两段式接口必须先调用aclnnQuantMatmulReduceSumWeightNzGetWorkspaceSize获取计算所需的 workspace 大小以及包含算子计算流程的执行器再调用aclnnQuantMatmulReduceSumWeightNz执行计算。第一段接口获取 workspace 大小与执行器aclnnStatus aclnnQuantMatmulReduceSumWeightNzGetWorkspaceSize( const aclTensor *x1, const aclTensor *x2, const aclTensor *x1Scale, const aclTensor *x2Scale, const aclTensor *yScale, const aclTensor *x1Offset, const aclTensor *x2Offset, const aclTensor *yOffset, const aclTensor *bias, bool transposeX1, bool transposeX2, int64_t groupSize, const aclIntArray *dims, bool keepDims, aclTensor *out, uint64_t *workspaceSize, aclOpExecutor **executor)第二段接口执行计算aclnnStatus aclnnQuantMatmulReduceSumWeightNz( void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, aclrtStream stream)第一段接口参数详解参数名输入/输出描述使用说明数据类型数据格式维度(shape)非连续Tensorx1aclTensor*输入公式中的x1。不支持空Tensor。INT8ND(batch, m, k)-x2aclTensor*输入公式中的x2。不支持空Tensor。各维度表示 (batch, n1, k1, k0, n0)其中 k016、n032x1 shape 中的 k 与 x2 shape 中的 k1 需满足 ceil(k/16)k1x2 的 n1 与 out 的 n 需满足 ceil(n/n0)n1。可使用 aclnnCalculateMatmulWeightSizeV2 与 aclnnTransMatmulWeight 接口完成 ND → FRACTAL_NZ 格式转换原始 ND shape 为 (batch, k, n)。INT8NZ5维-x1ScaleaclTensor*输入公式中的x1Scale。不支持空Tensor。计算时广播为 (batch, m, n)。FLOAT32ND(batch, m)√x2ScaleaclTensor*输入公式中的x2Scale。不支持空Tensor。计算时广播为 (batch, m, n)。BFLOAT16ND(n,)√yScaleaclTensor*输入预留参数当前版本不支持。需要传入nullptr。----x1OffsetaclTensor*输入预留参数当前版本不支持。需要传入nullptr。----x2OffsetaclTensor*输入预留参数当前版本不支持。需要传入nullptr。----yOffsetaclTensor*输入预留参数当前版本不支持。需要传入nullptr。----biasaclTensor*输入预留参数当前版本不支持。需要传入nullptr。----transposeX1bool输入x1的输入shape是否包含transpose。当前版本仅支持false。bool---transposeX2bool输入x2的输入shape是否包含transpose。当前版本仅支持false。bool---groupSizeint64_t输入预留参数当前版本不支持。需要传入0。----dimsaclIntArray*输入指定reduce维度。当前版本仅支持填[0]表示在第0维batch维做ReduceSum。INT64---keepDimsbool输入是否在输出张量中保留输入张量的维度。当前版本仅支持false。----outaclTensor*输出公式中的out。-BFLOAT16ND(m, n)√workspaceSizeuint64_t*输出返回需要在Device侧申请的workspace大小。-----executoraclOpExecutor**输出返回op执行器包含了算子计算流程。-----返回值与错误码两段接口均返回aclnnStatus状态码。第一段接口完成入参校验出现以下场景时报错返回值错误码描述ACLNN_ERR_PARAM_NULLPTR161001传入的 x1、x2、x1Scale、x2Scale 或 out 是空指针。ACLNN_ERR_PARAM_INVALID161002x1、x2、x1Scale、x2Scale 或 out 的数据类型和数据格式不在支持范围之内或 shape 不满足校验条件或为空 Tensor。这些校验逻辑在源码中有完整对应在 op_host/op_api/aclnn_quant_matmul_reduce_sum.cpp 中CheckInputs()依次执行CheckInputExistence()空指针检查、预留参数拦截、CheckInputAttr()transposeX1/transposeX2/groupSize/keepDims 仅允许默认值、CheckDtype()x1/x2 必须 INT8、x1Scale 必须 FLOAT、x2Scale 与 out 必须 BF16、CheckFormat()x2 必须 FRACTAL_NZ其余禁止 NZ、CheckInputShape()x1/x2 必须 3 维且 batch、k 相等x1Scale 必须 (batch, m)x2Scale 必须 (n,)。调用示例完整可运行代码以下示例来自 examples/test_aclnn_quant_matmul_reduce_sum_weight_nz.cpp完整展示了从初始化、构造 Tensor、两段式调用到取回结果的全部流程。示例采用 b8、m2048、k1024、n7168 的 shape所有输入数据初始化为 1.0INT8 为 1便于快速验证结果。#include cstdint #include cstring #include iostream #include memory #include vector #include acl/acl.h #include aclnnop/aclnn_quant_matmul_reduce_sum.h #include aclnnop/aclnn_trans_matmul_weight.h #define CHECK_RET(cond, return_expr) \ do { \ if (!(cond)) { \ return_expr; \ } \ } while (0) #define CHECK_FREE_RET(cond, return_expr) \ do { \ if (!(cond)) { \ Finalize(deviceId, stream); \ return_expr; \ } \ } while (0) #define LOG_PRINT(message, ...) \ do { \ printf(message, ##__VA_ARGS__); \ } while (0) int64_t GetShapeSize(const std::vectorint64_t shape) { int64_t shapeSize 1; for (auto i : shape) { shapeSize * i; } return shapeSize; } float Bfloat16ToFloat(uint16_t value) { // BF16是FP32的高16位低16位补0后即可得到对应的FP32位表示。 const uint32_t bits static_castuint32_t(value) 16U; float result 0.0F; std::memcpy(result, bits, sizeof(result)); return result; } int Init(int32_t deviceId, aclrtStream *stream) { // 固定写法资源初始化 auto ret aclInit(nullptr); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclInit failed. ERROR: %d\n, ret); return ret); ret aclrtSetDevice(deviceId); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclrtSetDevice failed. ERROR: %d\n, ret); return ret); ret aclrtCreateStream(stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclrtCreateStream failed. ERROR: %d\n, ret); return ret); return 0; } template typename T int CreateAclTensor(const std::vectorT hostData, const std::vectorint64_t shape, void **deviceAddr, aclDataType dataType, aclTensor **tensor) { auto size GetShapeSize(shape) * sizeof(T); // 调用aclrtMalloc申请device侧内存 auto ret aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclrtMalloc failed. ERROR: %d\n, ret); return ret); // 调用aclrtMemcpy将host侧数据拷贝到device侧内存上 ret aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclrtMemcpy failed. ERROR: %d\n, ret); return ret); // 计算连续tensor的strides std::vectorint64_t strides(shape.size(), 1); for (int64_t i shape.size() - 2; i 0; i--) { strides[i] shape[i 1] * strides[i 1]; } // 调用aclCreateTensor接口创建aclTensor *tensor aclCreateTensor(shape.data(), shape.size(), dataType, strides.data(), 0, aclFormat::ACL_FORMAT_ND, shape.data(), shape.size(), *deviceAddr); return 0; } void Finalize(int32_t deviceId, aclrtStream stream) { aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); } // 创建FRACTAL_NZ格式的x2通过aclnnCalculateMatmulWeightSizeV2计算NZ存储大小 template typename T int CreateAclTensorX2(const std::vectorT hostData, const std::vectorint64_t shape, void **deviceAddr, aclDataType dataType, aclTensor **tensor) { auto size static_castuint64_t(GetShapeSize(shape)); const aclIntArray *mat2Size aclCreateIntArray(shape.data(), shape.size()); auto ret aclnnCalculateMatmulWeightSizeV2(mat2Size, dataType, size); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnCalculateMatmulWeightSizeV2 failed. ERROR: %d\n, ret); return ret); size * sizeof(T); ret aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclrtMalloc failed. ERROR: %d\n, ret); return ret); ret aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclrtMemcpy failed. ERROR: %d\n, ret); return ret); std::vectorint64_t strides(shape.size(), 1); for (int64_t i shape.size() - 2; i 0; i--) { strides[i] shape[i 1] * strides[i 1]; } std::vectorint64_t storageShape; storageShape.push_back(GetShapeSize(shape)); *tensor aclCreateTensor(shape.data(), shape.size(), dataType, strides.data(), 0, aclFormat::ACL_FORMAT_FRACTAL_NZ, storageShape.data(), storageShape.size(), *deviceAddr); return 0; } int aclnnQuantMatmulWeightNzTest(int32_t deviceId, aclrtStream stream) { auto ret Init(deviceId, stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(Init acl failed. ERROR: %d\n, ret); return ret); // 2. 构造输入与输出 int64_t b 8; int64_t m 2048; int64_t k 1024; int64_t n 7168; // 创建x1 aclTensor std::vectorint64_t x1Shape {b, m, k}; void *x1DeviceAddr nullptr; aclTensor *x1 nullptr; std::vectorint8_t x1HostData(b * m * k, 1); ret CreateAclTensor(x1HostData, x1Shape, x1DeviceAddr, aclDataType::ACL_INT8, x1); std::unique_ptraclTensor, aclnnStatus (*)(const aclTensor *) x1TensorPtr(x1, aclDestroyTensor); std::unique_ptrvoid, aclError (*)(void *) x1DeviceAddrPtr(x1DeviceAddr, aclrtFree); CHECK_RET(ret ACL_SUCCESS, return ret); // 创建FRACTAL_NZ格式的x2 aclTensor std::vectorint64_t x2Shape {b, k, n}; void *x2DeviceAddr nullptr; aclTensor *x2 nullptr; std::vectorint8_t x2HostData(b * k * n, 1); ret CreateAclTensorX2(x2HostData, x2Shape, x2DeviceAddr, aclDataType::ACL_INT8, x2); std::unique_ptraclTensor, aclnnStatus (*)(const aclTensor *) x2HPTensorPtr(x2, aclDestroyTensor); std::unique_ptrvoid, aclError (*)(void *) x2HPDeviceAddrPtr(x2DeviceAddr, aclrtFree); CHECK_RET(ret ACL_SUCCESS, return ret); // 创建x1Scale aclTensor std::vectorint64_t x1ScaleShape {b, m}; void *x1ScaleDeviceAddr nullptr; std::vectorfloat x1ScaleHostData(b * m, 1); aclTensor *x1Scale nullptr; ret CreateAclTensor(x1ScaleHostData, x1ScaleShape, x1ScaleDeviceAddr, aclDataType::ACL_FLOAT, x1Scale); std::unique_ptraclTensor, aclnnStatus (*)(const aclTensor *) x1ScaleTensorPtr(x1Scale, aclDestroyTensor); std::unique_ptrvoid, aclError (*)(void *) x1ScaleDeviceAddrPtr(x1ScaleDeviceAddr, aclrtFree); CHECK_RET(ret ACL_SUCCESS, return ret); // 创建x2Scale aclTensorBF16数值1.0的位表示为0x3F80 std::vectorint64_t x2ScaleShape {n}; void *x2ScaleDeviceAddr nullptr; aclTensor *x2Scale nullptr; constexpr uint16_t bf16One 0x3F80U; std::vectoruint16_t x2ScaleHostData(n, bf16One); ret CreateAclTensor(x2ScaleHostData, x2ScaleShape, x2ScaleDeviceAddr, aclDataType::ACL_BF16, x2Scale); std::unique_ptraclTensor, aclnnStatus (*)(const aclTensor *) x2ScaleTensorPtr(x2Scale, aclDestroyTensor); std::unique_ptrvoid, aclError (*)(void *) x2ScaleDeviceAddrPtr(x2ScaleDeviceAddr, aclrtFree); CHECK_RET(ret ACL_SUCCESS, return ret); // 创建out aclTensorBF16数值0.0的位表示为0x0000 std::vectorint64_t outShape {m, n}; void *outDeviceAddr nullptr; aclTensor *out nullptr; std::vectoruint16_t outHostData(m * n, 0); ret CreateAclTensor(outHostData, outShape, outDeviceAddr, aclDataType::ACL_BF16, out); std::unique_ptraclTensor, aclnnStatus (*)(const aclTensor *) outTensorPtr(out, aclDestroyTensor); std::unique_ptrvoid, aclError (*)(void *) outDeviceAddrPtr(outDeviceAddr, aclrtFree); CHECK_RET(ret ACL_SUCCESS, return ret); bool transposeX1 false; bool transposeX2 false; // 创建dims aclIntArray第0维batch维做ReduceSum std::vectorint64_t dimsData {0}; aclIntArray *dims nullptr; dims aclCreateIntArray(dimsData.data(), dimsData.size()); CHECK_RET(dims ! nullptr, return ret); // 3. 两段式调用CANN算子库API uint64_t workspaceSize 0; aclOpExecutor *executor nullptr; // 第一段接口获取workspace大小与执行器预留参数传nullptrgroupSize传0keepDims传false ret aclnnQuantMatmulReduceSumWeightNzGetWorkspaceSize( x1, x2, x1Scale, x2Scale, nullptr, nullptr, nullptr, nullptr, nullptr, transposeX1, transposeX2, 0, dims, false, out, workspaceSize, executor); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnQuantMatmulReduceSumWeightNzGetWorkspaceSize failed. ERROR: %d\n, ret); return ret); // 根据第一段接口计算出的workspaceSize申请device内存 void *workspaceAddr nullptr; std::unique_ptrvoid, aclError (*)(void *) workspaceAddrPtr(nullptr, aclrtFree); if (workspaceSize 0) { ret aclrtMalloc(workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(allocate workspace failed. ERROR: %d\n, ret); return ret); workspaceAddrPtr.reset(workspaceAddr); } // 第二段接口执行计算 ret aclnnQuantMatmulReduceSumWeightNz(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnQuantMatmulReduceSumWeightNz failed. ERROR: %d\n, ret); return ret); // 4.固定写法同步等待任务执行结束 ret aclrtSynchronizeStream(stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclrtSynchronizeStream failed. ERROR: %d\n, ret); return ret); // 5. 获取输出的值将device侧内存上的结果拷贝至host侧 auto size GetShapeSize(outShape); // C无法直接打印BF16先用uint16_t读取其位表示再将BF16转换为FP32 std::vectoruint16_t resultData(size, 0); ret aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]), outDeviceAddr, size * sizeof(resultData[0]), ACL_MEMCPY_DEVICE_TO_HOST); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(copy result from device to host failed. ERROR: %d\n, ret); return ret); for (int64_t i 0; i 5; i) { LOG_PRINT(result[%ld] is: %f\n, i, Bfloat16ToFloat(resultData[i])); } return ACL_SUCCESS; } int main() { // 1.固定写法device/stream初始化参考acl API手册根据自己的实际device填写deviceId int32_t deviceId 0; aclrtStream stream; auto ret aclnnQuantMatmulWeightNzTest(deviceId, stream); CHECK_FREE_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnQuantMatmulWeightNzTest failed. ERROR: %d\n, ret); return ret); Finalize(deviceId, stream); return 0; }代码要点解读x2 的 FRACTAL_NZ 格式是本次调用的关键点x2 在 aclnn 接口层要求以 5 维 NZ 排布k016、n032 分形块传入。样例通过aclnnCalculateMatmulWeightSizeV2计算 NZ 存储大小并据此申请内存再以ACL_FORMAT_FRACTAL_NZ创建 aclTensor实际业务中若输入为 ND 格式可先用aclnnTransMatmulWeight完成 ND → NZ 转换。BF16 位操作C 无原生 BF16 类型样例用uint16_t保存 BF16 位表示1.0 为0x3F800.0 为0x0000读取结果时通过Bfloat16ToFloat左移 16 位补零转为 FP32 后打印。两段式固定流程第一段接口校验参数并构造执行器 → 按workspaceSize用aclrtMalloc申请 workspace → 第二段接口在指定 stream 上执行 →aclrtSynchronizeStream同步等待完成。源码级实现解析算子注册与属性定义op_defop_host/quant_matmul_reduce_sum_def.cpp 通过OP_ADD(QuantMatmulReduceSum)注册算子原型。除上文列出的输入输出外该文件还设置了丰富的编译配置DynamicCompileStaticFlag(true)动态编译静态化DynamicFormatFlag(true)支持动态格式DynamicRankSupportFlag(true)/DynamicShapeSupportFlag(true)支持动态 rank 与动态 shapePrecisionReduceFlag(false)不做精度降低通过ExtendCfgInfo(aclnnSupport.value, support_aclnn)声明算子支持 aclnn 调用。InferShape 推导逻辑op_host/quant_matmul_reduce_sum_infershape.cpp 实现了 shape 与 dtype 推导x1、x2 必须均为 3 维dimX1 ! 3 || dimX2 ! 3报错x1 与 x2 的 batch第 0 维必须相等、kx1 第 2 维与 x2 第 1 维必须相等输出恒为 2 维(mX1, nX2)shapeOut-SetDimNum(2)第 0 维取 x1 的 m第 1 维取 x2 的 n与「在 batch 维 ReduceSum 且 keepDimsfalse」的语义完全一致输出 dtype 固定为DT_BF16。计算图构建l0 封装op_host/op_api/aclnn_quant_matmul_reduce_sum.cpp 是 aclnn 两段式接口的直接实现。第一段接口内部依次完成CheckInputs()全量参数校验空指针、预留参数、属性默认值、dtype、format、shape详见上文错误码章节PreProcess()将各输入 Tensor 的 original shape 记录为 view shapeProcessL0()组装计算图先对 x1、x1Scale、x2Scale 执行l0op::Contiguous保证连续性对应约束中「不支持非连续 Tensor」的实现再调用l0op::QuantMatmulReduceSum见 op_host/op_api/quant_matmul_reduce_sum.cpp该 l0 接口通过INFER_SHAPE与ADD_TO_LAUNCHER_LIST_AICORE将算子挂入 AICore 执行队列最后若输出 out 为非连续 Tensor通过l0op::ViewCopy将计算得到的连续结果拷贝回 out 视图*workspaceSize uniqueExecutor-GetWorkspaceSize()返回所需 workspace 大小。第二段接口则直接调用CommonOpExecutorRun(workspace, workspaceSize, executor, stream)完成在指定 stream 上的异步执行。Tiling 与 Kernel 实现AICore 侧从 Tiling 源码 op_host/op_tiling/quant_matmul_reduce_sum_tiling.cpp 可以看到该算子的底层计算特征计算核心是 INT8 输入、INT32 累加的矩阵乘inputParams_.cDtype ge::DT_INT32x1Scale 必须是 FLOAT、x2Scale 必须是 BF16、输出 y 必须是 BF16编译信息采集包含 UB/L1/L2/L0_A/L0_B/L0_C 各级缓存容量与 AIC 核数compileInfo_.aicNum其中对 L2 容量做了特殊纠正当系统上报的 L2 为 96MB 时按 168MB 实际物理大小计算L2_REAL_SIZE 168、L2_FAKE_SIZE 96以保证大 shape 场景下的 Tiling 切分正确Kernel 侧 op_kernel/quant_matmul_reduce_sum.cpp 使用MixCoreAICAIV 混合核架构实现KERNEL_TASK_TYPE(0, KERNEL_TYPE_MIX_AIC_1_2)与KERNEL_TASK_TYPE(1, KERNEL_TYPE_MIX_AIC_1_1)定义了 AIC 与 AIV 的比例模式。执行流程分为两步先用QuantMatmulReduceSumInitOutput对输出 y 做清零因为累加结果要写入输出再通过QuantMatmulReduceSumQuantMixCoreCompute执行真正的量化矩阵乘与跨 batch 累加矩阵乘使用MatmulTypeGM, CubeFormat::ND, int8_tx1与MatmulTypeGM, CubeFormat::NZ, int8_tx2模板与 IR 层 x1ND、x2FRACTAL_NZ 的格式约束一一对应。由此可见QuantMatmulReduceSum 的「分组矩阵乘 ReduceSum」融合并非在 Host 侧拆解为多个算子而是在 AICore Kernel 内部一次性完成AIC 负责矩阵乘计算AIV 负责缩放与跨 batch 累加归约中间结果驻留在 on-chip 缓存与 workspace 中避免了多算子级联带来的额外显存读写。单元测试仓库为该算子提供了完整的 Host 侧与 Kernel 侧单测tests/ut/op_host/test_aclnn_quant_matmul_reduce_sum_api.cpp验证 aclnn 两段式接口的参数校验与基本调用tests/ut/op_host/test_quant_matmul_reduce_sum_infershape.cpp验证 InferShape 推导规则tests/ut/op_host/test_quant_matmul_reduce_sum_tiling.cpp验证 Tiling 参数计算tests/ut/op_kernel/test_quant_matmul_reduce_sum.cppKernel 计算正确性测试Tiling 数据结构定义见 tests/ut/op_kernel/quant_matmul_reduce_sum_tiling_def.h。其中small_case样例examples/test_aclnn_quant_matmul_reduce_sum_small_case.cpp专门覆盖了输出小于 32 字节的极小 shape 边界输出 32B 时 Kernel 走DataCopyPad路径输出 32B 时为对齐边界输出 32B 但存在 tail 时走 tail 处理路径这些边界场景是编写自测用例时值得参考的用例集。使用建议与注意事项平台确认调用前先确认设备属于 Atlas A2/A3 训练或推理系列Ascend 950、Atlas 200I/500 A2 及其他老平台不支持本算子。x2 格式预处理x2 需要 FRACTAL_NZ 5 维格式业务侧 ND 权重务必先用aclnnCalculateMatmulWeightSizeV2计算 NZ 存储大小、aclnnTransMatmulWeight完成格式转换示例源码中已给出可直接复用的CreateAclTensorX2模板函数。预留参数必须传空yScale/x1Offset/x2Offset/yOffset/bias传 nullptrgroupSize传 0transposeX1/transposeX2/keepDims传 falsedims传[0]——传错任一均会被第一段接口以ACLNN_ERR_PARAM_INVALID161002拦截。输出累加语义算子内部先对输出清零再累加输出为跨 batch 归约后的 (M, N) 单矩阵若上层需要保留逐组结果请拆分多次调用或使用其他 Matmul 类算子。确定性开关算子默认非确定性实现对精度可复现性有要求的场景可通过aclrtCtxSetSysParamOpt开启确定性。通过本文的公式语义、接口参数与源码实现三重视角你可以在 Atlas A2/A3 平台上快速落地「INT8 量化分组矩阵乘 跨 batch 求和」的融合计算并能够独立完成样例代码的改编、参数校验问题的排查与 shape 边界用例的构造。赞分享人工智能算子库深度学习CANNAscend【免费下载链接】ops-nn本项目是CANN提供的神经网络类计算算子库实现网络在NPU上加速计算。项目地址https://gitcode.com/cann/ops-nn点击查看免费下载相关推荐CANN ops-transformer GroupedMatmulAdd 算子全解析分组矩阵乘与原地累加的 K 轴分组实现与 aclnn 调用指南CANN ops transformer GroupedMatmulAdd 算子全解析分组矩阵乘与原地累加的 K 轴分组实现与 aclnn 调用指南 导读 本算子库人工智能深度学习AscendCANN ops-transformer 算子详解aclnnQuantGroupedMatmulInplaceAdd 量化分组矩阵乘原位累加融合算子CANN ops transformer 算子详解aclnnQuantGroupedMatmulInplaceAdd 量化分组矩阵乘原位累加融合算子 本技术指算子库人工智能深度学习AscendCANN ops-transformer 分组矩阵乘累加算子 aclnnGroupedMatmulAdd 接口详解与实战指南CANN ops transformer 分组矩阵乘累加算子 aclnnGroupedMatmulAdd 接口详解与实战指南 aclnnGroupedMatmu算子库人工智能深度学习Ascend上一篇Buzz语音转录终极指南3步打造你的本地隐私保护工作站下一篇FinRobot 金融 AI 代理平台完整入门指南五步跑通你的第一份股票研究报告创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

Kornia 相机几何空批次支持:PinholeCamera、StereoCamera 与 warp_frame_depth 的空批次(Empty Batch)处理机制

Kornia 相机几何空批次支持:PinholeCamera、StereoCamera 与 warp_frame_depth 的空批次(Empty Batch)处理机制

计算机视觉人工智能深度学习图像处理 【免费下载链接】kornia 🐍 Geometric Computer Vision Library for Spatial AI 项目地址: https://gitcode.com/gh_mirrors/ko/kornia 点击查看 免费下载 本篇文章聚焦 Kornia(Geometric Computer Visi…

2026/9/23 21:36:30 阅读更多 →
OBS Studio 32.1.0中文绿色版:专业直播录屏解决方案

OBS Studio 32.1.0中文绿色版:专业直播录屏解决方案

1. 项目概述:OBS Studio 32.1.0中文绿色版的核心价值作为一名从2016年就开始使用OBS的内容创作者,我见证了这款开源软件从简陋的直播工具成长为行业标杆的全过程。这次要介绍的32.1.0中文绿色版,可以说是目前最适合中文用户"开箱即用&qu…

2026/9/23 21:35:30 阅读更多 →
Convex Backend OCC 冲突调优指南:从检测症状到落地五种修复策略

Convex Backend OCC 冲突调优指南:从检测症状到落地五种修复策略

数据库后端 【免费下载链接】convex-backend The open-source reactive database for app developers 项目地址: https://gitcode.com/gh_mirrors/co/convex-backend 点击查看 免费下载 导读:本文基于 convex-backend 仓库中的性能审计技能文档&#xf…

2026/9/23 21:35:30 阅读更多 →

最新新闻

Moto CodeBuild 模拟实战:在测试中 Mock AWS CodeBuild 项目与构建 API

Moto CodeBuild 模拟实战:在测试中 Mock AWS CodeBuild 项目与构建 API

Mock测试 【免费下载链接】moto A library that allows you to easily mock out tests based on AWS infrastructure. 项目地址: https://gitcode.com/gh_mirrors/mo/moto 点击查看 免费下载 本篇技术指南围绕 moto 仓库中 CodeBuild 服务文档 展开,系统…

2026/9/25 3:31:50 阅读更多 →
并行加法器 vs 先行进位加法器:进位延迟、关键路径与工程实现

并行加法器 vs 先行进位加法器:进位延迟、关键路径与工程实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/25 3:31:50 阅读更多 →
grammars-v4 中 R 语言 ANTLR 语法解析指南:掌握 RFilter 换行符预处理机制

grammars-v4 中 R 语言 ANTLR 语法解析指南:掌握 RFilter 换行符预处理机制

编程语言编译器开发工具 【免费下载链接】grammars-v4 Grammars written for ANTLR v4; expectation that the grammars are free of actions. 项目地址: https://gitcode.com/gh_mirrors/gr/grammars-v4 点击查看 免费下载 导读 在 grammars-v4 仓库的 r 目录下&…

2026/9/25 3:31:50 阅读更多 →
VoltAgent 接入 Deep Infra:使用 `deepinfra/<model>` 模型路由打通低成本高性能推理

VoltAgent 接入 Deep Infra:使用 `deepinfra/<model>` 模型路由打通低成本高性能推理

人工智能AI AgentAgent 框架后端多智能体RAG工具调用Agent 记忆 【免费下载链接】voltagent AI Agent Engineering Platform built on an Open Source TypeScript AI Agent Framework 项目地址: https://gitcode.com/gh_mirrors/vo/voltagent 点击查看 免费下载 De…

2026/9/25 3:31:50 阅读更多 →
用 ANTLR v4 解析 Scala 3:grammars-v4 中 Scala3 语法的设计、覆盖率与已知限制

用 ANTLR v4 解析 Scala 3:grammars-v4 中 Scala3 语法的设计、覆盖率与已知限制

编程语言编译器开发工具 【免费下载链接】grammars-v4 Grammars written for ANTLR v4; expectation that the grammars are free of actions. 项目地址: https://gitcode.com/gh_mirrors/gr/grammars-v4 点击查看 免费下载 本文面向需要为 Scala 3 构建词法/语法分…

2026/9/25 3:31:50 阅读更多 →
Java工业物联网IOT驱动包:统一Modbus-TCP、Bacnet与OPC-UA协议接入

Java工业物联网IOT驱动包:统一Modbus-TCP、Bacnet与OPC-UA协议接入

简介:这份基于Java的物联网IOT通用驱动包设计源码,面向中高级Java开发者与系统集成商,解决Modbus-TCP、Bacnet、OPC-UA等多协议设备接入问题,封装为SDK形式,可直接嵌入业务系统。压缩包共76个文件,约1.73MB…

2026/9/25 3:30:49 阅读更多 →

日新闻

AI元人文:从工具使用到思维重构的深度探索

AI元人文:从工具使用到思维重构的深度探索

最近半年我一直在琢磨一件事:AI元人文到底是什么?说白了,就是“用元视角重新审视人与AI的关系”,也在“探索AI如何反向逼着我们发现自己的思考边界”。标题里的“元探索”,在我看就是一层套一层的追问——当你用AI解决…

2026/9/25 0:00:41 阅读更多 →
Python+CNN车牌识别实战:从数据预处理到模型训练与部署

Python+CNN车牌识别实战:从数据预处理到模型训练与部署

简介:基于Python与卷积神经网络的车牌识别项目,面向计算机视觉初学者及智能交通开发者,目标是帮助用户掌握从数据预处理、模型构建到实际部署的完整流程。压缩包共25个文件,包含jpg/png图像样本、py训练脚本、md说明文档、dat数据…

2026/9/25 0:00:41 阅读更多 →
Vim基础操作全攻略:保存退出、模式切换与高频命令实战

Vim基础操作全攻略:保存退出、模式切换与高频命令实战

1. 项目概述1.1 核心需求解析今天聊聊Vim。写这个题目的原因是:几乎每个后端开发者、运维人员、数据工程师某天都会遇到一个场景——深夜加班,服务器登录界面只有黑底白字,编辑器只有vi/vim,你必须在五分钟内完成一次配置修改并保…

2026/9/25 0:00:41 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/24 14:34:13 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/24 9:10:42 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/24 14:33:56 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/24 12:50:34 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/24 14:33:48 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/24 12:49:17 阅读更多 →