资讯动态

CANN ops-nn 仓库 ForeachAsin 算子详解:张量列表逐元素反正弦计算与 aclnnForeachAsin 两段式接口实战

发布时间:2026/9/21 19:11:49 来源:尧图企业网站定制
CANN ops-nn 仓库 ForeachAsin 算子详解张量列表逐元素反正弦计算与 aclnnForeachAsin 两段式接口实战【免费下载链接】ops-nn本项目是CANN提供的神经网络类计算算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-nn本篇技术指南以 foreach/foreach_asin/README.md 为主体深入解析 CANN ops-nn 仓库中 ForeachAsin 算子的功能语义、产品支持矩阵、输入输出约束并结合 aclnnForeachAsin 接口文档、host 侧算子定义、tiling 实现 与 kernel 实现 等源码完整还原从图 IR 构图、aclnn 两段式接口调用、Tiling 切分到 SIMT 向量核执行的全链路。读完本文你将掌握在 NPU 上对一组张量Tensor List批量执行反正弦arcsin运算的完整实战方案包括如何构造aclTensorList、如何按两段式接口申请 workspace 并执行计算以及各产品平台上的支持差异与约束陷阱。一、算子功能与数学定义ForeachAsin 是 CANN ops-nn 神经网络算子库foreach张量列表逐元素批量运算系列中的一员其核心能力是对输入张量列表中的每一个 Tensor按元素计算反正弦函数arcsin即 sin 的反函数。数学定义如下设输入张量列表为$$ x [{x_0}, {x_1}, ... {x_{n-1}}] $$输出张量列表为$$ y [{y_0}, {y_1}, ... {y_{n-1}}] $$则对每个元素有$$ {\rm y}_i \sin^{-1}(x_i) \quad (i0,1,...n-1) $$需要说明的是反正弦函数的定义域为[-1, 1]超出该范围时asin在数学上无实数结果因此该算子的典型应用场景是对归一化后的数据如归一化角度、概率值、嵌入向量余弦相似度等做反向三角变换。与单 Tensor 的Asin算子不同ForeachAsin 一次调用可处理一组 shape 各异的张量适合 PyTorchtorch._foreach_asin这类批量逐张量算子在对齐到 CANN 图模式或 aclnn 接口时的下沉落地。二、产品支持情况依据 foreach/foreach_asin/README.md 中的产品支持矩阵ForeachAsin 算子支持情况如下产品是否支持Ascend 950PR / Ascend 950DT√Atlas A3 训练系列产品 / Atlas A3 推理系列产品√Atlas A2 训练系列产品 / Atlas A2 推理系列产品√Atlas 200I/500 A2 推理产品×Atlas 推理系列产品×Atlas 训练系列产品×Kirin X90 处理器系列产品√Kirin 9030 处理器系列产品√从源码侧可以印证这一支持矩阵host 侧算子定义 foreach_asin_def.cpp 中通过this-AICore().AddConfig(ascend950)、AddConfig(ascend910_93)、AddConfig(ascend910b)分别注册了 Ascend 950、Atlas A3910_93、Atlas A2910b三类平台的 AICore 配置并通过GetKirinCoreConfig()为kirinx90与kirin9030注册了麒麟平台的独立配置而 op_host/config 目录下的二进制配置文件也按平台分别存放ascend950、ascend910_93、ascend910b、kirinx90、kirin9030与文档中不支持的 Atlas 200I/500 A2310b、Atlas 推理310p、Atlas 训练910等平台一一对应。三、参数说明依据 foreach/foreach_asin/README.md 与 aclnnForeachAsin 接口文档算子输入输出参数如下参数名输入/输出/属性描述数据类型数据格式x输入进行反正弦运算的输入张量列表对应公式中的x。列表中所有 Tensor 的数据类型保持一致FLOAT32、FLOAT16、BFLOAT16NDy输出进行反正弦运算的输出张量列表对应公式中的y。数据类型和数据格式与入参x一致shape size 大于等于入参x的 shape size。列表中所有 Tensor 的数据类型保持一致FLOAT32、FLOAT16、BFLOAT16ND补充约束Kirin X90 / Kirin 9030 处理器系列产品不支持 BFLOAT16。这一点在 host 算子定义 foreach_asin_def.cpp 中得到源码印证通用平台的tensor_dtype_list为{DT_FLOAT16, DT_FLOAT, DT_BF16}而GetKirinCoreConfig()中为麒麟平台单独配置的tensor_dtype_list_kirin仅包含{DT_FLOAT16, DT_FLOAT}同时开启了PrecisionReduceFlag(true)精度降低模式。aclnn 接口参数细节在 aclnn 接口场景下参数进一步细化为详见 aclnnForeachAsin.mdxaclTensorList*输入支持空 Tensor列表中所有 Tensor 数据类型保持一致支持 BFLOAT16、FLOAT16、FLOAT32数据格式 ND维度 0~8 维输入支持非连续 Tensor。outaclTensorList*输出支持空 Tensor数据类型与格式与x一致shape size 大于等于x的 shape size维度 0~8 维输出不支持非连续 Tensor。workspaceSizeuint64_t*输出返回需要在 Device 侧申请的 workspace 大小。executoraclOpExecutor**输出返回 op 执行器包含了算子计算流程。四、约束说明使用 ForeachAsin 算子需注意以下约束输出不支持非连续 Tensor即out中的 Tensor 必须是内存连续的strides 需与连续排布一致。从 aclnn 接口文档的参数表看输入x标注为“非连续 Tensor √”而输出out标注为“非连续 Tensor ×”。输入、输出列表中各 Tensor 的数据类型必须各自保持一致且输入与输出的数据类型必须一致。维度范围为 0~8 维。输出 shape size 需大于等于输入 shape sizeaclnn 接口下要求。五、调用方式foreach/foreach_asin/README.md 给出两种调用方式调用方式样例代码说明aclnn 接口test_aclnn_foreach_asin.cpp通过 aclnnForeachAsin 接口方式调用 ForeachAsin 算子图模式-通过算子 IR foreach_asin_proto.h 构图方式调用 ForeachAsin 算子5.1 图模式算子 IR图模式通过 op_graph/foreach_asin_proto.h 中注册的ForeachAsin算子原型构图。该 IR 使用REG_OP宏声明了动态输入x与动态输出y数据类型为{DT_FLOAT, DT_FLOAT16, DT_BF16}REG_OP(ForeachAsin) .DYNAMIC_INPUT(x, TensorType({DT_FLOAT, DT_FLOAT16, DT_BF16})) .DYNAMIC_OUTPUT(y, TensorType({DT_FLOAT, DT_FLOAT16, DT_BF16})) .OP_END_FACTORY_REG(ForeachAsin)5.2 aclnn 两段式接口aclnn 接口遵循 CANN 算子库通用的两段式接口设计参见 docs/zh/context/two_phase_api.md必须先调用aclnnForeachAsinGetWorkspaceSize获取计算所需 workspace 大小以及包含算子计算流程的执行器executor再调用aclnnForeachAsin执行计算。第一段接口原型aclnnStatus aclnnForeachAsinGetWorkspaceSize( const aclTensorList *x, const aclTensorList *out, uint64_t *workspaceSize, aclOpExecutor **executor)第二段接口原型aclnnStatus aclnnForeachAsin( void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, aclrtStream stream)第二段接口各参数含义workspace在 Device 侧申请的 workspace 内存地址workspaceSize在 Device 侧申请的 workspace 大小由第一段接口获取executorop 执行器包含算子计算流程stream指定执行任务的 Stream。第一段接口的返回码与错误场景第一段接口完成入参校验出现以下场景时报错返回码定义参见 docs/zh/context/aclnn_return_code.md返回码错误码描述ACLNN_ERR_PARAM_NULLPTR161001传入的 x 或 out 是空指针ACLNN_ERR_PARAM_INVALID161002x 或 out 的数据类型不在支持范围之内ACLNN_ERR_PARAM_INVALID161002x 和 out 的数据类型不一致ACLNN_ERR_INNER_TILING_ERROR561002x 与 out 的 shape 不满足约束ACLNN_ERR_INNER_TILING_ERROR561002x 或 out 中的 Tensor 数据类型不一致ACLNN_ERR_INNER_TILING_ERROR561002x 或 out 中的 Tensor 维度超过 8 维确定性计算aclnnForeachAsin默认采用确定性实现即相同输入在多次执行下结果可复现这对调试和精度比对场景非常友好。六、完整调用示例aclnn 接口仓库在 examples/test_aclnn_foreach_asin.cpp 提供了可直接参考的完整示例下面结合该示例讲解关键步骤具体编译与执行过程可参考 docs/zh/context/compile_and_run_sample.md。6.1 资源初始化首先完成 ACL 环境的固定初始化aclInit初始化、aclrtSetDevice绑定设备、aclrtCreateStream创建执行流。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; }6.2 构造输入输出 aclTensor 与 aclTensorListForeachAsin 的输入输出是张量列表因此需要先为每个 Tensor 创建aclTensor再通过aclCreateTensorList组装成aclTensorList。示例中构造了两个输入 Tensorshape 分别为{2, 3}和{1, 3}以及对应的两个输出 Tensor// 2. 构造输入与输出 std::vectorint64_t selfShape1 {2, 3}; std::vectorint64_t selfShape2 {1, 3}; std::vectorint64_t outShape1 {2, 3}; std::vectorint64_t outShape2 {1, 3}; std::vectorfloat input1HostData {0.1, 0.2, 0.3, 0.4, 0.5, 0.6}; std::vectorfloat input2HostData {0.7, 0.8, 0.9}; std::vectorfloat out1HostData(6, 0); std::vectorfloat out2HostData(3, 0); // 创建 input1 aclTensor ret CreateAclTensor(input1HostData, selfShape1, input1DeviceAddr, aclDataType::ACL_FLOAT, input1); ret CreateAclTensor(input2HostData, selfShape2, input2DeviceAddr, aclDataType::ACL_FLOAT, input2); ret CreateAclTensor(out1HostData, outShape1, out1DeviceAddr, aclDataType::ACL_FLOAT, out1); ret CreateAclTensor(out2HostData, outShape2, out2DeviceAddr, aclDataType::ACL_FLOAT, out2); std::vectoraclTensor* tempInput{input1, input2}; aclTensorList* tensorListInput aclCreateTensorList(tempInput.data(), tempInput.size()); std::vectoraclTensor* tempOutput{out1, out2}; aclTensorList* tensorListOutput aclCreateTensorList(tempOutput.data(), tempOutput.size());CreateAclTensor内部完成三步工作用aclrtMalloc申请 device 侧内存并用aclrtMemcpy将 host 数据拷入计算连续 Tensor 的 strides从最后一维向前累乘最后调用aclCreateTensor以ACL_FORMAT_ND格式创建张量描述。6.3 两段式调用先调用第一段接口获得workspaceSize与executor再按需申请 workspace 内存最后调用第二段接口执行计算// 3. 调用CANN算子库API uint64_t workspaceSize 0; aclOpExecutor* executor; // 调用aclnnForeachAsin第一段接口 ret aclnnForeachAsinGetWorkspaceSize(tensorListInput, tensorListOutput, workspaceSize, executor); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnForeachAsinGetWorkspaceSize failed. ERROR: %d\n, ret); return ret); // 根据第一段接口计算出的workspaceSize申请device内存 void* workspaceAddr nullptr; 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); } // 调用aclnnForeachAsin第二段接口 ret aclnnForeachAsin(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnForeachAsin failed. ERROR: %d\n, ret); return ret);注意workspaceSize 0时无需申请 workspace示例中if (workspaceSize 0)分支处理了该场景这也与 kernel 实现中“foreach(vector) 不需要 workspace”的注释相互印证见 foreach_asin.cpp。6.4 同步与结果回拷执行完毕后调用aclrtSynchronizeStream(stream)同步等待任务结束再通过aclrtMemcpy将结果从 device 侧拷回 host 侧并打印// 4. 固定写法同步等待任务执行结束 ret aclrtSynchronizeStream(stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclrtSynchronizeStream failed. ERROR: %d\n, ret); return ret); // 5. 获取输出的值 auto size GetShapeSize(outShape1); std::vectorfloat out1Data(size, 0); ret aclrtMemcpy(out1Data.data(), out1Data.size() * sizeof(out1Data[0]), out1DeviceAddr, size * sizeof(out1Data[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(out1 result[%ld] is: %f\n, i, out1Data[i]); }最后依次释放aclTensorList、device 内存含 workspace、Stream并调用aclrtResetDevice与aclFinalize完成资源回收。七、源码级实现解析从 Tiling 到 Kernel7.1 Host 侧算子注册与平台差异化配置op_host/foreach_asin_def.cpp 定义了ForeachAsin : public OpDef算子类。通用平台下输入输出均声明为DYNAMIC动态参数数据类型支持FLOAT16/FLOAT/BF16格式为FORMAT_ND并开启AutoContiguous()自动连续化处理通过AICore().AddConfig注册 ascend950、ascend910_93、ascend910b 三个平台。麒麟平台的差异化配置GetKirinCoreConfig()额外开启了DynamicCompileStaticFlag、DynamicFormatFlag、DynamicRankSupportFlag、DynamicShapeSupportFlag等动态编译/动态 shape 标志并将数据类型收窄为FLOAT16/FLOAT不支持 BF16同时PrecisionReduceFlag(true)允许精度降低计算。平台二进制配置方面ascend950 的 foreach_asin_binary.json 中为 float16、float32、bfloat16 三种数据类型分别生成了对应的算子二进制bin_filename输入输出均为paramType: dynamic、format: ND、shape: [-2]动态 shape说明该算子在 AICore 上是按数据类型模板实例化的动态算子。7.2 Tiling核数切分与累计偏移计算op_host/arch35/foreach_asin_tiling_arch35.cpp 是 arch35 平台Ascend 950 / A3 系列的 Tiling 实现核心流程获取平台信息优先从context-GetCompileInfo()读取核数与 UB 大小失败则回退到GetPlatformInfoFallback通过PlatformAscendC获取GetCoreNumAiv()与 UB 内存大小UB 大小需大于 DCACHE_SIZE128KB实际可用 UB 为ubSize - DCACHE_SIZE。统计张量列表通过GetInputInstanceInfo(0)-GetInstanceNum()获取 Tensor 数量1~256遍历每个GetDynamicInputShape(0, i)累加元素数填充tensorCumulativeOffset[]累计偏移数组。计算核切分参数perCoreElements取max(1024, ceil(totalElements / maxCoreNum))并向上对齐到 32needCoreNum由总元素数与单核元素数相除得到。空输入totalElements 0时兜底使用perCoreElements 1024、needCoreNum 1。确定 Tiling Key根据输入 dtypeFLOAT/FLOAT16/BF16映射到FOREACH_ASIN_TPL_DTYPE_FP32/FP16/BF16模板参数生成对应的 tiling key。Tiling 数据结构定义在 op_kernel/arch35/foreach_asin_tiling_data.h包含needCoreNum、totalElements、perCoreElements、tensorNum与tensorCumulativeOffset[257]数组最多支持 256 个 Tensor模板参数取值定义在 foreach_asin_tiling_key.h。7.3 KernelSIMT 向量核逐元素 asinarch35 平台的 kernel 入口 op_kernel/arch35/foreach_asin.cpp 根据编译期dtype模板参数FP32/FP16/BF16分派到NsForeachAsin::ProcessT。真正的计算逻辑位于 op_kernel/arch35/foreach_asin_simt.h这是一个 SIMT单指令多线程向量核实现关键点单元素 asin 计算ComputeAsinTfloat 直接调用asinfhalf 与 bfloat16 先转 float 计算asinf再转回原类型即“半精度提升到单精度计算后回写”保证精度。核级切分Process根据GetBlockIdx()与perCoreElements计算当前核负责的[coreStart, coreEnd)元素区间空核直接返回。Tensor 列表寻址通过ListTensorDesc解析各 Tensor 的 GM 地址遍历所有 Tensor计算当前核区间与各 Tensor 元素区间[tensorStart, tensorEnd)的交集将全局索引转换为 Tensor 内局部索引后以 512 线程的Simt::VF_CALLForeachAsinSimtKernelT启动向量函数并行计算。7.4 通用平台的 Kernel 入口非 arch35 平台如 arch22 对应的 Atlas A2/A3 等使用通用入口 op_kernel/foreach_asin.cpp。该实现基于foreach_utils提供的ForeachTriangle模板类见 foreach/foreach_utils按TILING_KEY分派TILING_KEY_IS(1)ForeachTrianglehalf, half, Asinhalf, falseFP16 输入/输出TILING_KEY_IS(2)ForeachTrianglefloat, float, Asinfloat, falseFP32 输入/输出TILING_KEY_IS(4)ForeachTrianglebfloat16_t, float, Asinfloat, falseBF16 输入、FP32 内部计算并针对__NPU_ARCH__ 3003 || 3113对应部分平台做了条件排除。其中AsinT, false来自 common 数学库 kernel_operator_asin_intf.h通过lib/math/kernel_operator_asin_intf.h引入false模板参数通常表示不做额外精度提升处理。八、测试与验证仓库为 ForeachAsin 提供了完整的单测与 ST 测试支撑可用于验证算子正确性UT单元测试host 侧arch22与arch35各有一套 tiling 单测另有 infershape 推导单测kernel 侧test_foreach_asin.cpp 配合 tensor_list_operate.h 构造张量列表用例数据生成与比对脚本位于 asin_data/gen_data.py 与 asin_data/compare_data.py。ST系统测试目录 tests/st/aclnnForeachAsin 下提供 ATK 用例配置 atk_aclnnForeachAsin.json 与执行脚本 executor_aclnnForeachAsin.py从框架侧端到端验证aclnnForeachAsin接口行为。九、小结ForeachAsin 是 CANN ops-nn 中“逐张量列表批量数学运算”家族的典型代表其设计体现了该系列算子共通的四层结构OpDef 算子注册含平台差异化配置→ Tiling 核切分按总元素数与累计偏移均匀切核→ Kernel 计算SIMT 向量核逐元素 asin半精度提升到 FP32 计算→ aclnn 两段式接口封装。在实际开发中若需要在 Ascend 950 / Atlas A2 / A3 等平台上对一组张量批量执行反正弦运算可直接复用aclnnForeachAsin接口若需图模式下沉则基于ForeachAsin算子 IR 构图即可。使用时务必注意输出不支持非连续 Tensor、麒麟平台不支持 BFLOAT16、输入输出 dtype 必须一致这三条关键约束。【免费下载链接】ops-nn本项目是CANN提供的神经网络类计算算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-nn创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价