CUDA, как получить сетку, блок, размер резьбы и параллализировать неквадратный матричный расчет - PullRequest
19 голосов
/ 13 апреля 2011

Я новичок в CUDA и мне нужна помощь в понимании некоторых вещей.Мне нужна помощь в распараллеливании этих двух циклов.В частности, как настроить dimBlock и dimGrid, чтобы сделать это быстрее.Я знаю, что это выглядит как пример добавления вектора в SDK, но этот пример только для квадратных матриц, и когда я пытаюсь изменить этот код для моей матрицы 128 x 1024, он не работает должным образом.

__global__ void mAdd(float* A, float* B, float* C)
{
    for(int i = 0; i < 128; i++)
    {
        for(int i = 0; i < 1024; i++)
        {
            C[i * 1024 + j] = A[i * 1024 + j] + B[i * 1024 + j];
        }
    }
}

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

1 Ответ

36 голосов
/ 13 апреля 2011

Как вы уже написали, это ядро ​​полностью последовательное.Каждый поток, запущенный для выполнения, будет выполнять одну и ту же работу.

Основная идея CUDA (и OpenCL и других подобных моделей программирования типа «одна программа, несколько данных») заключается в том, что вы выполняете операцию «параллельная передача данных», то есть та, где одна и та же, в основном независимая, операция должнавыполнить много раз - и написать ядро, которое выполняет эту операцию.Затем запускается большое количество (полу) автономных потоков для выполнения этой операции над входным набором данных.

В вашем примере добавления массива параллельная операция с данными

C[k] = A[k] + B[k];

для всехk между 0 и 128 * 1024. Каждая операция сложения является полностью независимой и не имеет требований к упорядочению, и, следовательно, может выполняться другим потоком.Чтобы выразить это в CUDA, можно написать ядро ​​следующим образом:

__global__ void mAdd(float* A, float* B, float* C, int n)
{
    int k = threadIdx.x + blockIdx.x * blockDim.x;

    if (k < n)
        C[k] = A[k] + B[k];
}

[заявление об отказе: код, написанный в браузере, не проверенный, используйте на свой страх и риск]

Здесь внутренний ивнешний цикл из последовательного кода заменяется одним потоком CUDA на операцию, и я добавил проверку ограничения в коде, чтобы в случаях, когда было запущено больше потоков, чем требуется, не происходило переполнения буфера.Если затем ядро ​​запускается так:

const int n = 128 * 1024;
int blocksize = 512; // value usually chosen by tuning and hardware constraints
int nblocks = n / nthreads; // value determine by block size and total work

madd<<<nblocks,blocksize>>>mAdd(A,B,C,n);

Тогда 256 блоков, каждый из которых содержит 512 потоков, будут запущены на оборудовании графического процессора для параллельного выполнения операции добавления массива.Обратите внимание, что если бы размер входных данных не был выражен в виде простого округления, кратного размеру блока, число блоков необходимо было бы округлить, чтобы охватить полный набор входных данных.

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

...