资讯详情

资讯详情

CANN ops-nn 稀疏4:2量化矩阵乘算子 aclnnSparse4to2QuantMatmulWeightNz 使用指南:INT8 稀疏量化 GEMM 的 NPU 两段式调用全解析

人工智能算子库深度学习CANNAscend【免费下载链接】ops-nn本项目是CANN提供的神经网络类计算算子库实现网络在NPU上加速计算。项目地址https://gitcode.com/cann/ops-nn点击查看免费下载本文以 CANN ops-nn 仓库中的 aclnnSparse4to2QuantMatmulWeightNz 算子文档 为骨架结合仓库内 op_api、op_host、op_kernel 等源码实现系统讲解稀疏 4:24 选 2量化矩阵乘算子的功能语义、两段式接口用法、参数约束、配套压缩预处理接口以及完整可运行的调用示例。读完本文你将能够在 Atlas A2/A3 训练与推理系列产品上独立完成从权重稀疏化压缩到aclnnSparse4to2QuantMatmulWeightNz双段接口调用的全流程开发。功能说明与计算公式aclnnSparse4to2QuantMatmulWeightNz是 CANN ops-nn 中Sparse4to2QuantMatmul算子的 aclnn 接口封装核心功能是完成稀疏 4:2 量化的矩阵乘计算。所谓稀疏 4:2指的是权重矩阵按每 4 个元素一组进行结构化稀疏每组中至少保留 2 个零元素压缩后每组仅保留 2 个非零元素及其位置索引从而在不损失精度的前提下显著降低权重存储与计算量。算子计算公式如下$$ out xsparseWeight \times sparseWeightScale \times xScale bias $$其中x为 INT8 激活矩阵左矩阵shape 为(m, k)sparseWeight为经过压缩处理的稀疏右矩阵shape 为(n, k_half)sparseWeightScale、xScale为 FLOAT32 量化缩放因子分别按 n、m 维度逐行缩放bias为可选的 BFLOAT16 偏置shape 为(n,)out为 BFLOAT16 输出shape 为(m, n)。从公式可以看出该算子将反量化 矩阵乘 量化融合为一次 NPU 计算先在 INT8 域完成稀疏矩阵乘xsparseWeight再与两个缩放因子相乘最后累加偏置并输出 BFLOAT16 结果。产品支持情况根据算子文档与 sparse4to2quant_matmul_def.cpp 中的 AICore 配置注册ascend910b、ascend910_93本算子支持以下产品形态产品是否支持Atlas A3 训练系列产品 / Atlas A3 推理系列产品支持Atlas A2 训练系列产品 / Atlas A2 推理系列产品支持源码中 aclnn_sparse4to2quant_matmul_weight_nz.cpp 的第一段接口入口同样做了平台校验仅允许ASCEND910_93与ASCEND910B两个 SoC 版本执行其他平台直接返回ACLNN_ERR_PARAM_INVALID。前置步骤权重压缩接口 aclnnTransSparse4to2ParaaclnnSparse4to2QuantMatmulWeightNz的输入sparseWeight与index并非原始权重而是必须经过配套的压缩预处理接口 aclnnTransSparse4to2Para 处理后得到的压缩矩阵与索引矩阵。该接口的完整文档位于 aclnnTransSparse4to2Para.md。其功能为对结构化稀疏的权重矩阵每 4 个元素中至少有 2 个零进行压缩预处理输出压缩后的稀疏矩阵以及对应的索引矩阵。压缩过程中原始矩阵中的每 4 个元素会在 index 矩阵中生成 2 个 2 位索引并按照固定规则编码。函数原型如下aclnnStatus aclnnTransSparse4to2Para( const int8_t* weight, aclIntArray* shape, int8_t** sparseWeight, int64_t** sparseWeightDims, uint64_t* sparseWeightDimsNum, uint8_t** index, int64_t** indexDims, uint64_t* indexDimsNum)参数要点详见原文档参数表参数输入/输出说明weight输入未压缩的稀疏右矩阵shape(n, k)INT8ND 格式须满足每 4 个元素至少 2 个零shape输入权重 shape通过aclCreateIntArray接口创建sparseWeight输出压缩后的右矩阵内存由调用者释放sparseWeightDims输出压缩后右矩阵 StorageShape 数组指针首地址shape 为(ceil(k_half/32), ceil(n/16), 16, 32)其中k_half ceil(k/8) * 8 / 2sparseWeightDimsNum输出StorageShape 数组维度index输出压缩后的索引矩阵内存由调用者释放indexDims输出索引矩阵 StorageShape 数组指针首地址shape 为(ceil(k_half/32), ceil(n/16), 16, 8)indexDimsNum输出索引矩阵 StorageShape 数组维度该接口校验失败时返回ACLNN_ERR_PARAM_NULLPTR161001weight 为空指针或ACLNN_ERR_PARAM_INVALID161002shape/format/dtype 不满足要求或数据不满足每 4 个元素至少 2 个零。两段式接口与函数原型与其他 aclnn 算子一致aclnnSparse4to2QuantMatmulWeightNz采用两段式接口设计详见 docs/zh/context/two_phase_api.md必须先调用第一段接口aclnnSparse4to2QuantMatmulWeightNzGetWorkspaceSize完成入参校验、构图并计算出所需 workspace 大小再调用第二段接口aclnnSparse4to2QuantMatmulWeightNz在指定 stream 上执行计算。第一段接口原型aclnnStatus aclnnSparse4to2QuantMatmulWeightNzGetWorkspaceSize( const aclTensor *x, const aclTensor *sparseWeight, const aclTensor *index, const aclTensor *xScale, const aclTensor *sparseWeightScale, const aclTensor *biasOptional, aclTensor *out, uint64_t *workspaceSize, aclOpExecutor **executor)第二段接口原型aclnnStatus aclnnSparse4to2QuantMatmulWeightNz( void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, aclrtStream stream)两个接口的声明位于 aclnn_sparse4to2quant_matmul_weight_nz.h头文件为aclnnop/aclnn_sparse4to2quant_matmul_weight_nz.h。第一段接口GetWorkspaceSize 参数详解以下参数表完整继承自算子文档并补充了源码校验逻辑作为佐证参数名输入/输出描述使用说明数据类型数据格式维度(shape)非连续Tensorx输入矩阵乘运算中的左矩阵仅支持 2 维输入shape 可表达为(m, k)INT8ND2×sparseWeight输入矩阵乘运算中的右矩阵经过aclnnTransSparse4to2Para接口处理后的稀疏矩阵仅支持 2 维输入ViewShape 可表达为(n, k_half)其中k_half ceil(k/8) * 8 / 2StorageShape 从aclnnTransSparse4to2Para出参sparseWeightDims和sparseWeightDimsNum获取为 4 维与 ViewShape 的关系为(ceil(k_half/32), ceil(n/16), 16, 32)INT8FRACTAL_NZ2×index输入经过aclnnTransSparse4to2Para接口压缩后计算得到的索引矩阵4 维输入ViewShape 可表达为(ceil(k_half/32), ceil(n/16), 16, 8)StorageShape 从aclnnTransSparse4to2Para出参indexDims和indexDimsNum获取为 4 维与 ViewShape 相同UINT8ND4×xScale输入量化参数缩放因子shape 可表达为(m,)FLOAT32ND1×sparseWeightScale输入量化参数缩放因子shape 可表达为(n,)FLOAT32ND1×biasOptional输入可选偏置shape 可表达为(n,)BFLOAT16ND1×out输出输出的张量公式中的 outshape 可表达为(m, n)BFLOAT16ND2×workspaceSize输出返回需要在 Device 侧申请的 workspace 大小-----executor输出返回 op 执行器包含了算子计算流程-----关于 ViewShape 与 StorageShapesparseWeight在逻辑视图ViewShape上是 2 维的(n, k_half)但在物理存储StorageShape上是 4 维的 FRACTAL_NZ 分形格式(ceil(k_half/32), ceil(n/16), 16, 32)index的 ViewShape 与 StorageShape 均为 4 维。创建这类张量时需要使用aclCreateTensor同时传入 view shape 与 storage shape见后文示例中的CreateSparseTensor辅助函数。源码中 GetWeightNzShape 会按上述规则计算期望的 NZ 存储 shape并与传入的sparseWeightStorageShape 严格比对不一致时返回ACLNN_ERR_PARAM_INVALID。返回值与错误码两段接口均返回aclnnStatus状态码具体参见 docs/zh/context/aclnn_return_code.md。第一段接口完成入参校验出现以下场景时报错返回码错误码描述ACLNN_ERR_PARAM_NULLPTR161001传入的必选输入 x、sparseWeight、index 或 out 是空指针ACLNN_ERR_PARAM_INVALID161002输入输出的数据类型、shape、format、dtype 不在支持的范围之内源码中 CheckParams 完整执行了四步校验与文档错误码一一对应CheckDtypeValidx、sparseWeight 必须为 INT8index 必须为 UINT8bias 必须为 BF16xScale、sparseWeightScale 必须为 FLOAT32out 必须为 BF16CheckFormatValidx/index/xScale/sparseWeightScale/bias/out 必须为 NDsparseWeight 主格式必须为 FRACTAL_NZCheckShapek 维需满足 x 的 k 维 8 对齐后等于 sparseWeight k 维的两倍CeilAlign(xK, 8) 2 * weightKbias 与 sparseWeightScale 的第 0 维等于 nxScale 第 0 维等于 mout shape 必须为(m, n)同时校验 k 不超过 65535LAST_AXIS_LIMITCheckEmptyTensor不支持空 tensor。第二段接口执行计算参数详解参数名输入/输出描述workspace输入在 Device 侧申请的 workspace 内存地址workspaceSize输入在 Device 侧申请的 workspace 大小由第一段接口aclnnSparse4to2QuantMatmulWeightNzGetWorkspaceSize获取executor输入op 执行器包含了算子计算流程stream输入指定执行任务的 Stream第二段接口的源码实现非常简洁直接调用通用执行器完成计算aclnnStatus aclnnSparse4to2QuantMatmulWeightNz(void* workspace, uint64_t workspaceSize, aclOpExecutor* executor, aclrtStream stream) { L2_DFX_PHASE_2(aclnnSparse4to2QuantMatmulWeightNz); return CommonOpExecutorRun(workspace, workspaceSize, executor, stream); }约束说明确定性说明Atlas A3 训练系列/推理系列产品、Atlas A2 训练系列/推理系列产品上aclnnSparse4to2QuantMatmulWeightNz为默认确定性实现即相同输入产生确定输出。k 维上限x 的最后一维即 shape 描述中 k 的值不能超过 65535。该限制在源码 MaxDimCheck 中通过LAST_AXIS_LIMIT常量强制校验。缩放因子必选当前只支持sparseWeightScale、xScale均不为 nullptr 的场景。这也是 tiling 阶段 AnalyzeDtype 中的硬性要求——二者为空时直接报错。不支持空 tensor、不支持非连续 tensor参见 README.md 约束说明。完整调用示例示例代码取自算子文档仓库内另有可直接参考的工程样例 test_aclnn_sparse4to2quant_matmul.cpp代码与文档示例基本一致均为完整可编译的 280 行程序。具体编译与运行方法请参考 docs/zh/context/compile_and_run_sample.md。#include iostream #include memory #include vector #include stdlib.h #include acl/acl.h #include aclnnop/aclnn_sparse4to2quant_matmul_weight_nz.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) #define CREATE_TENSOR(hostData, shape, deviceAddr, dtype, tensor) \ ret CreateAclTensor(hostData, shape, deviceAddr, dtype, tensor); \ std::unique_ptraclTensor, aclnnStatus (*)(const aclTensor*) tensor##Ptr(tensor, aclDestroyTensor); \ std::unique_ptrvoid, aclError (*)(void*) deviceAddr##Ptr(deviceAddr, aclrtFree); \ CHECK_RET(ret ACL_SUCCESS, return ret) #define CREATE_SPARSE_TENSOR(hostData, weightShape, storageShape, deviceAddr, dataType, tensor) \ ret CreateSparseTensor(hostData, weightShape, storageShape, deviceAddr, dataType, tensor); \ std::unique_ptraclTensor, aclnnStatus (*)(const aclTensor*) tensor##Ptr(tensor, aclDestroyTensor); \ std::unique_ptrvoid, aclError (*)(void*) deviceAddr##Ptr(deviceAddr, aclrtFree); \ CHECK_RET(ret ACL_SUCCESS, return ret) int64_t GetShapeSize(const std::vectorint64_t shape) { int64_t shapeSize 1; for (auto i : shape) { shapeSize * i; } return shapeSize; } 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 CreateSparseTensor( const T* sparseWeightData, const std::vectorint64_t viewShape, const std::vectorint64_t storageShape, void** deviceAddr, aclDataType dataType, aclTensor** tensor) { auto size static_castuint64_t(GetShapeSize(storageShape)) * 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, sparseWeightData, 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(viewShape.size(), 1); for (int64_t i viewShape.size() - 2; i 0; i--) { strides[i] viewShape[i 1] * strides[i 1]; } // 调用aclCreateTensor接口创建aclTensor同时传入view shape与storage shape *tensor aclCreateTensor( viewShape.data(), viewShape.size(), dataType, strides.data(), 0, aclFormat::ACL_FORMAT_ND, storageShape.data(), storageShape.size(), *deviceAddr); 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侧内存上 if (hostData.size() 0) { 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(); } void GenRandomMask(std::vectorsize_t masks) { masks[0] random() % 4; masks[1] random() % 4; while (masks[1] masks[0]) { masks[1] random() % 4; } } void GenRandomSparseData(std::vectorint8_t weightHostData) { srandom(233U); std::vectorsize_t masks(2, 0UL); constexpr size_t step 4UL; for (size_t i 0; i weightHostData.size(); i step) { GenRandomMask(masks); for (auto mask : masks) { weightHostData[i mask] 0; } } } std::vectorint64_t GenStorageShape(int64_t* dims, uint64_t dimsNum) { std::vectorint64_t storageShape; for (uint64_t i 0UL; i dimsNum; i) { storageShape.push_back(dims[i]); } return storageShape; } int aclnnSparse4to2QuantMatmulWeightNzTest(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. 构造输入与输出需要根据API的接口自定义构造 int64_t m 64L; int64_t k 512L; int64_t n 128L; std::vectorint64_t xShape {m, k}; std::vectorint64_t weightShape {n, k}; std::vectorint64_t indexShape {n, (k 7) / 8}; std::vectorint64_t biasShape {n}; std::vectorint64_t xScaleShape {m}; std::vectorint64_t weightScaleShape {n}; std::vectorint64_t outShape {m, n}; void* xDeviceAddr nullptr; void* sparseWeightDeviceAddr nullptr; void* indexDeviceAddr nullptr; void* biasDeviceAddr nullptr; void* xScaleDeviceAddr nullptr; void* weightScaleDeviceAddr nullptr; void* outDeviceAddr nullptr; aclTensor* x nullptr; aclTensor* sparseWeight nullptr; aclTensor* index nullptr; aclTensor* bias nullptr; aclTensor* xScale nullptr; aclTensor* weightScale nullptr; aclTensor* out nullptr; std::vectorint8_t xHostData(GetShapeSize(xShape), 1); std::vectorint8_t weightHostData(GetShapeSize(weightShape), 1); std::vectoruint16_t biasHostData(GetShapeSize(biasShape), 1); // 实际上是bfloat16半精度方式 std::vectorfloat xScaleHostData(GetShapeSize(xScaleShape), 1); std::vectorfloat weightScaleHostData(GetShapeSize(weightScaleShape), 1); GenRandomSparseData(weightHostData); int8_t* sparseWeightHostData nullptr; uint8_t* indexHostData nullptr; int64_t* sparseWeightDims nullptr; uint64_t sparseWeightDimsNum 0UL; int64_t* indexDims nullptr; uint64_t indexDimsNum 0UL; aclIntArray* weightShapeArray aclCreateIntArray(weightShape.data(), weightShape.size()); ret aclnnTransSparse4to2Para( weightHostData.data(), weightShapeArray, sparseWeightHostData, sparseWeightDims, sparseWeightDimsNum, indexHostData, indexDims, indexDimsNum); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnTransSparse4to2Para failed. ERROR: %d\n, ret); return ret); std::unique_ptrint8_t[] sparseWeightHostDataPtr(sparseWeightHostData); std::unique_ptruint8_t[] indexHostDataPtr(indexHostData); std::unique_ptrint64_t[] sparseWeightDimsPtr(sparseWeightDims); std::unique_ptrint64_t[] indexDimsPtr(indexDims); CREATE_TENSOR(xHostData, xShape, xDeviceAddr, aclDataType::ACL_INT8, x); weightShape.back() (weightShape.back() 7) / 8 * 8 / 2; // 4选2后K轴向上8对齐后减半 auto sparseWeightStorageShape GenStorageShape(sparseWeightDims, sparseWeightDimsNum); CREATE_SPARSE_TENSOR( sparseWeightHostData, weightShape, sparseWeightStorageShape, sparseWeightDeviceAddr, aclDataType::ACL_INT8, sparseWeight); auto indexStorageShape GenStorageShape(indexDims, indexDimsNum); CREATE_SPARSE_TENSOR(indexHostData, indexShape, indexStorageShape, indexDeviceAddr, aclDataType::ACL_UINT8, index); CREATE_TENSOR(biasHostData, biasShape, biasDeviceAddr, aclDataType::ACL_BF16, bias); CREATE_TENSOR(xScaleHostData, xScaleShape, xScaleDeviceAddr, aclDataType::ACL_FLOAT, xScale); CREATE_TENSOR(weightScaleHostData, weightScaleShape, weightScaleDeviceAddr, aclDataType::ACL_FLOAT, weightScale); CREATE_TENSOR(std::vectoruint16_t(), outShape, outDeviceAddr, aclDataType::ACL_BF16, out); uint64_t workspaceSize 0; aclOpExecutor* executor; void* workspaceAddr nullptr; // 调用aclnnSparse4to2QuantMatmulWeightNz第一段接口 ret aclnnSparse4to2QuantMatmulWeightNzGetWorkspaceSize( x, sparseWeight, index, xScale, weightScale, bias, out, workspaceSize, executor); CHECK_RET( ret ACL_SUCCESS, LOG_PRINT(aclnnSparse4to2QuantMatmulWeightNzGetWorkspaceSize failed. ERROR: %d\n, ret); return ret); // 根据第一段接口计算出的workspaceSize申请device内存 std::unique_ptrvoid, aclError (*)(void*) workspaceAddrPtrTrans(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); workspaceAddrPtrTrans.reset(workspaceAddr); } // 调用aclnnSparse4to2QuantMatmulWeightNz第二段接口 ret aclnnSparse4to2QuantMatmulWeightNz(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnSparse4to2QuantMatmulWeightNz 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侧需要根据具体API的接口定义修改 auto size GetShapeSize(outShape); // C语言中无法直接打印bf16的数据需要用uint16读出来自行通过二进制转成bf16 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 size; i) { LOG_PRINT(result[%ld] is: %u\n, i, resultData[i]); } return ACL_SUCCESS; } int main() { // 1.固定写法device/stream初始化参考acl API手册 // 根据自己的实际device填写deviceId int32_t deviceId 0; aclrtStream stream; auto ret aclnnSparse4to2QuantMatmulWeightNzTest(deviceId, stream); CHECK_FREE_RET( ret ACL_SUCCESS, LOG_PRINT(aclnnSparse4to2QuantMatmulWeightNzTest failed. ERROR: %d\n, ret); return ret); Finalize(deviceId, stream); return 0; }示例关键步骤解读资源初始化aclInit→aclrtSetDevice→aclrtCreateStream固定写法构造输入输出示例取m64, k512, n128。权重初始化为全 1 后通过GenRandomSparseData在每个 4 元素组中随机置零 2 个元素构造出满足每 4 个元素至少 2 个零的结构化稀疏数据调用aclnnTransSparse4to2Para压缩权重输入原始weightHostData与 shape 数组输出sparseWeightHostData、indexHostData及其各自 dims。注意压缩接口的输出内存由调用者释放示例用std::unique_ptr托管创建稀疏张量weightShape.back()修改为(k 7) / 8 * 8 / 2即k_half4 选 2 后 K 轴向上 8 对齐再减半并配合压缩接口返回的 StorageShape通过CreateSparseTensor创建带 view/storage 双 shape 的aclTensor两段式调用先GetWorkspaceSize获取workspaceSize与executor再按需aclrtMalloc申请 workspace最后执行第二段接口结果回读aclrtSynchronizeStream同步后将 out 从 Device 拷贝至 Host。由于 BF16 无法直接打印示例用uint16_t读出原始二进制自行按需转换。源码级实现原理算子定义与形状推导Sparse4to2QuantMatmul 的 OpDef 声明了 6 个输入与 1 个输出必选输入x(INT8/ND)、sparse_weight(INT8/FRACTAL_NZ)、index(UINT8/ND)可选输入x_scale(FLOAT32/ND)、sparse_weight_scale(FLOAT32/ND)、bias(BF16/ND)输出y(BF16/ND)属性dtypeAICore 配置仅注册ascend910b与ascend910_93两个平台并开启动态 shape、动态 rank、动态 format 支持。形状推导 sparse4to2quant_matmul_infershape.cpp 的逻辑与 aclnn 层校验一致x 与 sparseWeight 均须为 2 维x 的 k 维 8 对齐后必须等于 sparseWeight k 维的两倍输出 shape 为(m, n)。Tiling 策略sparse4to2quant_matmul_tiling.cpp 负责将 M/N/K 三维切分为 NPU 多核可执行的基本块数据类型解析AnalyzeDtype确认 x/sparseWeight 为 INT8、scale 为 FLOAT、bias 为 BF16、输出为 BF16输入解析AnalyzeInputs从原始 shape 提取mSize/kaSize/kbSize/nSize并校验 x 的 k8 对齐后为 sparseWeight k 的两倍基本块切分CalcL0Tiling/CalcL1TilingbaseM/baseN/baseK 候选集、L0A/L0B/L0C 容量约束、L1 depthA1/depthB1 流水深度与 index 空间联合裁剪L2 缓存切分DoL2CacheTiling与多核计算顺序ROW_FIRST/COL_FIRST决策workspace 计算GetWorkspaceSize16MB系统 workspace 加上baseM * baseN * usedCoreNum * 2 * sizeof(int32)的用量计算。值得注意的是 tiling 中针对稀疏特性做了专门优化稀疏权重访问数据量系数SPARSE_WEIGHT_RATIO 0.625即 5/8以及 index 空间按depthBSize / 4估算体现了压缩后少读一半权重的稀疏加速语义。Kernel 入口sparse4to2quant_matmul.cpp 中 kernel 入口sparse4to2quant_matmul接收x / sparseWeight / index / xScale / sparseWeightScale / bias / y / workSpace / tiling九个 GM 地址参数注册 tiling 数据后实例化Sparse4to2QuantMatmulint8_t, int8_t, DTYPE_Y, format_x1, format_x2并执行InitProcess。从类型模板可以看出A 矩阵x以 ND/INT8 进入B 矩阵sparseWeight以 FRACTAL_NZ/INT8 进入与 aclnn 层与算子定义中的格式约束完全一致。二进制配置算子二进制映射配置见 sparse4to2quant_matmul_binary.jsonascend910_93目录下存在同构配置声明了Sparse4to2QuantMatmul_ND_NZ_int8_int8_fp32_fp16二进制文件对应的输入输出 dtype/format 组合以及属性dtype的默认值 27对应 BF16 输出数据类型枚举。典型使用链路总结完整使用aclnnSparse4to2QuantMatmulWeightNz的链路如下准备满足每 4 个元素至少 2 个零的 INT8 权重矩阵(n, k)调用 aclnnTransSparse4to2Para 压缩权重获得sparseWeight含 StorageShape与index含 StorageShape构造x、xScale、sparseWeightScale、biasOptional、out等aclTensor注意sparseWeight/index需以 viewstorage 双 shape 方式创建两段式调用aclnnSparse4to2QuantMatmulWeightNzGetWorkspaceSize→ 申请 workspace →aclnnSparse4to2QuantMatmulWeightNz同步 stream 并回读outBF16。对于算子整体能力、参数总览与更多约束如不支持空 tensor、非连续 tensor可进一步参阅 matmul/sparse4to2quant_matmul/README.md仓库内的单元测试覆盖了 aclnn 接口、tiling 与 kernel 三个层面分别位于 tests/ut 目录下如 test_aclnn_sparse4to2quant_matmul.cpp 与对应 CSV 用例可作为理解算子边界行为与自行扩展用例的参考。赞分享人工智能算子库深度学习CANNAscend【免费下载链接】ops-nn本项目是CANN提供的神经网络类计算算子库实现网络在NPU上加速计算。项目地址https://gitcode.com/cann/ops-nn点击查看免费下载相关推荐TileLang 稀疏矩阵乘法T.gemm_sp实践从 2:4 结构化压缩到稀疏 Tensor Core GEMMTileLang 稀疏矩阵乘法T.gemm_sp实践从 2:4 结构化压缩到稀疏 Tensor Core GEMM 本文以 TileLang 官方教程 m编译器编程语言高性能计算人工智能深度学习Faust生产环境部署高可用、容错、扩展性保障Faust生产环境部署高可用、容错、扩展性保障 Faust是一个强大的Python流处理框架基于Kafka构建为实时数据处理提供了高效可靠的解决方案。本文人工智能算子库深度学习CANNAscendCANN ops-nn 伪量化矩阵乘算子 aclnnWeightQuantBatchMatmulV2 使用指南CANN ops nn 伪量化矩阵乘算子 aclnnWeightQuantBatchMatmulV2 使用指南 本文基于 CANN ops nn 开源算子库人工智能算子库深度学习CANNAscend上一篇深度解析todo-api-microservice-example核心组件与RESTful API设计下一篇为什么选择 notepad--一份免费的跨平台文本编辑器完整指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →