资讯详情

CANN SiP 信号处理加速库 Copy 算子指南:asdBlasScopy / asdBlasCcopy 向量复制全解析

📅 2026/9/18 3:12:02 | 华诺云谱 👁 阅读
CANN SiP 信号处理加速库 Copy 算子指南:asdBlasScopy / asdBlasCcopy 向量复制全解析
CANN SiP 信号处理加速库 Copy 算子指南asdBlasScopy / asdBlasCcopy 向量复制全解析【免费下载链接】sip本项目是CANN提供的一款高效、可靠的高性能信号处理算子加速库基于华为Ascend AI处理器专门为信号处理领域而设计。项目地址: https://gitcode.com/cann/sip本篇技术指南以 CANN SiP 信号处理加速库的 Copy 算子为绝对核心系统讲解asdBlasMakeCopyPlan、asdBlasScopy、asdBlasCcopy三个接口的功能定义、函数原型、参数约束与调用范式并结合仓库源码剖析其从 Handle/Plan 初始化、Workspace 申请到 Kernel 调度执行的完整调用链。读完本文你将掌握在 Atlas A2/A3 系列产品上编写实数/复数向量复制算子并完成编译、运行与结果校验的完整实战方案。产品支持情况Copy 算子在当前 CANN SiP 仓库中的产品支持矩阵如下依据 docs/zh/API_Reference/BLAS/Copy.md 整理产品支持情况Ascend 950PR/Ascend 950DT不支持Atlas A3 训练系列产品 / Atlas A3 推理系列产品支持Atlas A2 训练系列产品 / Atlas A2 推理系列产品支持Atlas 200I/500 A2 推理产品不支持Atlas 推理系列产品不支持Atlas 训练系列产品不支持从示例目录 example/A2/BLAS/copy/README.md 的说明来看示例代码适用于 Atlas A2/A3 训练系列产品、Atlas 800I A2 推理产品、Atlas A3 推理系列产品与上表支持范围一致。因此在编写和运行本文示例前请先确认目标设备型号属于上表中的支持行列。功能说明与计算公式Copy 算子提供两个方向的接口asdBlasMakeCopyPlan初始化该句柄对应的 Copy 算子配置即创建并绑定BlasCopyPlan。asdBlasScopy将实数向量的值复制到另外一个向量中。asdBlasCcopy将复数向量的值复制到另外一个向量中。两者的计算公式完全一致$$ y x $$即目标向量y与源向量x逐元素完全相同本质是一个元素级的一一映射/搬移运算。asdBlasScopy 示例输入x为[3, 4]调用后输出y为[3, 4]。asdBlasCcopy 示例输入x为[34i, 4-3i, 43i, 3-4i]调用后输出y为[34i, 4-3i, 43i, 3-4i]。该算子在信号处理场景中的典型用途包括数据搬移与重排前的缓冲、实数/复数数据的原位或异地复制、为后续 FFT/卷积等计算准备独立的数据副本等。作为 BLAS 层面的基础算子它直接对应经典 BLAS-1 级别的scopy/ccopy语义。函数原型三个接口的 C 函数原型如下与 include/blas_api.h 中声明一致AspbStatus asdBlasMakeCopyPlan( asdBlasHandle handle)AspbStatus asdBlasScopy( asdBlasHandle handle, const int64_t n, aclTensor * x, const int64_t incx, aclTensor * y, const int64_t incy)AspbStatus asdBlasCcopy( asdBlasHandle handle, const int64_t n, aclTensor * x, const int64_t incx, aclTensor * y, const int64_t incy)所有接口均返回AspbStatus状态码具体取值说明参见 docs/zh/context/SiP返回码.md。asdBlasMakeCopyPlan参数说明参数名输入/输出描述handleasdBlasHandle输入算子的句柄。返回值返回状态码具体参见 docs/zh/context/SiP返回码.md。源码层面的补充说明从 core/blas/copy.cpp 的实现可以看到asdBlasMakeCopyPlan内部会完成以下工作校验handle非空通过BlasPlanCache::doesPlanExist(handle)做重复绑定守卫——同一个 handle 只能绑定一次 plan重复初始化会被拒绝并返回ACL_ERROR_INVALID_PARAM避免对已绑定 handle 静默失败导致 use-after-free源码注释中标注了 issue #129new一个AsdSip::BlasCopyPlan并通过BlasPlanCache::MakePlan(handle, plan)注册到全局 Plan 缓存调用plan-MarkInitialized()标记 plan 已初始化完成。Plan 类本身定义在 core/blas/blasplan/include/blasplan/BlasCopyPlan.hBlasCopyPlan继承自基类BlasPlan负责承载算子执行所需的 stream、workspace 等运行期资源。asdBlasScopy asdBlasCcopy参数说明参数名输入/输出描述handleasdBlasHandle输入算子的句柄。nint64_t输入表示总的元素个数。xaclTensor *输入对应公式中的x。asdBlasScopy 的数据类型支持 FLOAT32asdBlasCcopy 的数据类型支持 COMPLEX64数据格式支持 NDshape 为[n]。incxint64_t输入x 相邻元素间的内存地址偏移量当前约束为 1。yaclTensor *输出对应公式中的y。asdBlasScopy 的数据类型支持 FLOAT32asdBlasCcopy 的数据类型支持 COMPLEX64数据格式支持 NDshape 为[n]。incyint64_t输入y 相邻元素间的内存地址偏移量当前约束为 1。返回值返回状态码具体参见 docs/zh/context/SiP返回码.md。源码层面的补充说明从 core/blas/copy.cpp 的实现可以看到两个接口内部执行的强校验逻辑asdBlasScopy通过aclGetDataType校验 x、y 的数据类型必须为ACL_FLOATFLOAT32否则返回ACL_ERROR_UNSUPPORTED_DATA_TYPEasdBlasCcopy校验 x、y 的数据类型必须为ACL_COMPLEX64否则返回ACL_ERROR_UNSUPPORTED_DATA_TYPE两者均校验n必须满足0 n UINT32_MAX即2^32否则返回ACL_ERROR_INVALID_PARAM当incx/incy传入 0时实现不会直接报错而是记录 INFO 日志并自动回退为 1这与文档中当前约束为 1的约束保持一致。实现细节在内部共用的asdBlasCopyImpl中core/blas/copy.cpp还会通过aclGetStorageShape读取 x、y 的存储形状并与n比对不匹配时返回ACL_ERROR_OP_OUTPUT_NOT_MATCH复数场景下cCopy 1存储元素个数按storageDims * 2计算一个 complex64 占两个 float32 存储单元对应asdBlasCcopy传入n * 2的存储元素数量。随后构造OpDescopName 为CopyOperation与OpParam::Copy参数定义在 ops/include/params/copy.h从BlasPlanCache取出已绑定的BlasCopyPlan最终调用RunAsdOpsV2沿 plan 绑定的 stream 异步下发计算。约束说明使用 Copy 算子时必须遵守以下约束来自原文档输入的元素个数当前支持的范围为[1, 6.71e06]。算子输入 shape 为[n]输出 shape 为[n]。算子实际计算时不支持 ND 高维度运算不支持维度 ≥ 3 的运算即仅支持一维向量复制。从 ops/blas/copy/copy/copy_kernel.cpp 的CanSupport校验可以看到内核侧还要求输入张量数量为 1、输出张量数量为 1且输入输出 dtype 必须一致否则拒绝执行。内核选择逻辑在 ops/blas/copy/copy_operation.cpp 中通过GetKernelByName(CopyF32Kernel)固定选用CopyF32Kernel该内核在 ops/blas/copy/CMakeLists.txt 中被注册进构建产物其 Tiling 逻辑由 ops/blas/copy/copy/tiling/copy_tiling.cpp 的CopyTiling函数完成用于将一维[n]数据切分为适合昇腾硬件分核并行搬运的分块。公共接口与算子调用流程Copy 算子遵循 SiP BLAS 算子统一的句柄Handle Plan Workspace调用范式公共接口定义详见 docs/zh/API_Reference/BLAS/BLAS公共接口.md。标准调用流程如下创建句柄调用asdBlasCreate(asdBlasHandle handle)创建全局唯一的 handle。初始化并绑定 Plan调用asdBlasMakeCopyPlan(handle)初始化该句柄对应的 Copy 算子配置并绑定 plan。注意同一 handle 只能初始化一次重复初始化会被拒绝。获取 Workspace 大小调用asdBlasGetWorkspaceSize(handle, size_t workspaceSize)获取计算所需 workspace 大小以及包含了算子计算流程的执行器。设置 Workspace调用asdBlasSetWorkspace(handle, void *workSpace)为对应的 plan 设置需要的 workspace。绑定 Stream调用asdBlasSetStream(handle, void *stream)将使用 runtime 创建的 stream 与 plan 实例绑定。执行计算调用asdBlasScopy或asdBlasCcopy执行向量复制。同步等待调用asdBlasSynchronize(handle)同步等待算子执行完成。销毁资源调用asdBlasDestroy(handle)销毁创建的 plan 并释放对应资源避免内存泄漏。调用示例以下示例代码源自原文档旨在提供快速上手、开发和调试算子的最小化实现其核心目标是使用最精简的代码展示算子的核心功能而非提供生产级的安全保障。不推荐用户直接将示例代码作为业务代码若将示例代码应用在自身真实业务场景中发生安全问题需用户自行承担。asdBlasScopy 示例FLOAT32 实数复制#include iostream #include vector #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; } 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 7; int64_t incx 1; int64_t incy 1; int64_t xSize 7; int64_t ySize 7; std::vectorfloat tensorInXData; tensorInXData.resize(xSize); for (int64_t i 0; i xSize; i) { tensorInXData[i] 1.0 i; } std::vectorfloat tensorOurtYData; tensorOurtYData.resize(ySize); std::cout ------- input x ------- std::endl; for (int64_t i 0; i xSize; i) { std::cout tensorInXData[i] ; } std::cout std::endl; std::vectorint64_t xShape {xSize}; std::vectorint64_t yShape {ySize}; aclTensor *inputX nullptr; aclTensor *outputY nullptr; void *inputXDeviceAddr nullptr; void *outputYDeviceAddr nullptr; ret CreateAclTensor(tensorInXData, xShape, inputXDeviceAddr, aclDataType::ACL_FLOAT, inputX); CHECK_RET(ret ::ACL_SUCCESS, return ret); ret CreateAclTensor(tensorOurtYData, yShape, outputYDeviceAddr, aclDataType::ACL_FLOAT, outputY); CHECK_RET(ret ::ACL_SUCCESS, return ret); asdBlasHandle handle; asdBlasCreate(handle); size_t lwork 0; void *buffer nullptr; asdBlasMakeCopyPlan(handle); asdBlasGetWorkspaceSize(handle, lwork); std::cout lwork lwork std::endl; if (lwork 0) { ret aclrtMalloc(buffer, static_castint64_t(lwork), ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret ::ACL_SUCCESS, LOG_PRINT(allocate workspace failed. ERROR: %d\n, ret); return ret); } asdBlasSetWorkspace(handle, buffer); asdBlasSetStream(handle, stream); ASD_STATUS_CHECK(asdBlasScopy(handle, n, inputX, incx, outputY, incy)); asdBlasSynchronize(handle); asdBlasDestroy(handle); ret aclrtMemcpy(tensorOurtYData.data(), ySize * sizeof(float), outputYDeviceAddr, ySize * sizeof(float), ACL_MEMCPY_DEVICE_TO_HOST); CHECK_RET(ret ::ACL_SUCCESS, LOG_PRINT(copy tensor y from device to host failed. ERROR: %d\n, ret); return ret); std::cout ------- output y ------- std::endl; for (int64_t i 0; i ySize; i) { std::cout tensorOurtYData[i] ; } std::cout std::endl; std::cout Execute successfully. std::endl; aclDestroyTensor(inputX); aclDestroyTensor(outputY); aclrtFree(inputXDeviceAddr); aclrtFree(outputYDeviceAddr); aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); return 0; }asdBlasCcopy 示例COMPLEX64 复数复制#include iostream #include vector #include cmath #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); \ } else { \ std::cout Execute successfully. std::endl; \ } \ } 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 tensorSize) { for (int64_t i 0; i tensorSize; 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 6; int incx 1; int incy 1; int64_t xSize 6; int64_t ySize 6; std::vectorstd::complexfloat tensorInXData; tensorInXData.resize(xSize); for (int64_t i 0; i xSize; i) { tensorInXData[i] {(float)(1.0 i), (float)(2.0 i)}; } std::vectorstd::complexfloat tensorOutYData; tensorOutYData.resize(ySize); std::cout ------- input TensorIn ------- std::endl; printTensor(tensorInXData.data(), xSize); std::vectorint64_t xShape {xSize}; std::vectorint64_t yShape {ySize}; aclTensor *inputX nullptr; aclTensor *outputY nullptr; void *inputXDeviceAddr nullptr; void *outputYDeviceAddr nullptr; ret CreateAclTensor(tensorInXData, xShape, inputXDeviceAddr, aclDataType::ACL_COMPLEX64, inputX); CHECK_RET(ret ::ACL_SUCCESS, return ret); ret CreateAclTensor(tensorOutYData, yShape, outputYDeviceAddr, aclDataType::ACL_COMPLEX64, outputY); CHECK_RET(ret ::ACL_SUCCESS, return ret); asdBlasHandle handle; asdBlasCreate(handle); size_t lwork 0; void *buffer nullptr; asdBlasMakeCopyPlan(handle); asdBlasGetWorkspaceSize(handle, lwork); std::cout lwork lwork std::endl; if (lwork 0) { ret aclrtMalloc(buffer, static_castint64_t(lwork), ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret ::ACL_SUCCESS, LOG_PRINT(allocate workspace failed. ERROR: %d\n, ret); return ret); } asdBlasSetWorkspace(handle, buffer); asdBlasSetStream(handle, stream); ASD_STATUS_CHECK(asdBlasCcopy(handle, n, inputX, incx, outputY, incy)); asdBlasSynchronize(handle); asdBlasDestroy(handle); ret aclrtMemcpy(tensorOutYData.data(), ySize * sizeof(std::complexfloat), outputYDeviceAddr, ySize * sizeof(std::complexfloat), ACL_MEMCPY_DEVICE_TO_HOST); CHECK_RET(ret ::ACL_SUCCESS, LOG_PRINT(copy tensor y from device to host failed. ERROR: %d\n, ret); return ret); std::cout ------- output TensorOut ------- std::endl; printTensor(tensorOutYData.data(), ySize); aclDestroyTensor(inputX); aclDestroyTensor(outputY); aclrtFree(inputXDeviceAddr); aclrtFree(outputYDeviceAddr); aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); return 0; }示例要点解读两个示例遵循完全一致的骨架核心可复用的套路包括初始化InitaclInit→aclrtSetDevice→aclrtCreateStream是 ACL 环境的固定初始化写法创建张量CreateAclTensor模板函数aclrtMalloc申请 Device 侧内存 →aclrtMemcpyACL_MEMCPY_HOST_TO_DEVICE写入输入数据 → 计算连续 strides →aclCreateTensor以ACL_FORMAT_ND格式创建aclTensor算子执行asdBlasCreate→asdBlasMakeCopyPlan→asdBlasGetWorkspaceSize当lwork 0时用aclrtMalloc申请 buffer→asdBlasSetWorkspace→asdBlasSetStream→asdBlasScopy/asdBlasCcopy结果回读与清理asdBlasSynchronize等待执行完成 →aclrtMemcpyACL_MEMCPY_DEVICE_TO_HOST回读输出 →asdBlasDestroy销毁句柄 → 依次aclDestroyTensor、aclrtFree、aclrtDestroyStream、aclrtResetDevice、aclFinalize释放所有资源。需要注意的是示例中asdBlasScopy的输出验证依赖y与x逐元素相等y x复数示例中ASD_STATUS_CHECK宏在成功时会额外打印Execute successfully.。示例生成的数据不代表实际场景可根据具体使用场景修改数据。编译与运行环境配置首先配置 CANN 环境变量source [CANN安装路径]/set_env.sh默认路径为source /usr/local/Ascend/ascend-toolkit/set_env.shSiP 加速库编译进入 SiP 根目录执行如下指令完成信号处理加速库的编译并设置加速库环境变量cd ${SiP_root_path} bash build.sh source output/set_env.sh特别说明上述编译方式仅支持通过 git 下载的加速库以 zip 压缩包方式下载的加速库不支持该编译方式编译过程需要联网下载依赖库因此编译环境需要联网该编译过程包括获取 ascend-boost-comm昇腾分布式通信加速库组件并编译该组件以及编译信号加速库两个步骤更多编译命令说明请参考 docs/compilation_build.md。运行 Demo仓库提供了可直接编译运行的示例程序 example/A2/BLAS/copy/example_scopy.cpp 与 example/A2/BLAS/copy/example_ccopy.cpp。进入示例所在目录执行构建脚本cd ${示例所在目录} bash build.sh默认编译脚本可直接编译运行example_scopy.cpp输入 xfloat32、ND、shape[n]输出 yfloat32、ND、shape[n]编译example_ccopy.cpp输入 xcomplex64、ND、shape[n]输出 ycomplex64、ND、shape[n]时需要将编译脚本中的example_scopy.cpp替换为example_ccopy.cpp后再编译运行。测试与验证仓库在单元测试中为 Copy 算子提供了端到端验证用例可作为自行开发与回归验证的参考实数场景 tests/ut/unittest/blas/scopy/test_scopy.cpp通过TestBlasScopyCase0用例调用asdBlasScopy执行复制将输入张量落盘生成测试数据再调用 tests/ut/unittest/blas/scopy/scopy_data/gen_data.py 生成 golden 数据最后用compare_data.py对比输出与 goldengolden 与输入 x 完全一致复数场景 tests/ut/unittest/blas/ccopy/test_ccopy.cpp使用同样的数据生成—执行—对比流程验证asdBlasCcopygolden 同样与输入 x 完全一致。这从测试侧再次印证了 Copy 算子y x的语义只要输出张量各元素与输入张量完全一致即可判定算子行为正确。总结Copy 算子是 CANN SiP 信号处理加速库中最基础的 BLAS-1 级算子之一通过asdBlasMakeCopyPlan完成 plan 绑定asdBlasScopy/asdBlasCcopy分别完成 FLOAT32 实数向量与 COMPLEX64 复数向量的一一复制。其实现严格遵循统一的Handle Plan Workspace Stream调用范式core/blas/copy.cpp经CopyOperationops/blas/copy/copy_operation.cpp选择CopyF32Kernelops/blas/copy/copy/copy_kernel.cpp在昇腾设备上分核并行执行。使用时需重点注意产品支持范围Atlas A2/A3 系列、数据类型约束FLOAT32/COMPLEX64、ND 格式、shape[n]、元素个数范围[1, 6.71e06]、不支持维度 ≥ 3 的运算以及同一 handle 只允许初始化一次。按照本文的示例代码与调用流程即可在支持的昇腾产品上快速完成向量复制算子的开发与验证。【免费下载链接】sip本项目是CANN提供的一款高效、可靠的高性能信号处理算子加速库基于华为Ascend AI处理器专门为信号处理领域而设计。项目地址: https://gitcode.com/cann/sip创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。