尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
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

相关推荐

SAP ERP实施中的ASAP方法论与项目验收要点

SAP ERP实施中的ASAP方法论与项目验收要点

简介:SAP ERP实施项目方案建议书是一份面向企业信息化负责人、项目经理及ERP实施顾问的完整项目规划参考,内容覆盖项目推行概要、实施方法论、阶段划分与验收标准。文档首先梳理了企业在全球化、数字化背景下面临的管理挑战,以及ERP项目优化运…

📅 2026/9/18 17:30:59
GPS单点定位精度瓶颈与多路径误差削弱策略

GPS单点定位精度瓶颈与多路径误差削弱策略

简介:这份资料是GPS单点定位观测值精度分析的原理性文档,适合测绘、导航与位置服务从业者及相关专业学生阅读,用于理解单点定位坐标解算流程与误差来源。包体为1个PDF文件,大小约150KB,内容聚焦伪距观测方程、卫星坐标…

📅 2026/9/18 17:30:59
Scrapy异步框架与全站递归爬取实战

Scrapy异步框架与全站递归爬取实战

简介:这份PDF文档系统讲解了开源Python网络爬虫框架Scrapy的核心知识,面向希望高效抓取网页数据的Python开发者,帮助读者理解框架的架构设计与异步网络库Twisted的运用,并能在数据挖掘、监测和自动化测试等场景中落地。资源为单个…

📅 2026/9/18 17:30:59
MORE NEWS

更多资讯

📰

Cocos Creator 真机闪退卡顿排查:资源、生命周期与打包 APK

1. 先给坑分个类:Cocos Creator 的问题为什么总集中在几块做 Cocos Creator 项目,尤其是从原型一路做到上线 APK 的团队,几乎都会经历同一个阶段:预览跑得好好的,真机一装就出问题。这不是运气差,而是引擎本…

📰

用colibri快速开发HTML5小游戏:轻量级Canvas引擎实战与踩坑记录

手上攒了不少做 Web 小游戏、交互动效的项目,之前一直用重型引擎,直到最近为一个小体量 HTML5 游戏找方案时,才发现了一个叫colibri的项目。它不像 Phaser 那样名声在外,也没有 PixiJS 那么庞大的生态,但正因为它轻、小…

📰

MiroFish:开源离线钓鱼记录与钓点规律分析工具

MiroFish 这个项目最初的起因特别朴素:我钓鱼有七八年,手机相册里躺着上千张鱼获照片,备忘录里零零散散记着"某某水库东岸,下午三点,鲫鱼几条",可真到了要复盘的时候,翻相册翻到手指发…

📰

TypeSpec 在伦敦证券交易所集团(LSEG)的实践:统一异构 API 与多语言 SDK 生成

TypeSpec 在伦敦证券交易所集团(LSEG)的实践:统一异构 API 与多语言 SDK 生成 【免费下载链接】typespec 项目地址: https://gitcode.com/GitHub_Trending/ty/typespec 本文基于 TypeSpec 官方博客中微软与 LSEG(London S…

📰

RWA代币化全链路实操指南:从资产上链到合规落地

RWA这个概念,我在2024年初第一次认真研究的时候,还觉得它就是个PPT里的热词,圈内人聊起来更多是拿来当叙事素材。到了最近这段时间,情况明显不一样了——身边做传统金融的朋友开始主动问起代币化资产怎么对接,几个做实…

📰

工具调用偶发循环?Sonnet 5 用 TaoToken 先核对 Base URL

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

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬