十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

CANN ops-math 中的 aclStft 算子:NPU 上滑动窗口傅里叶变换(STFT)的两段式接口调用与实现解析

CANN ops-math 中的 aclStft 算子:NPU 上滑动窗口傅里叶变换(STFT)的两段式接口调用与实现解析 CANN ops-math 中的 aclStft 算子NPU 上滑动窗口傅里叶变换STFT的两段式接口调用与实现解析【免费下载链接】ops-math本项目是CANN提供的数学类基础计算算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-math本文以 math/stft/docs/aclStft.md 为核心系统讲解 CANN ops-math 仓库中 STFT 算子的 aclnn 接口aclStft的功能定义、两段式调用流程、全部入参约束与返回码语义并结合 op_host/op_api/acl_stft.cpp 与 op_kernel/stft.cpp 等源码剖析其DFT 矩阵 Matmul的实现原理、AiCore/AiCpu 双路径调度与 DFT 矩阵缓存机制。读完本文你将能够独立完成 aclStft 的 workspace 计算、执行器创建、流式下发与结果回收并理解其输出 shape 推导、数据类型对应关系与性能相关约束。一、功能说明与数学定义STFTShort-Time Fourier Transform短时傅里叶变换将长时序信号切分为若干滑动窗口再对每个窗口分别做傅里叶变换得到时间-频率二维表示。aclStft接口计算输入在滑动窗口内的傅里叶变换与 PyTorch 的torch.stft对齐可参考 math/stft/op_graph/stft_proto.h 中 Compatible with pytorch STFT operator 的注释。当normalizedFalse时$$ X[w,m]\sum_{k0}^{winLength-1}window[k]*self[m*hopLengthk]exp(-j\frac{2{\pi}wk}{nFft}) $$当normalizedTrue时$$ X[w,m]\frac{1}{\sqrt{nFft}}(\sum_{k0}^{winLength-1}window[k]*self[m*hopLengthk]exp(-j\frac{2{\pi}wk}{nFft})) $$其中$w$FFT 的频点frequency bin。$m$滑动窗口的 index。$self$1 维或 2 维 Tensor。1 维时表示一条时序采样序列2 维时表示多条时序采样序列batch 维。$hopLength$滑动窗口的步进间隔hop。$window$1 维 Tensor即 STFT 的窗函数如 hann_window长度为 $winLength$。$exp(-j*\frac{2{\pi}wk}{nFft})$旋转因子twiddle factor。从源码视角看该公式在 NPU 上并非逐点循环实现而是被等价转化为DFT 矩阵 × 分帧矩阵的矩阵乘法问题aclStftGetWorkspaceSize内部生成旋转因子矩阵DFT matrix$W[k,n]exp(-j*2\pi k n / nFft)$配合窗函数与输入分帧结果通过 AiCore 上的 Matmul 完成频域计算详见后文源码级实现一节。二、产品支持情况aclStft在不同 NPU 平台上的支持情况如下产品支持情况Ascend 950PR / Ascend 950DT支持Atlas A3 训练系列产品 / Atlas A3 推理系列产品支持Atlas A2 训练系列产品 / Atlas A2 推理系列产品支持Atlas 200I/500 A2 推理产品不支持Atlas 推理系列产品不支持Atlas 训练系列产品不支持该表与算子级 READMEmath/stft/README.md完全一致且在源码中有直接印证acl_stft.cpp中的CheckPlatform()仅放行ASCEND910B、ASCEND910_93对应 A3、ASCEND950三类 SoC其他平台直接返回ACLNN_ERR_PARAM_INVALID并打印 STFT is not supported on this platform算子定义 op_host/stft_def.cpp 中也只为ascend910b、ascend910_93、ascend950三个平台注册了 AICore 配置。三、两段式接口与函数原型aclStft属于 CANN 的两段式接口必须先调用aclStftGetWorkspaceSize获取计算所需 workspace 大小以及包含算子计算流程的执行器executor再调用aclStft执行计算。第一段接口原型aclnnStatus aclStftGetWorkspaceSize( const aclTensor *self, const aclTensor *windowOptional, aclTensor *out, int64_t nFft, int64_t hopLength, int64_t winLength, bool normalized, bool onesided, bool returnComplex, uint64_t *workspaceSize, aclOpExecutor **executor)第二段接口原型aclnnStatus aclStft( void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, aclrtStream stream)对应的头文件声明位于 math/stft/op_host/op_api/acl_stft.h其 doxygen 注释中还给出了内部计算图self → l0op::Stft → l0op::ViewCopy → out即算子计算结果最终通过 ViewCopy 落到用户提供的outTensor 上。四、aclStftGetWorkspaceSize 参数详解第一段接口承担了全部入参校验、计算图构建与 workspace 估算任务。完整参数说明如下参数名输入/输出描述使用说明数据类型数据格式维度(shape)非连续Tensorself输入待计算的输入对应公式中的self不支持空 Tensorshape 为 [L]/[B, L]L 为时序采样序列长度B 为序列个数不支持输入 Inf、-Inf 或 NaNFLOAT32、DOUBLE、COMPLEX64、COMPLEX128ND1-2×windowOptional输入1D Tensor对应公式中的window不支持空 Tensor数据类型与self保持一致shape 为 [winLength]FLOAT32、DOUBLE、COMPLEX64、COMPLEX128ND1×out输出self 在 window 内的傅里叶变换结果对应公式中的X不支持空 TensorreturnComplexTrue 时 out 为 [N, T] 或 [B, N, T] 的复数 TensorreturnComplexFalse 时 out 为 [N, T, 2] 或 [B, N, T, 2] 的实数 Tensor。其中 N nFft(onesidedFalse) 或 (nFft // 2 1)(onesidedTrue)T 为滑动窗口个数T (L - nFft) // hopLength 1FLOAT32、DOUBLE、COMPLEX64、COMPLEX128ND3-4×nFft输入FFT 的点数大于 0对应公式中的nFft-INT64---hopLength输入滑动窗口的间隔大于 0对应公式中的hopLength-INT64---winLength输入window 的大小大于 0对应公式中的winLength-INT64---normalized输入是否对傅里叶变换结果进行标准化-BOOL---onesided输入是否返回全部结果或一半结果当输入self的数据类型为 COMPLEX64、COMPLEX128 时只支持配置为 FalseBOOL---returnComplex输入确认返回值是 complex Tensor 还是实、虚部分开的 Tensor-BOOL---workspaceSize输出返回需要在 Device 侧申请的 workspace 大小-----executor输出返回 op 执行器包含算子计算流程-----4.1 输出 shape 的推导规则输出维度由输入维度、onesided与returnComplex共同决定源码GetOutputShape()见 math/stft/op_host/op_api/acl_stft.cpp中的推导逻辑与文档一致滑动窗口个数frames (L - nFft) / hopLength 1频率维n onesided ? nFft / 2 1 : nFftreturnComplexTrue1 维输入 → [n, frames]2 维输入 → [B, n, frames]returnComplexFalse1 维输入 → [n, frames, 2]2 维输入 → [B, n, frames, 2]也就是说out的实际 shape 完全由self的 shape 与三个布尔属性推导而来调用方不能随意指定CheckShape()中会通过OP_CHECK_SHAPE_NOT_EQUAL_WITH_EXPECTED_SIZE严格校验。4.2 与算子级 README 参数表的对应关系在算子级文档 math/stft/README.md 中同样的参数以x / window / n_fft / hop_length / win_length / normalized / onesided / return_complex / y命名并补充了图模式下属性的默认值可供 aclnn 接口使用参考属性默认值hop_lengthn_fft / 4win_lengthn_fftnormalizedfalseonesidedtruereturn_complextrue在算子 IR 定义 math/stft/op_graph/stft_proto.h 中这些默认值同样被登记hop_length0、win_length00 表示未指定按默认规则取 floor(n_fft/4) 与 n_fft、normalizedfalse、onesidedtrue、return_complextruen_fft为必填属性。需要留意的是虽然仓库中注册了 STFT 的算子 IR但 math/stft/op_host/stft_def.cpp 中StftCheckSupport当前返回 Stft dont support graph mode即从当前源码看图模式调用并未放开aclnn 两段式接口是推荐的调用方式README 的调用说明一节也将 aclnn 接口列为第一种调用方式。五、返回码与错误场景aclStftGetWorkspaceSize返回aclnnStatus状态码具体取值可参见 aclnn 返回码说明。第一段接口完成入参校验以下场景会报错返回码错误码描述ACLNN_ERR_PARAM_NULLPTR161001传入的 self、out 是空指针ACLNN_ERR_PARAM_INVALID161002self 的数据格式不在支持的范围之内ACLNN_ERR_PARAM_INVALID161002self、windowOptional 的数据类型不一致ACLNN_ERR_PARAM_INVALID161002self、windowOptional、out 的数据类型不在平台的支持范围之内ACLNN_ERR_PARAM_INVALID161002nFft、hopLength、winLength 输入无效值ACLNN_ERR_PARAM_INVALID161002self、windowOptional、out 的维度不在支持的范围之内ACLNN_ERR_PARAM_INVALID161002当输入 self 的数据类型为 COMPLEX64、COMPLEX128 时onesided 的值为 True这些校验在源码CheckParams()及其子函数中逐条实现顺序为空指针检查CheckNotNull→ 数据类型检查CheckDtypeValid含self/window类型一致性→ 数据格式检查CheckFormat要求 ND→ shape 与属性检查CheckShape。其中CheckShape还会校验nFft 0且len nFfthopLength 00 winLength nFft当winLength ! nFft时window 的 shape 第 0 维必须等于winLength输入为复数时onesided必须为 False。六、aclStft 参数说明第二段接口仅负责执行参数如下参数名输入/输出描述workspace输入在 Device 侧申请的 workspace 内存地址workspaceSize输入在 Device 侧申请的 workspace 大小由第一段接口 aclStftGetWorkspaceSize 获取executor输入op 执行器包含算子计算流程stream输入指定执行任务的 Stream其实现aclStft函数体非常精简仅包含L2_DFX_PHASE_2性能打点与CommonOpExecutorRun(workspace, workspaceSize, executor, stream)的固定调用真正的工作都在第一段接口中完成。七、约束说明与 PyTorch 输入的差异PyTorch 接口的输入 self 为原始输入aclStftGetWorkspaceSize的入参 self 是原始输入经过前端 PyTorch 补 pad 后得到的结果。这一点意味着用户在 aclnn 侧直接调用时需要自行保证输入长度满足窗口切分要求。大 shape 超时风险当输入 self 的 shape 为 [B, L]且下式计算的结果较大时当前接口的计算可能超时$$ B * ((L - nFft) / hopLength 1) * nFft $$从源码看该公式对应分帧后的总数据规模AIC 侧 Matmul 的 M 维 × 帧数 × batch规模过大时核内循环frame split matmul耗时显著上升。约束nFft LwinLength nFft。normalized 语义当 normalizedTrue 时输出等价于对原始结果乘以 $\frac{1}{\sqrt{N}}$$$ STFT(w,m)\frac{1}{\sqrt{N}}X[w,m] $$数据类型对应关系self、windowOptional、returnComplex、out 之间的对应关系如下表selfwindowOptionalreturnComplexoutFLOAT32FLOAT32TrueCOMPLEX64DOUBLEDOUBLETrueCOMPLEX128COMPLEX64COMPLEX64TrueCOMPLEX64COMPLEX128COMPLEX128TrueCOMPLEX128FLOAT32FLOAT32FalseFLOAT32DOUBLEDOUBLEFalseDOUBLECOMPLEX64COMPLEX64FalseFLOAT32COMPLEX128COMPLEX128FalseDOUBLE即returnComplexTrue 时实数输入输出升级为复数类型returnComplexFalse 时复数输入退化为实/虚部各占一维的实数输出out 多出最后一维 size2。确定性计算aclStft默认是确定性实现同一输入多次执行结果一致。八、调用示例示例代码如下来源于 math/stft/examples/test_aclnn_stft.cpp与文档示例等价且采用 RAII 智能指针管理资源。具体编译和执行过程可参考编译与运行样例。#include iostream #include vector #include acl/acl.h #include aclnnop/acl_stft.h #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) { // 固定写法资源初始化 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() { // 1.固定写法device/stream初始化参考acl对外接口列表 // 根据自己的实际device填写deviceId int32_t deviceId 0; aclrtStream stream; auto ret Init(deviceId, stream); // check根据自己的需要处理 CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(Init acl failed. ERROR: %d\n, ret); return ret); // 2.构造输入与输出需要根据API的接口自定义构造 std::vectorint64_t selfShape {5}; std::vectorint64_t windowShape {4}; std::vectorint64_t outShape {3, 1, 2}; void* selfDeviceAddr nullptr; void* windowDeviceAddr nullptr; void* outDeviceAddr nullptr; aclTensor* self nullptr; aclTensor* window nullptr; aclTensor* out nullptr; std::vectorfloat selfHostData {1, 6, 8, 5, 7}; std::vectorfloat windowHostData {1, 1, 1, 1}; std::vectorfloat outHostData {0, 0, 0, 0, 0, 0}; // 创建self aclTensor ret CreateAclTensor(selfHostData, selfShape, selfDeviceAddr, aclDataType::ACL_FLOAT, self); CHECK_RET(ret ACL_SUCCESS, return ret); // 创建window aclTensor ret CreateAclTensor(windowHostData, windowShape, windowDeviceAddr, aclDataType::ACL_FLOAT, window); CHECK_RET(ret ACL_SUCCESS, return ret); // 创建out aclTensor ret CreateAclTensor(outHostData, outShape, outDeviceAddr, aclDataType::ACL_FLOAT, out); CHECK_RET(ret ACL_SUCCESS, return ret); int n_fft 4; int hop_length 2; int win_length 4; bool normalized false; bool onesided true; bool returnComplex false; // 3.调用CANN算子库API需要修改为具体的API名称 uint64_t workspaceSize 0; aclOpExecutor* executor; // 调用aclStft第一段接口 ret aclStftGetWorkspaceSize( self, window, out, n_fft, hop_length, win_length, normalized, onesided, returnComplex, workspaceSize, executor); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclStftGetWorkspaceSize 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); } // 调用aclStft第二段接口 ret aclStft(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclStft 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); std::vectorfloat 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: %f\n, i, resultData[i]); } // 6.释放aclTensor需要根据具体API的接口定义修改 aclDestroyTensor(self); aclDestroyTensor(window); aclDestroyTensor(out); // 7.释放device资源需要根据具体API的接口定义修改 aclrtFree(selfDeviceAddr); aclrtFree(windowDeviceAddr); aclrtFree(outDeviceAddr); if (workspaceSize 0) { aclrtFree(workspaceAddr); } aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); return 0; }8.1 示例参数与输出 shape 的对应验证示例中selfShape {5}L5、n_fft 4、hop_length 2、win_length 4、onesided true、returnComplex false则频率维 N n_fft // 2 1 3窗口数 T (5 - 4) // 2 1 1returnComplexFalse故 out 的 shape 为 [N, T, 2] [3, 1, 2]与代码中的outShape {3, 1, 2}完全吻合。调用流程可归纳为 7 步固定套路① acl 环境初始化与 stream 创建② 构造 self/window/out 三个 aclTensorhost→device 拷贝 aclCreateTensor③ 第一段接口求 workspaceSize 与 executor并按需aclrtMallocworkspace④ 第二段接口aclStft异步下发⑤aclrtSynchronizeStream同步等待⑥aclrtMemcpy回拷结果并打印⑦ 依次释放 tensor、device 内存、workspace、stream 并aclFinalize。九、源码级实现剖析9.1 第一段接口的整体流程aclStftGetWorkspaceSize的执行顺序对应 math/stft/op_host/op_api/acl_stft.cppL2_DFX_PHASE_1记录入参self、windowOptional、nFft、hopLength、winLength、normalized、onesided、returnComplex与输出 out用于问题定位CREATE_EXECUTOR()创建 OpExecutorCheckPlatform()平台校验仅 910B/910_93/950CheckParams()完成空指针、dtype、format、shape 四类校验空 Tensor 处理self为空时直接返回workspaceSize 置 0通过l0op::Contiguous将 self 转为连续 Tensor路径选择调用l0op::IsStftAiCoreSupported(...)判断走 AiCore 高性能路径还是 AiCpu 兜底路径构建计算图详见 9.2最后通过uniqueExecutor-GetWorkspaceSize()汇总 workspace 大小并ReleaseTo(executor)返回。9.2 AiCore 与 AiCpu 双路径从源码日志分支可以清晰看到两条路径AiCpu 路径pathAiCpu直接以l0op::Stft构造算子计算再接l0op::ViewCopy将结果搬运到 out。用于不支持 AiCore 加速的 shape/类型组合AiCore 路径pathAiCore以矩阵乘法为核心流程为若winLength nFft准确说是小于按 block 对齐后的nFftAlign调用GeneratePadWindow用 PadV3conversion/pad_v3算子constant 模式、pad value0把窗函数补齐到 nFft 长度若调用方未传 window则先用l0op::OnesLike生成全 1 窗调用GenerateDftMatrix生成旋转因子矩阵K × nFftK onesided ? nFft/21 : nFft实部/虚部按频率行交错排布为 [2, K, nFft_align]组合l0op::Mul把窗函数乘入 DFT 矩阵与l0op::Stft完成分帧矩阵与 DFT 矩阵的乘加最终l0op::ViewCopy落盘 out。9.3 DFT 矩阵缓存DftMatrixCache由于旋转因子矩阵只由 K、nFft 与对齐方式决定acl_stft.cpp实现了基于 LRU 的 DFT 矩阵缓存缓存键为{K, nFft, nfftAlignBytes, deviceId}四元组注释明确指出该 4 字段键可完整覆盖 8 个入参组合的所有场景hopLength/winLength/normalized/onesided/returnComplex 只通过nfftAlignBytes间接影响矩阵布局缓存预算默认 8GBDEFAULT_DFT_CACHE_MAX_MEMORY注释说明可覆盖 nFft 到 32768 的所有常见尺度单矩阵超过预算 50% 时不缓存避免大矩阵挤占小矩阵命中缓存时通过SetStorageAddr复用已有显存并AbandonCache矩阵构造通过八分圆octant对称法计算 cos/sin避免大范围三角函数计算。从工程视角看这对语音/音频类高频场景同一 nFft 反复调用可显著减少重复建矩阵的开销。9.4 Kernel 侧实现分帧 MatmulKernel 入口stft见 math/stft/op_kernel/stft.cpp依据 tiling key 分发到 4 种实现Tiling Key实现输入类型0STFT_PERFORMANCE_IMPLStftND特化性能路径float1STFT_GENERALIZED_IMPL通用路径含 plan 与 window 相乘float2STFT_GENERALIZED_COMPLEX_IMPL复数通用路径float3STFT_GENERALIZED_IMPLhalf以StftNDmath/stft/op_kernel/stft.h为例其核心思路是 AIV/AIC 协同AIVVector 核SplitFrameNormal按 hop 步进把输入切分为blkFrame × nfft的帧矩阵写入 workspace分帧窗口重叠拷贝随后把 Matmul 输出的实部/虚部通过Gather指令交错拼成复数排列写回输出AICCube 核以分帧矩阵 × DFT 矩阵为 Matmul 主体M 为频率行/窗函数行N 为帧数K 为 nFft通过Mmad/Fixpipe完成累加与落盘采用 L1/A0/B0/C0 多级流水与双缓冲baseM96、baseK80、N 基本块 96 等经验参数提升吞吐。9.5 Tiling 与算子定义Tiling 入口在 math/stft/op_host/stft_tiling.cpp通过Math::OpTiling::TilingRegistry分派具体 tiling 实现TilingPrepare4STFT在编译期采集核数AIV/AIC、UB 内存大小、系统 workspace 等平台信息算子定义在 math/stft/op_host/stft_def.cpp输入为x必填、plan必填即 DFT 矩阵/plan、window可选输出y并开启了动态 rank、动态 shape、dynamic compile、precision reduce 等特性由于 aclnn 接口内部使用 l0op 拼装计算图Contiguous/PadV3/Mul/Stft/ViewCopy用户侧只需面对aclStft一个入口复杂度被完全封装。十、测试与验证仓库为 aclStft 提供了完整的测试覆盖可作为功能与正确性验证的参考算子级 ST 测试math/stft/tests/st/aclStft/atk_aclStft.json 以torch.stft为基准name: torch.stftaclnn_name: aclStft覆盖数十组用例输入 shape 为 [16, 约 10 万23 万] 的长序列、FLOAT32、nFft400、hopLength160、winLength400、onesidedtrue、returnComplexfalse数值范围 [-10.0, 10.0]精度标准为cv_fused_double_benchmark。这组参数恰与源码中NfftAlignBytes的特化分支nFft400、hop160、onesided、非复数走 32B block 对齐的 X1 性能路径对应Host 侧 UTmath/stft/tests/ut/op_host/op_api/test_aclnn_stft.cpp 验证 aclnn 接口调用math/stft/tests/ut/op_host/test_stft_infershape.cpp 与 math/stft/tests/ut/op_host/test_stft_tiling.cpp 分别验证 shape 推导与 tiling 计算Kernel 侧 UTmath/stft/tests/ut/op_kernel/test_stft.cpp 配合stft_tiling.h验证 kernel 计算正确性可直接编译运行的示例math/stft/examples/test_aclnn_stft.cpp 即第八节代码的完整 RAII 版本适合作为上板验证的最小可运行工程。十一、相关参考本文核心文档math/stft/docs/aclStft.md算子级说明与参数表math/stft/README.mdaclnn 对外头文件math/stft/op_host/op_api/acl_stft.h接口实现与 DFT 矩阵缓存math/stft/op_host/op_api/acl_stft.cppKernel 实现math/stft/op_kernel/stft.cpp、math/stft/op_kernel/stft.h算子定义与 IRmath/stft/op_host/stft_def.cpp、math/stft/op_graph/stft_proto.h通用概念两段式接口、aclnn 返回码、编译与运行样例【免费下载链接】ops-math本项目是CANN提供的数学类基础计算算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-math创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表