Ошибка неверного аргумента при копировании данных с устройства на хост - PullRequest
3 голосов
/ 23 февраля 2012

У меня проблемы с копированием данных с моего устройства обратно на хост.Мои данные организованы в виде структуры:

typedef struct Array2D {
    double* arr;        
    int rows;       
    int cols;       
} Array2D;

arr - это «плоский» массив.rows и cols описывает размеры.

Приведенный ниже код показывает, как я пытаюсь скопировать данные обратно на хост:

h_output = (Array2D*) malloc(sizeof(Array2D));
cudaMemcpy(h_output, d_output, sizeof(Array2D), cudaMemcpyDeviceToHost);
double* h_arr = (double*) malloc(h_output->cols*h_output->rows*sizeof(double));
cudaMemcpy(h_arr, h_output->arr, h_output->cols*h_output->rows*sizeof(double), cudaMemcpyDeviceToHost);
h_output->arr = h_arr;

Однако в четвертой строкевыполнение завершается с ошибкой cuda 11 (неверный аргумент).Я не могу понять, почему это происходит.Размер массива правильный, и я могу получить доступ к h_output и h_array с хоста, и у обоих есть «реальные» адреса.

РЕДАКТИРОВАТЬ Извините за поздний ответ на запрос дополнительной информации (= больше кода).

Я проверил, что указатель d_output->arr является указателем устройства, пытаясь получить доступ к значению указателя устройства на хосте.Как и ожидалось, мне не позволили сделать это, заставив меня подумать, что d_output->arr на самом деле является действительным указателем устройства.

Цель кода - решить дифференциальное уравнение Тиле, используя метод Рунге-Кутты четвертого порядка.,

class CalculationSpecification
{

    /* FUNCTIONS OMITTED */

public:
    __device__ void RK4_n(CalculationSpecification* cs, CalcData data, Array2D* d_output)
    {
        double* rk4data = (double*)malloc((data.pdata->endYear - data.pdata->startYear + 1)*data.pdata->states*sizeof(double));

        /* CALCULATION STUFF HAPPENS HERE */

        // We know that rows = 51, cols = 1 and that rk4data contains 51 values as it should.
        // This was confirmed by using printf directly in this function.
        d_output->arr = rk4data;
        d_output->rows = data.pdata->endYear - data.pdata->startYear + 1;
        d_output->cols = data.pdata->states;
    }
};


class PureEndowment : CalculationSpecification
{
    /* FUNCTIONS OMITTED */

public:
    __device__ void Compute(Array2D *result, CalcData data)
    {
        RK4_n(this, data, result);
    }
};


__global__ void kernel2(Array2D *d_output)
{
    /* Other code that initializes 'cd'. */
    PureEndowment pe;
    pe.Compute(d_output,cd);
}


void prepareOutputSet(Array2D* h_output, Array2D* d_output, int count)
{
    h_output = (Array2D*) malloc(sizeof(Array2D));
    cudaMemcpy(h_output, d_output, sizeof(Array2D), cudaMemcpyDeviceToHost); // After this call I can read the correct values of row, col as well as the address of the pointer.
    double* h_arr = (double*) malloc(h_output->cols*h_output->rows*sizeof(double));
    cudaMemcpy(h_arr, h_output->arr, h_output->cols*h_output->rows*sizeof(double), cudaMemcpyDeviceToHost)
    h_output->arr = h_arr;
}

int main()
{
    Array2D *h_output, *d_output;
    cudaMalloc((void**)&d_output, sizeof(Array2D));

    kernel2<<<1,1>>>(d_output);
    cudaDeviceSynchronize();

    prepareOutputSet(h_output, d_output, 1);

    getchar();
    return 0;
}

EDIT2

Кроме того, теперь я проверил, что значение d_output->arr при работе на устройстве идентичнозначение h_output->arr после первого cudaMemcpy -звука в prepareOutputSet.

Ответы [ 4 ]

2 голосов
/ 06 марта 2012

Это (копирование выделенной устройству памяти с помощью cudaMemcpy) является известным ограничением в CUDA 4.1.Исправление находится в разработке и будет выпущено в будущей версии среды выполнения CUDA.

0 голосов
/ 24 февраля 2012

Ошибка, которую вы видите, почти наверняка вызвана тем, что h_output->arr не является действительным указателем устройства, или h_output->rows или h_output->cols имеют неправильные значения.Вы решили не показывать код, объясняющий, как было установлено содержимое исходной памяти d_output, поэтому невозможно точно сказать, что является основной причиной вашей проблемы.

Чтобы проиллюстрировать это, вот полная, запускаемая демонстрация, показывающая опубликованный код в действии:

#include <cstdlib>
#include <cstdio>

inline void GPUassert(cudaError_t code, char * file, int line, bool Abort=true)
{
    if (code != 0) {
        fprintf(stderr, "GPUassert: %s %s %d\n", cudaGetErrorString(code),file,line);
        if (Abort) exit(code);
    }       
}

#define GPUerrchk(ans) { GPUassert((ans), __FILE__, __LINE__); }

typedef float Real;

typedef struct Array2D {
    Real* arr;        
    int rows;       
    int cols;       
} Array2D;

__global__ void kernel(const int m, const int n, Real *lval, Array2D *output)
{
    lval[threadIdx.x] = 1.0f + threadIdx.x;
    if (threadIdx.x == 0) {
        output->arr = lval;
        output->rows = m;
        output->cols = n;
    }
}

int main(void)
{
    const int m=8, n=8, mn=m*n;

    Array2D *d_output;
    Real *d_arr;
    GPUerrchk( cudaMalloc((void **)&d_arr,sizeof(Real)*size_t(mn)) ); 

    GPUerrchk( cudaMalloc((void **)&d_output, sizeof(Array2D)) );
    kernel<<<1,mn>>>(m,n,d_arr,d_output);
    GPUerrchk( cudaPeekAtLastError() );

    // This section of code is the same as the original question
    Array2D *h_output = (Array2D*)malloc(sizeof(Array2D));
    GPUerrchk( cudaMemcpy(h_output, d_output, sizeof(Array2D), cudaMemcpyDeviceToHost) );
    size_t sz = size_t(h_output->rows*h_output->cols)*sizeof(Real);
    Real *h_arr = (Real*)malloc(sz);
    GPUerrchk( cudaMemcpy(h_arr, h_output->arr, sz, cudaMemcpyDeviceToHost) );

    for(int i=0; i<h_output->rows; i++)
        for(int j=0; j<h_output->cols; j++)
            fprintf(stdout,"(%d %d) %f\n", i, j, h_arr[j + i*h_output->rows]);

    return 0;
}

Мне пришлось взять здесь несколько свобод, потому что у меня есть только вычисленияВозможности устройства 1.2 в моем распоряжении, поэтому нет стороны устройства malloc и нет двойной точности.Но вызовы API на стороне хоста, которые извлекают действительную структуру Array2D из памяти устройства и используют ее содержимое, практически одинаковы.Запуск программы работает как положено:

$ nvcc -Xptxas="-v" -arch=sm_12 Array2D.cu 
ptxas info    : Compiling entry function '_Z6kerneliiPfP7Array2D' for 'sm_12'
ptxas info    : Used 2 registers, 16+16 bytes smem

$ cuda-memcheck ./a.out 
========= CUDA-MEMCHECK
(0 0) 1.000000
(0 1) 2.000000
(0 2) 3.000000
(0 3) 4.000000
(0 4) 5.000000
(0 5) 6.000000
(0 6) 7.000000
(0 7) 8.000000
(1 0) 9.000000
(1 1) 10.000000
(1 2) 11.000000
(1 3) 12.000000
(1 4) 13.000000
(1 5) 14.000000
(1 6) 15.000000
(1 7) 16.000000
(2 0) 17.000000
(2 1) 18.000000
(2 2) 19.000000
(2 3) 20.000000
(2 4) 21.000000
(2 5) 22.000000
(2 6) 23.000000
(2 7) 24.000000
(3 0) 25.000000
(3 1) 26.000000
(3 2) 27.000000
(3 3) 28.000000
(3 4) 29.000000
(3 5) 30.000000
(3 6) 31.000000
(3 7) 32.000000
(4 0) 33.000000
(4 1) 34.000000
(4 2) 35.000000
(4 3) 36.000000
(4 4) 37.000000
(4 5) 38.000000
(4 6) 39.000000
(4 7) 40.000000
(5 0) 41.000000
(5 1) 42.000000
(5 2) 43.000000
(5 3) 44.000000
(5 4) 45.000000
(5 5) 46.000000
(5 6) 47.000000
(5 7) 48.000000
(6 0) 49.000000
(6 1) 50.000000
(6 2) 51.000000
(6 3) 52.000000
(6 4) 53.000000
(6 5) 54.000000
(6 6) 55.000000
(6 7) 56.000000
(7 0) 57.000000
(7 1) 58.000000
(7 2) 59.000000
(7 3) 60.000000
(7 4) 61.000000
(7 5) 62.000000
(7 6) 63.000000
(7 7) 64.000000
========= ERROR SUMMARY: 0 errors
0 голосов
/ 27 февраля 2012

Я попытался выделить указатель Array2D->arr на хосте, используя cudaMalloc, вместо того, чтобы размещать его на устройстве, используя malloc.После этого код работает как задумано.

Очень похоже на проблему, описанную в теме (http://forums.nvidia.com/index.php?showtopic=222659) на форуме nVidia, на которую Паван ссылался в комментариях к вопросу.

Я думаю, что, вероятно, закрываетсяВопрос на данный момент, так как код работает нормально. Однако, если у кого-то есть предложение по решению, которое использует malloc на устройстве, не стесняйтесь писать.

0 голосов
/ 23 февраля 2012

Похоже, h_output выделяется при вызове malloc().В первом вызове cudaMemcpy() (строка 2), h_output используется как указатель хоста (что кажется правильным).Во втором вызове cudaMemcpy() (строка 4) h_output->arr используется в качестве указателя устройства (что выглядит неправильно).В этой четвертой строке выглядит, как будто вы копируете из памяти хоста в память хоста.Таким образом, вы, вероятно, захотите использовать просто memcpy() вместо cudaMemcpy().

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

...