带袖口的实数到复数 FFT
In place real to complex FFT with cufft
我正在尝试使用袖带执行就地实数到复数 FFT。
我知道类似的问题 。但是我在尝试重现相同方法时遇到问题。
如果我做了一个不合适的转换,没有问题,但是一旦我做了,我在 FFT 中就没有正确的值(使用 python 检查,使用二进制之间的文件)。我没有错误,只是值不正确。
这是我的代码:
void fftCuda2d(mat3d* scene)
{
cufftResult resultStatus;
cudaError_t cuda_status;
cufftHandle plan_forward;
resultStatus = cufftPlan2d(&plan_forward, scene->_height, scene->_width, CUFFT_R2C);
cout << "Creating plan forward: " << _cudaGetErrorEnum(resultStatus) << endl;
cufftComplex *d_fft, *d_scene, *h_fft;
size_t size_fft = (int(scene->_width/2)+1)*scene->_height;
cudaMalloc((void**)&d_scene, sizeof(cufftComplex)*size_fft);
cudaMalloc((void**)&d_fft, sizeof(cufftComplex)*size_fft);
h_fft = (cufftComplex*) malloc(sizeof(cufftComplex)*size_fft);
cuda_status = cudaMemcpy(d_scene, scene->_pData, sizeof(cufftReal) * scene->_height * scene->_width, cudaMemcpyHostToDevice);
resultStatus = cufftExecR2C(plan_forward, (cufftReal*) d_scene, d_scene);
cuda_status = cudaMemcpy(h_fft, d_scene, sizeof(cufftReal)*scene->_height*scene->_width, cudaMemcpyDeviceToHost);
FILE* *pFileTemp;
pFileTemp = fopen("temp.bin", "wb");
check = fwrite(h_fft, sizeof(cufftComplex), sizeFft, pFileTemp);
}
如果我使用 resultStatus = cufftExecR2C(plan_forward, (cufftReal*) d_scene, d_fft);
并保存 d_fft
的输出,我会得到正确的结果。
所以你看到我这里有什么错误了吗?
P.S Mat3d 是一个结构,其中 _width 和 _height 包含矩阵的大小,pData 是指向数据的指针,但这没有问题。
(看起来这应该是一个重复的问题,但我找不到重复的问题。)
使用就地转换时,您的输入数据需要以不同方式组织(填充)。这在 2D 情况下尤为明显,因为每一行数据都必须填充。
在非就地R2C变换中,输入数据为实值且大小为高度*宽度(例如R=4,C=4的情况):
X X X X
X X X X
X X X X
X X X X
以上数据正好占据16*sizeof(cufftReal)
(假设float
输入数据,维度R = 4,C = 4),它会在内存中以这种方式组织,线性,没有间隙。但是,当我们切换到就地转换时,输入缓冲区的大小会发生变化。这种大小的变化对数据排列有影响。具体来说,输入缓冲区的大小是R*(C/2 + 1)*sizeof(cufftComplex)
。对于 R=4,C=4 的例子,即 12*sizeof(cufftComplex)
或 24*sizeof(cufftReal)
,但它仍然组织为 4 行数据。因此,每行的长度为 6(如果以 cufftReal
测量)或 3(如果以 cufftComplex
测量)。将其视为cufftReal
,那么当我们创建输入数据时,我们必须这样组织它:
X X X X P P
X X X X P P
X X X X P P
X X X X P P
其中 P
位置是 "padding" 数据,而不是您的输入数据。如果我们在内存中线性查看它,它看起来像:
X X X X P P X X X X P P X X X X P P X X X X P P
That is the expectation/requirement of CUFFT(我相信 FFTW 也一样)。但是,由于您未对存储数据的方式进行任何更改,因此您提供的数据如下所示:
X X X X X X X X X X X X X X X X P P P P P P P P
这两种模式的不同是导致结果输出不同的原因。有多种方法可以解决此问题。我将选择演示如何使用 cudaMemcpy2D
在就地情况下填充设备输入缓冲区,这将为我们提供所需的模式。这可能不是 best/fastest 方式,具体取决于您的应用程序需要。
您也没有将正确大小的结果数据从设备复制回主机。
这是一个固定的例子:
$ cat t1589.cu
#include <cufft.h>
#include <iostream>
#include <cstdlib>
struct mat3d{
int _width;
int _height;
cufftReal *_pData;
};
void fftCuda2d(mat3d* scene)
{
cufftResult resultStatus;
cudaError_t cuda_status;
cufftHandle plan_forward;
resultStatus = cufftPlan2d(&plan_forward, scene->_height, scene->_width, CUFFT_R2C);
std::cout << "Creating plan forward: " << (int)resultStatus << std::endl;
cufftComplex *d_fft, *d_scene, *h_fft;
size_t size_fft = (int(scene->_width/2)+1)*scene->_height;
cudaMalloc((void**)&d_scene, sizeof(cufftComplex)*size_fft);
cudaMalloc((void**)&d_fft, sizeof(cufftComplex)*size_fft);
h_fft = (cufftComplex*) malloc(sizeof(cufftComplex)*size_fft);
#ifdef USE_IP
cuda_status = cudaMemcpy2D(d_scene, ((scene->_width/2)+1)*sizeof(cufftComplex), scene->_pData, (scene->_width)*sizeof(cufftReal), sizeof(cufftReal) * scene->_width, scene->_height, cudaMemcpyHostToDevice);
resultStatus = cufftExecR2C(plan_forward, (cufftReal*) d_scene, d_scene);
cuda_status = cudaMemcpy(h_fft, d_scene, sizeof(cufftComplex)*size_fft, cudaMemcpyDeviceToHost);
#else
cuda_status = cudaMemcpy(d_scene, scene->_pData, sizeof(cufftReal) * scene->_height * scene->_width, cudaMemcpyHostToDevice);
resultStatus = cufftExecR2C(plan_forward, (cufftReal*) d_scene, d_fft);
cuda_status = cudaMemcpy(h_fft, d_fft, sizeof(cufftComplex)*size_fft, cudaMemcpyDeviceToHost);
#endif
std::cout << "exec: " << (int)resultStatus << std::endl;
for (int i = 0; i < size_fft; i++)
std::cout << h_fft[i].x << " " << h_fft[i].y << ",";
std::cout << std::endl;
}
const int dim = 4;
int main(){
mat3d myScene;
myScene._pData = new cufftReal[dim*dim];
myScene._width = dim;
myScene._height = dim;
for (int i = 0; i < dim*dim; i++) myScene._pData[i] = rand()/(float)RAND_MAX;
fftCuda2d(&myScene);
std::cout << cudaGetErrorString(cudaGetLastError()) << std::endl;
}
$ nvcc -lineinfo -o t1589 t1589.cu -lcufft
t1589.cu(15): warning: variable "cuda_status" was set but never used
$ ./t1589
Creating plan forward: 0
exec: 0
9.71338 0,-0.153554 1.45243,0.171302 0,0.878097 0.533959,0.424595 -0.834714,0.858133 -0.393671,-0.205139 0,-0.131513 -0.494514,-0.165712 0,0.878097 -0.533959,0.0888268 1.49303,0.858133 0.393671,
no error
$ nvcc -lineinfo -o t1589 t1589.cu -lcufft -DUSE_IP
t1589.cu(15): warning: variable "cuda_status" was set but never used
$ ./t1589
Creating plan forward: 0
exec: 0
9.71338 0,-0.153554 1.45243,0.171302 0,0.878097 0.533959,0.424595 -0.834714,0.858133 -0.393671,-0.205139 0,-0.131513 -0.494514,-0.165712 0,0.878097 -0.533959,0.0888268 1.49303,0.858133 0.393671,
no error
$
我正在尝试使用袖带执行就地实数到复数 FFT。
我知道类似的问题
如果我做了一个不合适的转换,没有问题,但是一旦我做了,我在 FFT 中就没有正确的值(使用 python 检查,使用二进制之间的文件)。我没有错误,只是值不正确。
这是我的代码:
void fftCuda2d(mat3d* scene)
{
cufftResult resultStatus;
cudaError_t cuda_status;
cufftHandle plan_forward;
resultStatus = cufftPlan2d(&plan_forward, scene->_height, scene->_width, CUFFT_R2C);
cout << "Creating plan forward: " << _cudaGetErrorEnum(resultStatus) << endl;
cufftComplex *d_fft, *d_scene, *h_fft;
size_t size_fft = (int(scene->_width/2)+1)*scene->_height;
cudaMalloc((void**)&d_scene, sizeof(cufftComplex)*size_fft);
cudaMalloc((void**)&d_fft, sizeof(cufftComplex)*size_fft);
h_fft = (cufftComplex*) malloc(sizeof(cufftComplex)*size_fft);
cuda_status = cudaMemcpy(d_scene, scene->_pData, sizeof(cufftReal) * scene->_height * scene->_width, cudaMemcpyHostToDevice);
resultStatus = cufftExecR2C(plan_forward, (cufftReal*) d_scene, d_scene);
cuda_status = cudaMemcpy(h_fft, d_scene, sizeof(cufftReal)*scene->_height*scene->_width, cudaMemcpyDeviceToHost);
FILE* *pFileTemp;
pFileTemp = fopen("temp.bin", "wb");
check = fwrite(h_fft, sizeof(cufftComplex), sizeFft, pFileTemp);
}
如果我使用 resultStatus = cufftExecR2C(plan_forward, (cufftReal*) d_scene, d_fft);
并保存 d_fft
的输出,我会得到正确的结果。
所以你看到我这里有什么错误了吗?
P.S Mat3d 是一个结构,其中 _width 和 _height 包含矩阵的大小,pData 是指向数据的指针,但这没有问题。
(看起来这应该是一个重复的问题,但我找不到重复的问题。)
使用就地转换时,您的输入数据需要以不同方式组织(填充)。这在 2D 情况下尤为明显,因为每一行数据都必须填充。
在非就地R2C变换中,输入数据为实值且大小为高度*宽度(例如R=4,C=4的情况):
X X X X
X X X X
X X X X
X X X X
以上数据正好占据16*sizeof(cufftReal)
(假设float
输入数据,维度R = 4,C = 4),它会在内存中以这种方式组织,线性,没有间隙。但是,当我们切换到就地转换时,输入缓冲区的大小会发生变化。这种大小的变化对数据排列有影响。具体来说,输入缓冲区的大小是R*(C/2 + 1)*sizeof(cufftComplex)
。对于 R=4,C=4 的例子,即 12*sizeof(cufftComplex)
或 24*sizeof(cufftReal)
,但它仍然组织为 4 行数据。因此,每行的长度为 6(如果以 cufftReal
测量)或 3(如果以 cufftComplex
测量)。将其视为cufftReal
,那么当我们创建输入数据时,我们必须这样组织它:
X X X X P P
X X X X P P
X X X X P P
X X X X P P
其中 P
位置是 "padding" 数据,而不是您的输入数据。如果我们在内存中线性查看它,它看起来像:
X X X X P P X X X X P P X X X X P P X X X X P P
That is the expectation/requirement of CUFFT(我相信 FFTW 也一样)。但是,由于您未对存储数据的方式进行任何更改,因此您提供的数据如下所示:
X X X X X X X X X X X X X X X X P P P P P P P P
这两种模式的不同是导致结果输出不同的原因。有多种方法可以解决此问题。我将选择演示如何使用 cudaMemcpy2D
在就地情况下填充设备输入缓冲区,这将为我们提供所需的模式。这可能不是 best/fastest 方式,具体取决于您的应用程序需要。
您也没有将正确大小的结果数据从设备复制回主机。
这是一个固定的例子:
$ cat t1589.cu
#include <cufft.h>
#include <iostream>
#include <cstdlib>
struct mat3d{
int _width;
int _height;
cufftReal *_pData;
};
void fftCuda2d(mat3d* scene)
{
cufftResult resultStatus;
cudaError_t cuda_status;
cufftHandle plan_forward;
resultStatus = cufftPlan2d(&plan_forward, scene->_height, scene->_width, CUFFT_R2C);
std::cout << "Creating plan forward: " << (int)resultStatus << std::endl;
cufftComplex *d_fft, *d_scene, *h_fft;
size_t size_fft = (int(scene->_width/2)+1)*scene->_height;
cudaMalloc((void**)&d_scene, sizeof(cufftComplex)*size_fft);
cudaMalloc((void**)&d_fft, sizeof(cufftComplex)*size_fft);
h_fft = (cufftComplex*) malloc(sizeof(cufftComplex)*size_fft);
#ifdef USE_IP
cuda_status = cudaMemcpy2D(d_scene, ((scene->_width/2)+1)*sizeof(cufftComplex), scene->_pData, (scene->_width)*sizeof(cufftReal), sizeof(cufftReal) * scene->_width, scene->_height, cudaMemcpyHostToDevice);
resultStatus = cufftExecR2C(plan_forward, (cufftReal*) d_scene, d_scene);
cuda_status = cudaMemcpy(h_fft, d_scene, sizeof(cufftComplex)*size_fft, cudaMemcpyDeviceToHost);
#else
cuda_status = cudaMemcpy(d_scene, scene->_pData, sizeof(cufftReal) * scene->_height * scene->_width, cudaMemcpyHostToDevice);
resultStatus = cufftExecR2C(plan_forward, (cufftReal*) d_scene, d_fft);
cuda_status = cudaMemcpy(h_fft, d_fft, sizeof(cufftComplex)*size_fft, cudaMemcpyDeviceToHost);
#endif
std::cout << "exec: " << (int)resultStatus << std::endl;
for (int i = 0; i < size_fft; i++)
std::cout << h_fft[i].x << " " << h_fft[i].y << ",";
std::cout << std::endl;
}
const int dim = 4;
int main(){
mat3d myScene;
myScene._pData = new cufftReal[dim*dim];
myScene._width = dim;
myScene._height = dim;
for (int i = 0; i < dim*dim; i++) myScene._pData[i] = rand()/(float)RAND_MAX;
fftCuda2d(&myScene);
std::cout << cudaGetErrorString(cudaGetLastError()) << std::endl;
}
$ nvcc -lineinfo -o t1589 t1589.cu -lcufft
t1589.cu(15): warning: variable "cuda_status" was set but never used
$ ./t1589
Creating plan forward: 0
exec: 0
9.71338 0,-0.153554 1.45243,0.171302 0,0.878097 0.533959,0.424595 -0.834714,0.858133 -0.393671,-0.205139 0,-0.131513 -0.494514,-0.165712 0,0.878097 -0.533959,0.0888268 1.49303,0.858133 0.393671,
no error
$ nvcc -lineinfo -o t1589 t1589.cu -lcufft -DUSE_IP
t1589.cu(15): warning: variable "cuda_status" was set but never used
$ ./t1589
Creating plan forward: 0
exec: 0
9.71338 0,-0.153554 1.45243,0.171302 0,0.878097 0.533959,0.424595 -0.834714,0.858133 -0.393671,-0.205139 0,-0.131513 -0.494514,-0.165712 0,0.878097 -0.533959,0.0888268 1.49303,0.858133 0.393671,
no error
$