Ядра CUDA последовательно возвращают плохие результаты - PullRequest
1 голос
/ 14 февраля 2011

Я новичок в CUDA, который успешно скомпилировал и запустил несколько примеров кода с использованием библиотек CUDA, таких как CUFFT и CUBLAS.Однако в последнее время я пытался сгенерировать свои собственные простые ядра и неоднократно получал бессмысленные значения обратно после вызова моих ядер.То есть - когда я передаю параметр в ядро, устанавливаю его значение в ядре, затем пытаюсь скопировать результаты обратно на хост и прочитать значения позже, они являются поддельными.Я пробовал много разных простых учебных руководств, которые, кажется, работают для большинства людей онлайн, но я всегда получаю бессмысленные значения.Например ...

#define SIZE 10

    //  Kernel definition, see also section 4.2.3 of Nvidia Cuda Programming Guide                                      
    __global__  void vecAdd(float* A, float* B, float* C) {

      // threadIdx.x is a built-in variable  provided by CUDA at runtime                                                
      int i = threadIdx.x;
      A[i]=0;
      B[i]=i;
      C[i] = A[i] + B[i];

    }

    int main {

      int N=SIZE;
      float A[SIZE], B[SIZE], C[SIZE];
      float *devPtrA;
      float *devPtrB;
      float *devPtrC;
      int memsize= SIZE * sizeof(float);

      cudaMalloc((void**)&devPtrA, memsize);
      cudaMalloc((void**)&devPtrB, memsize);
      cudaMalloc((void**)&devPtrC, memsize);
      cudaMemcpy(devPtrA, A, memsize,  cudaMemcpyHostToDevice);
      cudaMemcpy(devPtrB, B, memsize,  cudaMemcpyHostToDevice);
      // __global__ functions are called:  Func<<< Dg, Db, Ns >>>(parameter);                                          
      vecAdd<<<1, N>>>(devPtrA,  devPtrB, devPtrC);
      cudaMemcpy(C, devPtrC, memsize,  cudaMemcpyDeviceToHost);

      for (int i=0; i<SIZE; i++)
        printf("C[%d]=%f\n",i,C[i]);

      cudaFree(devPtrA);
      cudaFree(devPtrA);
      cudaFree(devPtrA);

}

Это довольно простая проблема;результаты должны быть:

C[0]=0.000000 
C[1]=1.000000 
C[2]=2.000000 
C[3]=3.000000 
C[4]=4.000000 
C[5]=5.000000 
C[6]=6.000000 
C[7]=7.000000 
C[8]=8.000000 
C[9]=9.000000 

Тем не менее, мои удивительные результаты всегда случайны и обычно выглядят так:

C[0]=nan
C[1]=-32813464158208.000000
C[2]=nan
C[3]=-27667211200843743232.000000
C[4]=34559834084263395806523272811251761152.000000
C[5]=9214363188332593152.000000
C[6]=nan
C[7]=-10371202300694685655937271382147072.000000
C[8]=121653576586393934243511643668480.000000
C[9]=-30648783863808.000000

Так что, в основном, когда я передаю параметры в ядро ​​CUDAнамерение хранить результаты внутри них для копирования обратно на хост, я склонен выбрасывать мусор.

Этот действительно меня озадачил.Любая помощь будет принята с благодарностью.

Спасибо.

Ответы [ 4 ]

2 голосов
/ 15 февраля 2011

Вы всегда должны проверять ошибки, возвращаемые вызовами API. Например, разработчики на C полностью привыкли к проверке NULL из malloc (), так как не проверка на NULL часто приводит к разыменованию нулевого указателя в дальнейшем (из-за плохих вещей). Разработчики C ++ часто полагаются на исключения, но многие API-интерфейсы выполнены в стиле C (включая используемые вами вызовы CUDA и многие другие библиотеки), поэтому вы должны знать, когда проверять ошибки.

В идеале вы должны проверять ошибки при каждом вызове API. Лично я бы не использовал макросы SAFE_CALL из CUTIL, вместо этого я бы проверял ошибку, обрабатывал ее правильно и генерировал исключение (C ++) или, по крайней мере, очищал должным образом. Таким образом, когда вы превращаете эксперимент в более крупное приложение, вы уже задумывались об обработке ошибок.

По крайней мере, вы должны проверить на наличие ошибки в конце:

cudaError_t cudaResult;
cudaResult = cudaGetLastError();
if (cudaResult != cudaSuccess)
{
    // Do whatever you want here
    // I normally create a std::string msg with a description of where I am
    // and append cudaGetErrorString(cudaResult)
}
1 голос
/ 16 февраля 2011

Я наконец понял это.Я работаю на 64-битном Mac Pro и передал -Xcompiler "arch x86_64" в качестве аргумента nvcc.Модератор на форумах NVidia отметил, что на Mac я должен вместо этого передать «-m64» в nvcc.Должно быть, я пропустил это в документации.Передача -m64 исправила мой вывод, и теперь кажется, что ядра успешно запускаются.Спасибо всем за ваши ответы.

1 голос
/ 15 февраля 2011

Я запустил ваш код и не получил никаких ошибок. Я бы попытался увидеть, если образцы SDK все еще работают? Кроме того, GPUOcelot обеспечивает поддержку эмуляции, если она вам когда-либо понадобится (в данном случае это выглядит немного излишне).

мой вывод: cuda2: ~ / tests $ ./test С [0] = 0,000000 С [1] = 1,000000 С [2] = 2,000000 С [3] = 3,000000 С [4] = 4,000000 С [5] = 5,000000 С [6] = 6,000000 С [7] = 7,000000 С [8] = 8,000000 С [9] = 9,000000

1 голос
/ 15 февраля 2011

Я получаю такое же поведение на хосте Linux с вашим кодом, когда у меня не загружен модуль ядра?Вы уверены, что у вас загружен драйвер?Вы можете проверить, что у вас есть устройство с поддержкой CUDA, запустив пример исполняемого файла deviceQuery, который поставляется вместе с SDK.

В качестве обновления, если у вас загружен модуль (проверено с помощью lsmod).Возможно, вам необходимо убедиться, что узлы устройств / dev / nvidia * существуют.В руководстве по началу работы приведен пример сценария (стр. 6, http://developer.download.nvidia.com/compute/cuda/3_2_prod/docs/Getting_Started_Linux.pdf).

Дальнейшее обновление. Если вы обрабатываете ошибки, как предложил Том, вы поймете эту ошибку. Если вы хотитебыстрый и грязный подход, который скажет вам, где вы столкнулись с ошибкой, вы можете посмотреть код для CUDA by Example book (http://developer.nvidia.com/object/cuda-by-example.html). Код предоставляет HANDLE_ERROR, который прекратит работу вашей программы при возникновении ошибкии отправьте сообщение на стандартный вывод. Это не лучший подход для производственного кода, но он быстрый и грязный.

...