使用clFFT库执行2D实转复FFT输出全零求助排查
我用clFFT执行2D实到复FFT运算时,输出数组的实部和虚部全为零,但输入数组float *in是正确的(已有基于fftw3的可正常运行版本)。OpenCL初始化流程所有操作均返回CL_SUCCESS,输出数组按厄米对称分配了足够空间,也设置了strides参数,代码如下:
auto cols = (signal_length / 2 + 1); auto rows = receiver_number; float *data_ptr = in; // in 是大小为 receiver_number * signal_length 的float指针 float *out = new float[cols * rows * 2]; // 交错存储复数的实部和虚部(厄米对称) /* 准备OpenCL内存对象并写入数据 */ cl_int err; cl_mem data_ptr_d = clCreateBuffer( context, CL_MEM_READ_WRITE, sizeof(float) * receiver_number * signal_length, NULL, &err ); cl_mem out_d = clCreateBuffer( context, CL_MEM_READ_WRITE, sizeof(float) * cols * rows * 2, NULL, &err ); err = clEnqueueWriteBuffer( queue, data_ptr_d, CL_TRUE, 0, sizeof(float) * receiver_number * signal_length, data_ptr, 0, NULL, NULL); /* 创建FFT默认计划 */ const size_t N0 = receiver_number, N1 = signal_length; clfftPlanHandle forward_plan; size_t clLengthsForward[2] = {N0, N1}; err = clfftCreateDefaultPlan(&forward_plan, context, CLFFT_2D, clLengthsForward); /* 设置输入输出stride */ size_t clStridesForwardIn[2] = {1, (unsigned long)signal_length}; size_t clStridesForwardOut[2] = {1, (unsigned long)cols}; err = clfftSetPlanInStride(forward_plan, CLFFT_2D, clStridesForwardIn); err = clfftSetPlanOutStride(forward_plan, CLFFT_2D, clStridesForwardOut); err = clfftSetPlanDistance(forward_plan, receiver_number * signal_length, cols * rows * 2); /* 设置计划参数 */ err = clfftSetPlanPrecision(forward_plan, CLFFT_SINGLE); err = clfftSetLayout(forward_plan, CLFFT_REAL, CLFFT_HERMITIAN_INTERLEAVED); err = clfftSetResultLocation(forward_plan, CLFFT_OUTOFPLACE); // 输出到独立数组 /* 固化计划 */ err = clfftBakePlan(forward_plan, 1, &queue, NULL, NULL); /* 执行变换 */ err = clfftEnqueueTransform(forward_plan, CLFFT_FORWARD, 1, &queue, 0, NULL, NULL, &data_ptr_d, &out_d, NULL); /* 等待计算完成 */ err = clFinish(queue); /* 读取结果 */ err = clEnqueueReadBuffer( queue, out_d, CL_TRUE, 0, sizeof(float)* cols * rows * 2, out, 0, NULL, NULL );
所有内存分配、传输及内核执行调用均返回CL_SUCCESS,但输出全零,以下是关键排查点和修复方案:
1. 维度顺序与clFFT存储模型不匹配
clFFT的2D维度遵循最快变化维度在前的规则,而你的输入是行优先存储(receiver_number行,每行signal_length个元素),最快变化的维度是列数(signal_length),最慢变化的是行数(receiver_number)。
当前代码将clLengthsForward设为{receiver_number, signal_length},把行数放在第一个维度,会导致clFFT对输入数组的维度解析错误。
修复:调整维度顺序为列数在前,行数在后:
size_t clLengthsForward[2] = {signal_length, receiver_number};
2. 输出stride参数错误
输出格式为CLFFT_HERMITIAN_INTERLEAVED,每个复数由两个连续float组成(实部+虚部),以float为单位的stride应满足:
- 同一行内,相邻复数的起始位置间隔为2(跳过当前复数的两个元素)
- 行之间的间隔为
cols * 2(一行所有复数的总float元素数)
当前代码中clStridesForwardOut = {1, cols}会导致clFFT错误访问输出缓冲区,最终输出全零。
修复:修正输出stride:
size_t clStridesForwardOut[2] = {2, cols * 2};
3. 适配维度顺序的输入stride验证
调整维度顺序后,输入stride{1, signal_length}是正确的(同一行内元素间隔1,行之间间隔一行的总元素数),无需修改,但要确保和新的维度顺序匹配。
4. 简化Plan的distance参数
clfftSetPlanDistance用于批量变换时指定连续数据集的间隔,单变换场景下可直接设为0避免歧义:
err = clfftSetPlanDistance(forward_plan, 0, 0);
修复后的核心代码片段
/* 创建FFT默认计划:调整维度顺序 */ const size_t N0 = signal_length, N1 = receiver_number; clfftPlanHandle forward_plan; size_t clLengthsForward[2] = {N0, N1}; err = clfftCreateDefaultPlan(&forward_plan, context, CLFFT_2D, clLengthsForward); /* 设置输入输出stride:修正输出stride */ size_t clStridesForwardIn[2] = {1, (unsigned long)signal_length}; size_t clStridesForwardOut[2] = {2, cols * 2}; err = clfftSetPlanInStride(forward_plan, CLFFT_2D, clStridesForwardIn); err = clfftSetPlanOutStride(forward_plan, CLFFT_2D, clStridesForwardOut); /* 单变换场景设置distance为0 */ err = clfftSetPlanDistance(forward_plan, 0, 0);
内容的提问来源于stack exchange,提问作者mr_jnrdve

