新闻详情

新闻详情

首页 / 资讯中心 / 详情

CANN SiP asdMul 算子详解:基于 Ascend NPU 的复数向量逐元素乘法(Hadamard 积)API 指南

发布时间:2026/9/18 17:36:03来源:尧图网络
CANN SiP asdMul 算子详解:基于 Ascend NPU 的复数向量逐元素乘法(Hadamard 积)API 指南
CANN SiP asdMul 算子详解基于 Ascend NPU 的复数向量逐元素乘法Hadamard 积API 指南【免费下载链接】sip本项目是CANN提供的一款高效、可靠的高性能信号处理算子加速库基于华为Ascend AI处理器专门为信号处理领域而设计。项目地址: https://gitcode.com/cann/sip本文是 CANN SiPSignal Processing信号处理加速库基础BASE域asdMul算子的完整技术指南。该算子面向华为 Ascend AI 处理器为复数向量提供逐元素乘积Hadamard 积能力是复数域信号处理、阵列运算中高频复用的基础原语。阅读本文后你将掌握asdMul的函数原型、参数约束、产品支持矩阵、C 调用示例、Torch 自定义算子torch_sip封装以及其在仓库中的源码实现与单测验证链路可直接上手在 Atlas A2/A3 与 Ascend 950 系列环境编写并运行首个复数乘法样例。功能说明复向量逐元素乘积Hadamard 积asdMul的接口功能是支持向量逐元素乘积Hadamard能力返回一个和输入同样形状大小的复数矩阵。它接受两个复数输入张量A、B输出与二者同形状的结果张量result计算公式为$$ result A \odot B (A){ij} (B){ij} $$即两个矩阵对应位置上的复数元素直接相乘不做任何约简或广播扩展输入 shape 必须一致。这一操作在复数信号处理中对应复平面上的幅度-相位联合缩放例如对频域信号逐点乘以复滤波器系数即可通过本算子完成。计算示例输入A为[ [ 11i, 11i ], [ 22i, 22i ] ]输入B为[ [ 11i, 11i ], [ 22i, 22i ] ]调用asdMul算子后输出result为[ [ 02i, 02i ], [ 08i, 08i ] ]以(11i) × (11i)为例1×1 - 1×1 0为实部1×1 1×1 2为虚部即02i同理(22i) × (22i) 08i与示例输出完全一致可用于快速自检调用结果。产品支持情况asdMul在不同昇腾产品系列上的支持情况如下表与仓库 docs/zh/API_Reference/base/asdMul.md 保持一致产品系列支持情况Ascend 950PR / Ascend 950DT支持Atlas A3 训练系列产品 / Atlas A3 推理系列产品支持Atlas A2 训练系列产品 / Atlas A2 推理系列产品支持Atlas 200I/500 A2 推理产品不支持Atlas 推理系列产品不支持Atlas 训练系列产品不支持从源码角度印证在 ops/base/mul/mul_operation.cpp 的MulOperation::GetBestKernel中算子仅对ASCEND_950Ascend 950 系列与ASCEND_910B对应 Atlas A2 训练/推理系列两个平台类型返回可用 Kernel其他平台直接记录错误日志并返回nullptr与上表“不支持”的产品列完全对应。函数原型asdMul的声明位于公共头文件 include/base_api.h原型如下AspbStatus asdMul( int n, const aclTensor * x, const aclTensor * y, aclTensor * z, void * stream, void * workspace nullptr)返回值类型AspbStatus为状态码枚举定义于 core/utils/include/utils/aspb_status.hACL_SUCCESS表示执行成功。参数说明参数名输入/输出描述nint输入表示输入的元素个数。xconst aclTensor *输入表示输入的矩阵对应公式中的A。数据类型支持 COMPLEX32、COMPLEX64数据格式支持 NDshape 为 [n]。yconst aclTensor *输入表示输入的矩阵对应公式中的B。数据类型支持 COMPLEX32、COMPLEX64数据格式支持 NDshape 为 [n]。zaclTensor *输出表示输出的矩阵对应公式中的result。数据类型支持 COMPLEX32、COMPLEX64数据格式支持 NDshape 为 [n]。streamvoid *输入NPU 执行流aclrtStream。workspacevoid *输入asdMul 算子所需要的 workspace可缺省默认nullptr。要点说明数据类型支持两种复数精度。COMPLEX32 对应半精度复数实部、虚部各为 fp16COMPLEX64 对应单精度复数实部、虚部各为 fp32。两个输入x、y与输出z必须使用同一数据类型。数据格式仅支持 ND 连续排布格式输入 shape 为[n]的一维向量。注意n表示的是复数元素个数而非实/虚部分开的标量个数。shape 一致性x、y的 shape 必须一致算子按对应位置逐元素相乘输出z与输入同 shape。约束说明输入的元素个数n理论支持范围为[1, 9.22e18]对应 64 位整数可表示的取值上限。实际运行时的可用规模同时受设备内存容量与 Kernel 分块策略限制建议在目标产品上以实际 benchmark 为准。返回码接口返回AspbStatus状态码各错误码的详细含义请参见 docs/zh/context/SiP返回码.md。典型使用方式是通过ASD_STATUS_CHECK宏在调用失败时打印Execute failed.并退出#define ASD_STATUS_CHECK(err) \ do { \ AsdSip::AspbStatus err_ (err); \ if (err_ ! AsdSip::ErrorType::ACL_SUCCESS) { \ std::cout Execute failed. std::endl; \ exit(-1); \ } \ } while (0)调用示例以下示例代码来自原文档旨在提供快速上手、开发和调试算子的最小化实现核心目标是使用最精简的代码展示算子的核心功能而非提供生产级的安全保障。不推荐将示例代码直接作为业务代码使用。前置环境准备参照 example/A2/BASE/asd_mul/README.md 中的说明配置 CANN 环境变量source /usr/local/Ascend/ascend-toolkit/set_env.sh在 SiP 根目录编译加速库并设置环境变量cd ${SiP_root_path} bash build.sh source output/set_env.sh注意该编译方式仅支持通过 git 下载的加速库编译过程需要联网下载依赖库。更多编译命令说明参见 docs/compilation_build.md。进入示例目录执行构建脚本cd example/A2/BASE/asd_mul bash build.shmul_complex32 示例COMPLEX32 / fp16 复数#include iostream #include vector #include complex #include asdsip.h #include acl/acl.h #include acl_meta.h using namespace AsdSip; #define ASD_STATUS_CHECK(err) \ do { \ AsdSip::AspbStatus err_ (err); \ if (err_ ! AsdSip::ErrorType::ACL_SUCCESS) { \ std::cout Execute failed. std::endl; \ exit(-1); \ } \ } while (0) #define CHECK_RET(cond, return_expr) \ do { \ if (!(cond)) { \ 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; } int Init(int32_t deviceId, aclrtStream *stream) { // 固定写法acl初始化 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 printTensor(const std::complexop::fp16_t *tensorData, int64_t nums) { for (int64_t i 0; i nums; i) { std::cout ( (float)tensorData[i].real() , (float)tensorData[i].imag() ) ; } std::cout std::endl; } int main(int argc, char **argv) { int deviceId 0; aclrtStream stream; auto ret Init(deviceId, stream); CHECK_RET(ret ::ACL_SUCCESS, LOG_PRINT(Init acl failed. ERROR: %d\n, ret); return ret); int64_t n 8; int64_t vecSize n; std::vectorstd::complexop::fp16_t tensorInXData; std::vectorstd::complexop::fp16_t tensorInYData; tensorInXData.reserve(vecSize); tensorInYData.reserve(vecSize); for (int64_t i 0; i vecSize; i) { tensorInXData.push_back({(op::fp16_t)(9.0f i), (op::fp16_t)(100.0f i)}); } for (int64_t i 0; i vecSize; i) { tensorInYData.push_back({(op::fp16_t)(22.0f i), (op::fp16_t)(33.0f * (i 1))}); } std::vectorstd::complexop::fp16_t tensorOutZData( vecSize, {(op::fp16_t)0.0f, (op::fp16_t)0.0f}); std::cout ------- input X ------- std::endl; printTensor(tensorInXData.data(), vecSize); std::cout ------- input Y ------- std::endl; printTensor(tensorInYData.data(), vecSize); std::vectorint64_t xShape {vecSize}; std::vectorint64_t yShape {vecSize}; std::vectorint64_t zShape {vecSize}; aclTensor *inputX nullptr; aclTensor *inputY nullptr; aclTensor *outputZ nullptr; void *inputXDeviceAddr nullptr; void *inputYDeviceAddr nullptr; void *outputZDeviceAddr nullptr; ret CreateAclTensor(tensorInXData, xShape, inputXDeviceAddr, aclDataType::ACL_COMPLEX32, inputX); CHECK_RET(ret ::ACL_SUCCESS, return ret); ret CreateAclTensor(tensorInYData, yShape, inputYDeviceAddr, aclDataType::ACL_COMPLEX32, inputY); CHECK_RET(ret ::ACL_SUCCESS, return ret); ret CreateAclTensor(tensorOutZData, zShape, outputZDeviceAddr, aclDataType::ACL_COMPLEX32, outputZ); CHECK_RET(ret ::ACL_SUCCESS, return ret); ASD_STATUS_CHECK(asdMul(n, inputX, inputY, outputZ, stream)); ret aclrtSynchronizeStream(stream); CHECK_RET(ret ::ACL_SUCCESS, LOG_PRINT(aclrtSynchronizeStream failed. ERROR: %d\n, ret); return ret); ret aclrtMemcpy(tensorOutZData.data(), vecSize * sizeof(std::complexop::fp16_t), outputZDeviceAddr, vecSize * sizeof(std::complexop::fp16_t), ACL_MEMCPY_DEVICE_TO_HOST); CHECK_RET(ret ::ACL_SUCCESS, LOG_PRINT(copy z from device to host failed. ERROR: %d\n, ret); return ret); std::cout ------- output Z ------- std::endl; printTensor(tensorOutZData.data(), vecSize); std::cout Execute successfully. std::endl; aclDestroyTensor(inputX); aclDestroyTensor(inputY); aclDestroyTensor(outputZ); aclrtFree(inputXDeviceAddr); aclrtFree(inputYDeviceAddr); aclrtFree(outputZDeviceAddr); aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); return 0; }代码结构解读ACL 初始化固定写法Init内依次调用aclInit、aclrtSetDevice、aclrtCreateStream完成运行时初始化并创建 NPU 执行流。host 到 device 的数据搬运CreateAclTensor模板函数先通过aclrtMalloc申请 device 侧内存再用aclrtMemcpyACL_MEMCPY_HOST_TO_DEVICE把 host 数据拷入随后计算连续 tensor 的 strides最后用aclCreateTensor创建 ND 格式的aclTensor。COMPLEX32 场景使用op::fp16_t类型的std::complex作为元素类型。核心调用一行ASD_STATUS_CHECK(asdMul(n, inputX, inputY, outputZ, stream));即完成算子下发其中n为复数元素个数。结果回读与资源释放aclrtSynchronizeStream同步等待算子执行完成aclrtMemcpy以ACL_MEMCPY_DEVICE_TO_HOST方式将结果拷回 host 并打印最后依次aclDestroyTensor、aclrtFree、aclrtDestroyStream、aclrtResetDevice、aclFinalize释放全部资源。mul_complex64 示例COMPLEX64 / fp32 复数COMPLEX64 场景与上述流程完全一致仅三处差异元素类型从std::complexop::fp16_t换为std::complexfloat创建 tensor 时aclDataType从ACL_COMPLEX32换为ACL_COMPLEX64数据构造与打印函数按std::complexfloat适配。#include iostream #include vector #include complex #include asdsip.h #include acl/acl.h #include acl_meta.h using namespace AsdSip; #define ASD_STATUS_CHECK(err) \ do { \ AsdSip::AspbStatus err_ (err); \ if (err_ ! AsdSip::ErrorType::ACL_SUCCESS) { \ std::cout Execute failed. std::endl; \ exit(-1); \ } \ } while (0) #define CHECK_RET(cond, return_expr) \ do { \ if (!(cond)) { \ 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; } int Init(int32_t deviceId, aclrtStream *stream) { // 固定写法acl初始化 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 printTensor(const std::complexfloat *tensorData, int64_t nums) { for (int64_t i 0; i nums; i) { std::cout tensorData[i] ; } std::cout std::endl; } int main(int argc, char **argv) { int deviceId 0; aclrtStream stream; auto ret Init(deviceId, stream); CHECK_RET(ret ::ACL_SUCCESS, LOG_PRINT(Init acl failed. ERROR: %d\n, ret); return ret); int64_t n 8; int64_t vecSize n; std::vectorstd::complexfloat tensorInXData; std::vectorstd::complexfloat tensorInYData; tensorInXData.resize(vecSize); tensorInYData.resize(vecSize); for (int64_t i 0; i vecSize; i) { tensorInXData[i] {(float)(1.0 i), (float)(1.0 i)}; } for (int64_t i 0; i vecSize; i) { tensorInYData[i] {(float)(2.0 i), 3.0}; } std::vectorstd::complexfloat tensorOutZData(vecSize, {0.0f, 0.0f}); std::cout ------- input X ------- std::endl; printTensor(tensorInXData.data(), vecSize); std::cout ------- input Y ------- std::endl; printTensor(tensorInYData.data(), vecSize); std::vectorint64_t xShape {vecSize}; std::vectorint64_t yShape {vecSize}; std::vectorint64_t zShape {vecSize}; aclTensor *inputX nullptr; aclTensor *inputY nullptr; aclTensor *outputZ nullptr; void *inputXDeviceAddr nullptr; void *inputYDeviceAddr nullptr; void *outputZDeviceAddr nullptr; ret CreateAclTensor(tensorInXData, xShape, inputXDeviceAddr, aclDataType::ACL_COMPLEX64, inputX); CHECK_RET(ret ::ACL_SUCCESS, return ret); ret CreateAclTensor(tensorInYData, yShape, inputYDeviceAddr, aclDataType::ACL_COMPLEX64, inputY); CHECK_RET(ret ::ACL_SUCCESS, return ret); ret CreateAclTensor(tensorOutZData, zShape, outputZDeviceAddr, aclDataType::ACL_COMPLEX64, outputZ); CHECK_RET(ret ::ACL_SUCCESS, return ret); ASD_STATUS_CHECK(asdMul(n, inputX, inputY, outputZ, stream)); ret aclrtSynchronizeStream(stream); CHECK_RET(ret ::ACL_SUCCESS, LOG_PRINT(aclrtSynchronizeStream failed. ERROR: %d\n, ret); return ret); ret aclrtMemcpy(tensorOutZData.data(), vecSize * sizeof(std::complexfloat), outputZDeviceAddr, vecSize * sizeof(std::complexfloat), ACL_MEMCPY_DEVICE_TO_HOST); CHECK_RET(ret ::ACL_SUCCESS, LOG_PRINT(copy z from device to host failed. ERROR: %d\n, ret); return ret); std::cout ------- Output ------- std::endl; printTensor(tensorOutZData.data(), vecSize); std::cout Execute successfully. std::endl; aclDestroyTensor(inputX); aclDestroyTensor(inputY); aclDestroyTensor(outputZ); aclrtFree(inputXDeviceAddr); aclrtFree(inputYDeviceAddr); aclrtFree(outputZDeviceAddr); aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); return 0; }两个示例的完整可运行版本分别位于 example/A2/BASE/asd_mul/example_mul_complex32.cpp 与 example/A2/BASE/asd_mul/example_mul_complex64.cpp默认构建脚本可直接编译 complex32 场景complex64 场景需将编译脚本中的源文件替换为example_mul_complex64.cpp后重新构建。源码实现从 aclnnMul 到平台 Kernel 的调度链路公共 API 层基于 aclnnMul 的封装core/base/mul.cpp 实现了算子核心调度逻辑Mul。其工作流程为通过toAclTensor将内部Mki::Tensor转换为aclTensor任一转换失败即返回ACL_ERROR_INTERNAL_ERROR并打印错误日志调用aclnnMulGetWorkspaceSize查询 workspace 大小并创建aclOpExecutor执行器调用aclnnMul(deviceBuffer, workspaceSize, executor, stream)将算子任务下发到 NPU 执行流。由此可见asdMul在公共 API 层复用了 CANN 通用Mul算子aclnnMul的异步执行机制workspace即为aclnnMul所需的设备侧临时缓冲区。算子内核层按平台与精度分发的 Kernel在算子内核侧ops/base/mul/mul_operation.cpp 中注册了MulOperation算子GetBestKernel根据平台类型选择 KernelAscend 950 系列ASCEND_950使用MulArch35KernelAtlas A2 系列ASCEND_910B则依据OpParam::CMul::CMulType区分MUL_C64MulC64Kernel与MUL_C32MulC32Kernel。InferShapeImpl根据cMulType推断输出张量 dtypeCOMPLEX64 或 COMPLEX32并保持输出与输入x相同的 format 与 dims。算子入参结构OpParam::CMul定义于 ops/include/params/mul.h包含复数元素个数n与乘法类型cMulType两个字段。对应 Kernel 实现位于 ops/base/mul/mul/op_kernel/ 目录mul_c32.cpp、mul_c64.cpp、mul_arch35.cpptiling 切分逻辑位于 ops/base/mul/mul/tiling/ 目录负责将大规模向量按 NPU 计算核资源切块调度。Torch 集成通过 torch_sip 在 PyTorch 中调用 asdMul除 C 接口外仓库还提供了 PyTorch 自定义算子封装sip_pta即 SiP PyTorch Adapter入口为 sip_pta/csrc/base/asd_mul.cppat::Tensor asdMul(const at::Tensor x, const at::Tensor y) { c10_npu::NPUGuard guard(x.device()); at::Tensor z at::empty_like(x); auto sip_stream c10_npu::getCurrentNPUStream().stream(false); int64_t n x.numel(); uint8_t* workspace_ptr nullptr; EXEC_FUNC(AsdSip::asdMul, n, x, y, z, sip_stream, workspace_ptr); return z; } TORCH_LIBRARY_FRAGMENT(torch_sip, m) { m.def(asd_mul(Tensor x, Tensor y) - Tensor); } TORCH_LIBRARY_IMPL(torch_sip, PrivateUse1, m) { m.impl(asd_mul, asdMul); }封装要点n x.numel()直接从输入张量的元素总数推导无需用户显式传入复用当前 NPU 流c10_npu::getCurrentNPUStream输出z通过at::empty_like(x)自动与输入保持相同 shape 与 dtype通过TORCH_LIBRARY_FRAGMENTTORCH_LIBRARY_IMPL注册为torch_sip.asd_mul自定义算子。对应的 Python 测试脚本 sip_pta/test/base/asd_mul.py 展示了完整调用方式import torch import torch_npu import torch_sip device torch.device(npu:0) torch.npu.set_device(device) shape (4, 4) x torch.complex(torch.randn(shape, devicedevice), torch.randn(shape, devicedevice)) y torch.complex(torch.randn(shape, devicedevice), torch.randn(shape, devicedevice)) expected x * y result torch_sip.asd_mul(x, y)测试通过torch.allclose(result, expected, rtol1e-3, atol1e-3)与 PyTorch 原生复数乘法结果对比验证精度一致性。构建扩展并运行cd sip_pta python setup.py build_ext --inplace python test/base/asd_mul.py单元测试验证Golden 数据生成与精度比对仓库为asdMul提供了基于 Golden 数据比对的 C 单元测试位于 tests/ut/unittest/base/asd_mul/test_asd_mul.cppTestAsdMulComplex64Case0与TestAsdMulComplex32Case0分别覆盖 COMPLEX64、COMPLEX32 两种精度输入规模取n 16测试流程为用 asd_mul_data/gen_data.py 生成 Golden 数据 → 构造aclTensor并调用asdMul下发执行 → 将设备侧结果拷回 host 落盘 → 用 asd_mul_data/compare_data.py 与 Golden 数据比对精度阈值与示例保持一致绝对误差ATOL 0.001、相对误差RTOL 0.001该文件同样可作算子正确性回归验证的参考模板。总结asdMul是 CANN SiP 中面向复数向量的基础逐元素乘法算子接口签名简洁asdMul(n, x, y, z, stream)仅需n与三个 ND 格式aclTensor即可完成一次 Hadamard 积计算支持 COMPLEX32 / COMPLEX64 两种精度覆盖 Ascend 950PR/950DT、Atlas A2、Atlas A3 系列产品。从仓库实现看它一方面复用了 CANN 通用aclnnMul的异步执行框架另一方面针对平台差异Ascend 950 的MulArch35Kernel、Atlas A2 的MulC32Kernel/MulC64Kernel做了 Kernel 级分发优化并配套 C 示例、torch_sip PyTorch 封装与 Golden 数据单测形成了一条从 API 到 Kernel、再到框架集成的完整闭环。如果你需要在昇腾 NPU 上对复数信号做逐点复乘如频域滤波、复数加权可直接参照本文示例接入该算子。【免费下载链接】sip本项目是CANN提供的一款高效、可靠的高性能信号处理算子加速库基于华为Ascend AI处理器专门为信号处理领域而设计。项目地址: https://gitcode.com/cann/sip创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

微信小程序连续扫码实战:camera组件避坑与性能优化指南 2026/9/18 18:12:10

微信小程序连续扫码实战:camera组件避坑与性能优化指南

1. 从一个真实需求说起:为什么要死磕连续扫码去年接了一个仓储盘点的小程序项目,需求方开口第一句话就是:“我要能一直扫,扫完一个自动接着扫下一个,中间不要让我点任何按钮。”听起来很简单对吧?微信小程序…

阅读更多 →
SCA落地实战:从Gitee集成到选型框架与依赖治理 2026/9/18 18:12:10

SCA落地实战:从Gitee集成到选型框架与依赖治理

1. 先搞清楚:SCA 到底解决什么问题1.1 SCA 的核心价值:不只是查漏洞很多人一听到软件成分分析(Software Composition Analysis,简称 SCA)就以为是“扫描依赖里有没有已知 CVE”。这个理解不能说错,但太窄了…

阅读更多 →
CANN ops-nn 算子开发指南:aclnnHardsigmoid 与 aclnnInplaceHardsigmoid 两段式接口详解 2026/9/18 18:12:10

CANN ops-nn 算子开发指南:aclnnHardsigmoid 与 aclnnInplaceHardsigmoid 两段式接口详解

CANN ops-nn 算子开发指南:aclnnHardsigmoid 与 aclnnInplaceHardsigmoid 两段式接口详解 【免费下载链接】ops-nn 本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。 项目地址: https://gitcode.com/cann/ops-nn HardSigmoid 是…

阅读更多 →
vllm-omni serve 命令详解:基于 Stage 的分进程部署与参数配置指南 2026/9/18 18:12:10

vllm-omni serve 命令详解:基于 Stage 的分进程部署与参数配置指南

vllm-omni serve 命令详解:基于 Stage 的分进程部署与参数配置指南 【免费下载链接】vllm-omni A framework for efficient model inference with omni-modality models 项目地址: https://gitcode.com/GitHub_Trending/vl/vllm-omni 导读 vllm serve 是 vL…

阅读更多 →
Visual Studio中C++多源文件独立运行的三种实操方案 2026/9/18 18:12:10

Visual Studio中C++多源文件独立运行的三种实操方案

1. 项目概述:为什么“多个源文件分开运行”是个伪命题,但却是新手最真实的痛点在 Visual Studio(VS)里点开一个 C 项目,看到七八个.cpp文件堆在解决方案资源管理器里,心里就发毛:“我改了main.c…

阅读更多 →
SaaS订阅订单管理实战:OPM体系如何与网络及服务器协同 2026/9/18 18:09:10

SaaS订阅订单管理实战:OPM体系如何与网络及服务器协同

做SaaS商业化这些年,订阅订单的管理一直是个被低估的环节。很多人觉得订单嘛,支付成功就算完事了,数据同步了,剩下的交给财务开票就行。但真正跑起来才发现,订阅制订单和传统买断制完全是两种生物——买断制是一次性交…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞