Ядро CUDA должно динамически зависать в зависимости от размера блока - PullRequest
1 голос
/ 28 февраля 2012

Я хочу сделать умножение разреженной матрицы на плотный вектор.Предположим, что единственным форматом хранения для сжатия записей в матрице является сжатое хранилище строк CRS.

Мое ядро ​​выглядит следующим образом:

__global__ void
krnlSpMVmul1(
        float *data_mat,
        int num_nonzeroes,
        unsigned int *row_ptr,
        float *data_vec,
        float *data_result)
{
    extern __shared__ float local_result[];
    local_result[threadIdx.x] = 0;

    float vector_elem = data_vec[blockIdx.x];

    unsigned int start_index = row_ptr[blockIdx.x];
    unsigned int end_index = row_ptr[blockIdx.x + 1];

    for (int index = (start_index + threadIdx.x); (index < end_index) && (index < num_nonzeroes); index += blockDim.x)
        local_result[threadIdx.x] += (data_mat[index] * vector_elem);

    __syncthreads();

   // Reduction

   // Writing accumulated sum into result vector
}

Как вы можете видеть, ядро ​​должнобыть настолько наивным, насколько это возможно, и даже делает несколько вещей неправильно (например, vector_elem не всегда является правильным значением).Я знаю об этих вещах.

Теперь к моей проблеме: предположим, я использую размер блока из 32 или 64 потоков.Как только строка в моей матрице содержит более 16 ненулевых элементов (например, 17), только первые 16 умножений выполняются и сохраняются в общей памяти.Я знаю, что значение local_result[16], которое является результатом 17-го умножения, просто равно нулю.Использование размера блока из 16 или 128 потоков решает описанную проблему.

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

Помощь очень ценится!


Редактировать в комментарии к комментариям:

Я напечатал значения, которые были в local_result[16] непосредственно после вычисления.Это было 0.Тем не менее, вот отсутствующий код:

Часть сокращения:

int k = blockDim.x / 2;
while (k != 0)
{
    if (threadIdx.x < k)
        local_result[threadIdx.x] += local_result[threadIdx.x + k];
    else
        return;

    __syncthreads();

    k /= 2;
}

и как я записываю результаты обратно в глобальную память:

data_result[blockIdx.x] = local_result[0];

Вот и всеГот.

Сейчас я тестирую сценарий с матрицей, состоящей из одной строки с 17 элементами, которые все не равны нулю.Буферы выглядят так в псевдокоде:

float data_mat[17] = { val0, .., val16 }
unsigned int row_ptr[2] = { 0, 17 }
float data_vec[17] = { val0 } // all values are the same
float data_result[1] = { 0 }

И это отрывок из моей функции-обертки:

float *dev_data_mat;
unsigned int *dev_row_ptr;
float *dev_data_vec;
float *dev_data_result;

// Allocate memory on the device
HANDLE_ERROR(cudaMalloc((void**) &dev_data_mat, num_nonzeroes * sizeof(float)));
HANDLE_ERROR(cudaMalloc((void**) &dev_row_ptr, num_row_ptr * sizeof(unsigned int)));
HANDLE_ERROR(cudaMalloc((void**) &dev_data_vec, dim_x * sizeof(float)));
HANDLE_ERROR(cudaMalloc((void**) &dev_data_result, dim_y * sizeof(float)));

// Copy each buffer into the allocated memory
HANDLE_ERROR(cudaMemcpy(
        dev_data_mat,
        data_mat,
        num_nonzeroes * sizeof(float),
        cudaMemcpyHostToDevice));
HANDLE_ERROR(cudaMemcpy(
        dev_row_ptr,
        row_ptr,
        num_row_ptr * sizeof(unsigned int),
        cudaMemcpyHostToDevice));
HANDLE_ERROR(cudaMemcpy(
        dev_data_vec,
        data_vec,
        dim_x * sizeof(float),
        cudaMemcpyHostToDevice));
HANDLE_ERROR(cudaMemcpy(
        dev_data_result,
        data_result,
        dim_y * sizeof(float),
        cudaMemcpyHostToDevice));

// Calc grid dimension and block dimension
dim3 grid_dim(dim_y);
dim3 block_dim(BLOCK_SIZE);

// Start kernel
krnlSpMVmul1<<<grid_dim, block_dim, BLOCK_SIZE>>>(
        dev_data_mat,
        num_nonzeroes,
        dev_row_ptr,
        dev_data_vec,
        dev_data_result);

Надеюсь, это просто, но объясню, если это будет интересно..

Еще одна вещь: я только что понял, что использование BLOCK_SIZE из 128 и 33 ненулевых значений также приводит к сбою ядра.Опять же, только последнее значение не вычисляется.

1 Ответ

1 голос
/ 28 февраля 2012

Ваш динамически распределенный объем совместно используемой памяти неверен. Прямо сейчас вы делаете это:

krnlSpMVmul1<<<grid_dim, block_dim, BLOCK_SIZE>>>(.....)

Размер разделяемой памяти должен быть указан в байтах . Использование 64 потоков в каждом блоке означает, что вы будете выделять достаточно разделяемой памяти для 16 слов с плавающей запятой, и объясняет, почему магические 17 записей в каждом случае строки приводят к сбою - у вас переполнение общего буфера, которое вызывает сбой защиты в GPU и прервать ядро.

Вы должны делать что-то вроде этого:

krnlSpMVmul1<<<grid_dim, block_dim, BLOCK_SIZE * sizeof(float)>>>(.....)

Это даст вам правильный динамический объем разделяемой памяти и устранит проблему.

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