2012-02-23 2 views
3

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

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 от хоста, и оба имеют «реальные» адреса.

EDIT Извините за поздний ответ на запрос о предоставлении дополнительной информации (= больше кода).

Я тестировал, что указатель 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 -Call в prepareOutputSet.

+1

Наиболее вероятным источником ошибки является 'houtput-> arr', не являющийся допустимым указателем устройства. Можете ли вы немного расширить свой код, чтобы показать, как вы распределяете и копируете содержимое 'd_output' на устройство? – talonmies

+0

'd_output' и его содержимое выделяется на устройстве с помощью' malloc() '. Я уверен, что он содержит фактические данные, поскольку я пытался распечатать содержимое 'd_output-> arr' и получил ожидаемый результат. – ssnielsen

+0

Вы имеете в виду 'h_output' _and its contents_? Потому что 'd_output' не отображается в вашем примере кода. – pQB

ответ

2

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

+0

Приятно слышать это. – ssnielsen

+0

@harrism: У меня такой же код ошибки. При дальнейшем расследовании я обнаружил, что оставалось недостаточно памяти для копирования данных обратно в CPU. Таким образом, 'cudaMemcpy' терпел неудачу. Является ли это действительной причиной для кода ошибки или я ошибаюсь? – Programmer

+0

Это другая проблема, чем описано здесь. – harrism

-1

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

По крайней мере, это то, что похоже на код, который вы предоставили.

+0

Это не совсем то, что делает код вообще. Это должно быть прекрасно, но * только *, если 'h_output-> arr' (и по расширению' d_output-> arr', который является исходной памятью) содержат допустимые указатели на устройства. – talonmies

+0

Я не уверен, что я следую. 'h_output' выделяется' malloc() '. Затем 'cudaMemcpy()' используется для копирования в него, используя его как указатель узла. Затем (без установки 'h_output-> arr', кроме того, что он был установлен с предыдущего вызова, на' cudaMemcpy() ')' cudaMemcpy() 'снова вызывается с помощью' h_output-> arr' в качестве указателя устройства. И кажется, что вы комментируете этот вопрос вокруг вопроса. –

+0

После вызова 'cudaMemcpy()' h_output' фактически указывает на копию структуры памяти устройства, на которую указывает 'd_output'. Если 'd_output-> arr' был допустимым указателем устройства, то после копии будет' h_output-> arr'. – talonmies

0

Ошибка, которую вы видите, почти наверняка вызвана тем, что 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

Оригинальный вопрос теперь обновляется с большим количеством кода. Надеюсь, это проливает свет на проблему. – ssnielsen

+0

Этот дополнительный код, который вы опубликовали, не меняет вывод этого ответа - он ясно показывает, что если соответствующий указатель является допустимым указателем устройства, исходный код API, который вы опубликовали, исправлен.Реальный вопрос в том, почему ваш указатель malloced вашего устройства недействителен к моменту его возвращения на хост. – talonmies

0

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

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

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

Смежные вопросы