Я хочу скопировать результаты операции fft с устройства на хост.
Вот что происходит. Ввод представляет собой указатель на указатель на float. значения распределяются во время выполнения. то он переносится в gpu и вычисляется fft. Затем результаты передаются в массив float2 2D. Но результат, который я получаю, ошибочен. Он содержит все ноль. Итак, как я могу преодолеть эту проблему?
#define NRANK 2
#define BATCH 10
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include <cufft.h>
#include <stdio.h>
#include <iostream>
#include <vector>
using namespace std;
float func(int,int){
return 2.0f; // some value get return. I have put a dummy value here
}
int main()
{
const size_t NX = 4;
const size_t NY = 5;
// Input array - host side
float **a = new float*[NX];
for (int r = 0; r < NX; ++r) // this can be also done on GPU
{
a[r] = new float[NY];
for (int c = 0; c < NY; ++c)
{
a[r][c] = func(r,c);
}
}
// Output array - host side
float2 c[NX][NY] = { 0 };
cufftHandle plan;
cufftComplex *data; // Input and output arrays - device side
int n[NRANK] = {NX, NY};
// Transfer the data from host to device - have to do it like this becase
// the array is a dynamic array
cudaMalloc((void**)&data, sizeof(cufftComplex)*NX*(NY/2+1));
for(int i=0; i<NX; ++i){
cudaMemcpy(reinterpret_cast<float*>(data) + i*NY, a[i], sizeof(float)*NY,
cudaMemcpyHostToDevice);
}
/* Create a 2D FFT plan. */
cufftPlanMany(&plan, NRANK, n,NULL, 1, 0,NULL, 1, 0,CUFFT_C2C,BATCH);
cufftSetCompatibilityMode(plan, CUFFT_COMPATIBILITY_NATIVE);
cufftExecC2C(plan, data, data, CUFFT_FORWARD);
cudaThreadSynchronize();
cudaMemcpy(c, data, sizeof(float2)*NX*NY, cudaMemcpyDeviceToHost);
// Print the values of c ---- ALL ARE 0
for (int i = 0; i < NX; i++)
{
for (int j =0 ; j< NY; j++)
{
printf(" %f + %fi ",c[i][j].x,c[i][j].y);
b
}
printf("\n");
}
cufftDestroy(plan);
cudaFree(data);
return 0;
}
Как я могу решить эту проблему?
Рассмотрев предложение Роберта Кровеллы, я изменил код как
// Output array - host side
float2 c[NX][NY + 2] ;
// New device side variable that will hold the result from the FFT size - twice as input {2 x NX*(NY/2 + 1)}
cufftComplex *data_out;
cudaMalloc((void**)&data_out, sizeof(cufftComplex)*NX*(NY+2));
/* Create a 2D FFT plan. */
cufftPlanMany(&plan, NRANK, n,NULL, 1, 0,NULL, 1, 0,CUFFT_C2C,BATCH);
cufftSetCompatibilityMode(plan, CUFFT_COMPATIBILITY_NATIVE);
cufftExecC2C(plan, data, data_out, CUFFT_FORWARD);
cudaThreadSynchronize();
cudaError cudaStat2 = cudaMemcpy(c, data_out, sizeof(cufftComplex)*NX*(NY+2) , cudaMemcpyDeviceToHost);
cout << cudaGetErrorString(cudaStat2) << " ,\n";
for (int i = 0; i < NX; i++)
{
for (int j =0 ; j< NY; j++)
{
printf(" %f ,",c[i][j].x);
}
printf("\n");
}
Теперь матрица устройства вывода 2 x sizeof (cufftComplex) NX (NY/2 + 1), и я объявила ее как data_out. Затем матрицу стороны узла также можно было отрегулировать так, чтобы удерживать элементы NX * (NY + 2) float2. Теперь я не получаю никаких ошибок от cudaMemcpy. Но я не получаю ответа. Я получаю массив значений 1. # QNAN0.
Итак, как я могу это решить?
Проблема, описанная в заголовке вопроса, была исправлена путем внесения изменений, которые я описал в комментариях. После этого у вашего кода были другие проблемы, не связанные с копией результатов.
Вы просите преобразование C2C размера NX*NY
, но ваш размер входных данных - только sizeof(cufftComplex)*NX*(NY/2+1)
. Когда я исправляю множество проблем вокруг ваших входных данных и размера, я начинаю получать результаты, которые не являются NAN в коде.
Кроме того, мне непонятно, почему вы выделяете размер (NY+2)
в разных местах. Когда я исправляю эти ошибки, я могу получить какой-то результат (не-NAN) из вашего кода:
$ cat t311.cu
#define NRANK 2
#define BATCH 10
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include <cufft.h>
#include <stdio.h>
#include <iostream>
#include <vector>
#define cudaCheckErrors(msg) \
do { \
cudaError_t __err = cudaGetLastError(); \
if (__err != cudaSuccess) { \
fprintf(stderr, "Fatal error: %s (%s at %s:%d)\n", \
msg, cudaGetErrorString(__err), \
__FILE__, __LINE__); \
fprintf(stderr, "*** FAILED - ABORTING\n"); \
exit(1); \
} \
} while (0)
using namespace std;
float func(int,int){
return 2.0f; // some value get return. I have put a dummy value here
}
int main()
{
const size_t NX = 4;
const size_t NY = 5;
// Input array - host side
float **a = new float*[NX];
for (int r = 0; r < NX; ++r) // this can be also done on GPU
{
a[r] = new float[NY];
for (int c = 0; c < NY; ++c)
{
a[r][c] = func(r,c);
}
}
// Output array - host side
float2 c[NX][NY] ;
cufftHandle plan;
cufftComplex *data; // Input and output arrays - device side
int n[NRANK] = {NX, NY};
cudaMalloc((void**)&data, sizeof(cufftComplex)*NX*NY);
cudaMemset(data,0, sizeof(cufftComplex)*NX*NY);
for(int i=0; i<NX; ++i){
cudaMemcpy(reinterpret_cast<float*>(data) + i*NY, a[i], sizeof(float)*NY,cudaMemcpyHostToDevice);
cudaCheckErrors("cudaMemcpy H2D fail");
}
// New device side variable that will hold the result from the FFT size - twice as input {2 x NX*(NY/2 + 1)}
cufftComplex *data_out;
cudaMalloc((void**)&data_out, sizeof(cufftComplex)*NX*(NY));
cudaCheckErrors("cudaMalloc data_out fail");
/* Create a 2D FFT plan. */
if ((cufftPlanMany(&plan, NRANK, n,NULL, 1, 0,NULL, 1, 0,CUFFT_C2C,BATCH)) != CUFFT_SUCCESS) printf("cufft fail 1\n");
if ((cufftSetCompatibilityMode(plan, CUFFT_COMPATIBILITY_NATIVE)) != CUFFT_SUCCESS) printf("cufft fail 2\n");
if ((cufftExecC2C(plan, data, data_out, CUFFT_FORWARD)) != CUFFT_SUCCESS) printf("cufft fail 3\n") ;
cudaDeviceSynchronize();
cudaMemcpy(c, data_out, sizeof(cufftComplex)*NX*(NY) , cudaMemcpyDeviceToHost);
cudaCheckErrors("cudaMemcpy D2H fail");
for (int i = 0; i < NX; i++)
{
for (int j =0 ; j< NY; j++)
{
printf(" %f ,",c[i][j].x);
}
printf("\n");
}
cufftDestroy(plan);
cudaFree(data);
cudaCheckErrors("some error");
return 0;
}
$ nvcc -arch=sm_20 -o t311 t311.cu -lcufft
$ ./t311
20.000000 , 0.000000 , 0.000000 , 0.000000 , 0.000000 ,
20.000000 , 0.000000 , 0.000000 , 0.000000 , 0.000000 ,
0.000000 , 0.000000 , 0.000000 , 0.000000 , 0.000000 ,
0.000000 , 0.000000 , 0.000000 , 0.000000 , 0.000000 ,
$
Я не говорю, что это исправляет все возможные проблемы или ошибки, которые может иметь этот код, но были устранены первые две проблемы, которые вы определили.
Я думаю, что оставшиеся проблемы связаны с тем, как вы заполняете входные данные. Вы помещаете неравномерное число (NY = 5) значений float, расположенных поверх массива cufftComplex. Для меня это принесло бы странные результаты. Первые два комплексных значения в каждой строке (данных) будут иметь реальные и сложные компоненты 2. Третье значение будет иметь реальную составляющую 2 и мнимую составляющую 0. Последние два комплексных значения будут равны нулю.
Если вы хотите увидеть один из возможных методов для копирования массива значений float
в реальные части массива сложных значений с помощью одного вызова API, рассмотрите cudaMemcpy2D
, описанный здесь, и с недавним примером здесь. Этот пример на самом деле показывает, как копировать из массива структуры в массив с float
, но выполнение обратного (массив float
для структуры массива) использует подобный метод. Что-то вроде этого должно работать:
for(int i=0; i<NX; ++i){
cudaMemcpy2D(data + i*NY, sizeof(cufftComplex), a[i], sizeof(float), sizeof(float), NY, cudaMemcpyHostToDevice);
}
Если у вас есть новые вопросы/новые проблемы, отправьте новый вопрос SO, чтобы описать их.
float
cufftComplex
массива cufftComplex
. Мне это дало бы странные результаты. Первые два комплексных значения в каждой строке ( data
) будут иметь действительные и комплексные компоненты 2. Третье значение будет иметь действительный компонент 2 и мнимый компонент 0. Все последние два комплексных значения будут равны нулю.