Структура CUDA программы.

Программа на CUDA состоит из одной или более частей, которые исполняются на хосте (CPU) или устройстве (GPU). Те части, где мало или нет совсем параллельного кода, исполняет CPU. В частях, где содержатся данные для параллельной обработки, обрабатываются графическим процессором. Программа состоит из кода, который будет исполняться как на CPU, так и на GPU. Компилятор NVIDIA C (nvcc) сам разделит код на части. Код, который будет выполнен на CPU, представляет собой чистый Си и будет скомпилирован обычным компилятором языка Си, установленный в вашей системе. Код для устройства состоит из расширенного ANSI Си с использованием специфических функций, называемых kernels, с соответствующими структурами данных.

Код для устройства будет скомпилирован nvcc и выполняться будет на графическом процессоре. В случаях, когда нет доступных устройств, то можно выполнить kernels и на CPU в режиме эмуляции (можно выбрать в настройках режима компиляции). Режим эмуляции - это специальное средство для отладки программ CUDA. Все блоки при этом будут выполнены последовательно на CPU, поэтому скорость работы программы будет в несколько раз медленнее. Программы, скомпилированные в режиме эмуляции, можно запускать вне зависимости от наличия CUDA. Kernels обычно генерируют большое число потоков для обеспечения параллелизма. Потоки на GPU имеют во много раз меньший "вес" по сравнению с CPU.

Выполнение программы начинается с выполнения хост-кода. Когда вызывается kernel-функция, управление передается на GPU, где сгенерированные потоки выполняют параллельный код. Все потоки в результате вызова функции называются сеткой (grid). Когда все потоки завершат свою работу, управление снова передается на хост до вызова следующей kernel-функции.

Управление устройствами CUDA

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

int deviceCount; cudaGetDeviceCount(&deviceCount); for(int device = 0; device < deviceCount; device++) { cudaDeviceProp deviceProp; cudaGetDeviceProperties(&deviceProp, device); printf("Номер устройства: %d\n",device); printf("Имя устройства: %s\n", deviceProp.name); printf("Объем глобальной памяти: %d\n", deviceProp.totalGlobalMem); printf("Объем shared-памяти в блоке : %d\n", deviceProp.sharedMemPerBlock); printf("Объем регистровой памяти: %d\n", deviceProp.regsPerBlock); printf("Размер warp'a: %d\n", deviceProp.warpSize); printf("Размер шага памяти: %d\n", deviceProp.memPitch); printf("Макс количество потоков в блоке: %d\n", deviceProp.maxThreadsPerBlock); printf("Максимальная размерность потока: x = %d, y = %d, z = %d\n", deviceProp.maxThreadsDim[0], deviceProp.maxThreadsDim[1], deviceProp.maxThreadsDim[2]); printf("Максимальный размер сетки: x = %d, y = %d, z = %d\n", deviceProp.maxGridSize[0], deviceProp.maxGridSize[1], deviceProp.maxGridSize[2]); printf("Тактовая частота: %d\n", deviceProp.clockRate); printf("Общий объем константной памяти: %d\n", deviceProp.totalConstMem); printf("Вычислительная мощность: %d.%d\n", deviceProp.major, deviceProp.minor); printf("Величина текстурного выравнивания : %d\n", deviceProp.textureAlignment); printf("Количество процессоров: %d\n", deviceProp.multiProcessorCount); } cudaSetDevice используется для выбора устройства : cudaSetDevice(device) Устройство должно быть выбрано раньше любого вызова __global__ функции,
если этого не произшло по умолчанию ставится устройство с номером 0.