Как скопировать 2D-массив с устройства на хост в Cuda?

0

Я хочу скопировать результаты операции 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.

Итак, как я могу это решить?

  • 1
    нет проверки ошибок? API-интерфейсы CUFFT и CUDA имеют ошибки, которые могут предоставить полезную информацию.
  • 0
    @RobertCrovella спасибо за совет. Я проверил ошибки, и ошибка в 'cudaMemcpy (c, data, sizeof (float2) * NX * NY, cudaMemcpyDeviceToHost); " и ошибка "неверный аргумент".
Показать ещё 4 комментария
Теги:
memory
cuda
fft

1 ответ

1
Лучший ответ

Проблема, описанная в заголовке вопроса, была исправлена путем внесения изменений, которые я описал в комментариях. После этого у вашего кода были другие проблемы, не связанные с копией результатов.

Вы просите преобразование 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, чтобы описать их.

  • 0
    Спасибо за ответ. Как вы сказали, он печатает значения не NAN. Но результаты неверны. Я постараюсь посмотреть, что я могу сделать, и если мне это не удастся, я отправлю новый вопрос. Относительно NY + 2, поскольку входное значение равно NX * (NY / 2 + 1) * sizeof (cufftComplex) --- [или NX NY sizeof (float) - это размер для хранения массива NX x NY] Я умножил выходной размер на 2. Таким образом, NY / 2 + 1 стал NY + 2.
  • 0
    Я думаю, что оставшиеся проблемы связаны с тем, как вы заполняете входные данные. Вы помещаете неравное число (NY = 5) значений с float cufftComplex массива cufftComplex . Мне это дало бы странные результаты. Первые два комплексных значения в каждой строке ( data ) будут иметь действительные и комплексные компоненты 2. Третье значение будет иметь действительный компонент 2 и мнимый компонент 0. Все последние два комплексных значения будут равны нулю.
Показать ещё 3 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню