Странное поведение элементарного кода CUDA. - PullRequest
1 голос
/ 28 ноября 2011

У меня проблемы с пониманием вывода следующего простого кода CUDA. Все, что делает код, - это выделяет два целочисленных массива: один на хосте и один на устройстве каждого размера 16. Затем он устанавливает для элементов массива устройства целое значение 3, а затем копирует эти значения в host_array, где все элементы затем распечатывается.

#include <stdlib.h>
#include <stdio.h>

int main(void)
{
  int num_elements = 16;
  int num_bytes = num_elements * sizeof(int);

  int *device_array = 0;
  int *host_array = 0;

  // malloc host memory
  host_array = (int*)malloc(num_bytes);

  // cudaMalloc device memory
  cudaMalloc((void**)&device_array, num_bytes);

  // Constant out the device array with cudaMemset
  cudaMemset(device_array, 3, num_bytes);

  // copy the contents of the device array to the host
  cudaMemcpy(host_array, device_array, num_bytes, cudaMemcpyDeviceToHost);

  // print out the result element by element
  for(int i = 0; i < num_elements; ++i)
    printf("%i\n", *(host_array+i));

  // use free to deallocate the host array
  free(host_array);

  // use cudaFree to deallocate the device array
  cudaFree(device_array);

  return 0;
}

Вывод этой программы печатается 50529027 построчно 16 раз.

50529027
50529027
50529027
..
..
..
50529027
50529027

Откуда пришло это число? Когда я заменяю 3 на 0 в вызове cudaMemset, я получаю правильное поведение. то есть 0 печатается построчно 16 раз.

Я скомпилировал код с nvcc test.cu в Ubuntu 10.10 с CUDA 4.0

Ответы [ 4 ]

7 голосов
/ 28 ноября 2011

Я не эксперт по cuda, но 50529027 это 0x03030303 в гексе.Это означает, что cudaMemset устанавливает для каждого byte в массиве значение 3, а не для каждого int.Это неудивительно, учитывая сигнатуру cuda memset (для передачи количества байтов для установки) и общую семантику операций memset.

Edit : Что касается вашего (я полагаю)неявный вопрос о том, как достичь того, что вы хотели, я думаю, что вы должны написать цикл и инициализировать каждый элемент массива.

4 голосов
/ 28 ноября 2011

Как уже отмечали другие, cudaMeset работает как стандарт C memset - он устанавливает байтовые значения. Из документации CUDA:

cudaError_t cudaMemset( void * devPtr, int value, size_t count)

Заполняет первые байты счетчика области памяти, на которую указывает devPtr с постоянным байтовым значением value.

Если вы хотите установить значения размера слова, лучшее решение - использовать ваше собственное ядро ​​memset, возможно, что-то вроде этого:

template<typename T>
__global__ void myMemset(T * x, T value, size_t count )
{
    size_t tid = threadIdx.x + blockIdx.x * blockDim.x;
    size_t stride = blockDim.x * gridDim.x;

    for(int i=tid; i<count; i+=stride) {
        x[i] = value;
    }
}

, который может быть запущен с достаточным количеством блоков, чтобы покрыть количество MP в вашем графическом процессоре, и каждый поток будет выполнять столько итераций, сколько требуется для заполнения выделения памяти. Записи будут объединены, поэтому производительность не должна быть слишком плохой. Это также может быть адаптировано к векторным типам CUDA, если вы того пожелаете.

1 голос
/ 30 ноября 2011

Это классический недостаток memset; он работает только с типом данных с 8-битным размером, т.е. char . Это означает, что он устанавливает (вероятно) 3 на каждые 8 ​​битов общей памяти. Вы можете подтвердить это простым кодом C ++:

int main ()  
{    
    int x=16;
    size_t bytes = x*sizeof(int);

    int *M = (int*)malloc(bytes);
    memset(M,3,bytes);


    for (int i = 0; i < x; ++i) { 
        printf("%d\n", M[i]); 
    }    

    return 0;
}

Единственный случай, когда memset работает на всех типах данных, это когда вы устанавливаете его в 0. (он устанавливает каждый байт в 0 и, следовательно, все данные в 0). Если вы измените тип данных на char , вы увидите желаемый результат. cudaMemset - это то же самое, что и копия memset, с той лишь разницей, что для ввода требуется указатель GPU.

Таким образом, memset или cudaMemset, вероятно, устанавливают каждый байт в целочисленное значение (в вашем случае 3) всего пространства памяти, определенного третьим аргументом, независимо от типа данных.

Совет:

Google: 50529027 в двоичном виде , и вы получите ответ:)

1 голос
/ 28 ноября 2011

memset устанавливает байты, а целое число составляет 4 байта .. так что вы получите 50529027 десятичных, что 0x3030303 в шестнадцатеричном формате ... Другими словами - вы используете его неправильно, и это не имеет ничего общего с CUDA.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...