CUDA, как получить сетку, блок, размер резьбы и рассчитать вычисление не квадратной матрицы

Я новичок в 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

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

Основная идея моделей 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 довольно зрелая, и есть много хороших бесплатных учебных материалов, которые можно использовать в Интернете, чтобы, возможно, использовать их для дальнейшего освещения многих аспектов модели программирования, которые я замалчивал в этом ответе.