CUDA
CUDA представляет из себя модификацию языка C, поэтому для исходных файлов принято использовать специальное расширение - 'cu' вместо 'c'.
Основные термины:
- host - компьютер 'в обычном понимании', управляемый CPU.
- device (устройство) - карта с GPU.
- kernel (ядро) - функция, которая будет запущена в нескольких экземплярах, каждый из которых будет работать на своём ядре устройства. Для указания, что функция будет ядром, при её описании используется спецификатор
___global___
Выполнение ядра с распараллеливанием на N потоков описывается следующим образом:
MyKernel<<<1, N>>>(параметры);
Используемое ПО
CUDA Toolkit
CUDA Toolkit предоставляет полную среду разработки на C и C++ с использованием вычислений на GPU NVIDIA. Включает компилятор для GPU, инструменты для отладки и оптимизации, математические библиотеки и документацию.
Для сервера a6500g10 используется версия 12.6, необходимые переменные окружения настраиваются автоматически при авторизации на сервере.
CUDA Code Samples
CUDA Code Samples (предыдущее название - GPU Computing SDK) содержит примеры кода и официальные документы, призванные помочь создавать ПО, использующее NVIDIA GPU, с помощью CUDA C/C++, OpenCL или DirectCompute.
Пример
- Ниже приведён пример запуска программы, складывающей средствами CUDA два вектора 'A' и 'B' (т.е. два массива поэлементно) и сохраняющей сумму в вектор 'С'.
- Поскольку GPU обрабатывает данные, находящиеся в своей собственной памяти, а не в ОЗУ компьютера, требуются дополнительные действия - выделение памяти на устройстве, копирование туда исходных данных, копирование полученного результата обратно на компьютер.
- Номер GPU, который будет использоваться, программа будет получать при запуске в качестве первого параметра.
- Создать файл 'addvectors.cu' следующего содержания:
#include <stdio.h> #include "cuda.h" #define N 128 int assigned_device; int used_device; // Data on the host system int HostA[N]; int HostB[N]; int HostC[N]; // Pointers to data on the device int *DeviceA; int *DeviceB; int *DeviceC; //---------------------------------------------------------- __global__ void AddVectors(int* a, int* b, int* c) { int i = threadIdx.x; c[i] = a[i] + b[i]; } //---------------------------------------------------------- int main(int argc, char** argv) { // Define the device to use: if (argc < 2) { printf ("Error: device number is absent\n"); return 100; } assigned_device=atoi(argv[1]); if ( strlen(argv[1]) > 1 or ( assigned_device == 0 and strcmp(argv[1],"0") != 0 ) ) { printf ("Error: device number is incorrect\n"); return 110; } // Select the used device: if ( cudaSetDevice(assigned_device) != cudaSuccess or cudaGetDevice( &used_device ) != cudaSuccess or used_device != assigned_device ) { printf ("Error: unable to set device %d\n", assigned_device); return 120; } printf ("Used device: %d\n", used_device); // Initialize summands: for (int i=0; i<N; i++) { HostA[i]=i*2; HostB[i]=i*3; } // Allocate memory on the device: cudaMalloc((void**)&DeviceA, N*sizeof(int)); cudaMalloc((void**)&DeviceB, N*sizeof(int)); cudaMalloc((void**)&DeviceC, N*sizeof(int)); // Copy summands from host to device: cudaMemcpy(DeviceA, HostA, N*sizeof(int), cudaMemcpyHostToDevice); cudaMemcpy(DeviceB, HostB, N*sizeof(int), cudaMemcpyHostToDevice); // Execute kernel: AddVectors<<<1, N>>>(DeviceA, DeviceB, DeviceC); // Copy result from device to host: cudaMemcpy(HostC, DeviceC, N*sizeof(int), cudaMemcpyDeviceToHost); // Show result: for (int i=0; i<N; i++) { printf ("%d + %d = %d\n",HostA[i],HostB[i],HostC[i]); } cudaFree(DeviceA); cudaFree(DeviceB); cudaFree(DeviceC); } - Для компилирования будет используется утилита nvcc. Необходимо выполнить:
nvcc addvectors.cu -o addvectors
Либо можно использовать 'makefile' следующего содержания
addvectors : addvectors.cu nvcc addvectors.cu -o $@В результате должен быть создан исполняемый файл 'addvectors'
- Для взаимодействия с планировщиком PBS создать файл 'submit.sh' следующего содержания:
#!/bin/sh #PBS -q a6500g10q #PBS -l walltime=0:01:00 #PBS -l select=1:ngpus=1:ncpus=1:mem=2gb cd $PBS_O_WORKDIR nvcc addvectors.cu -o addvectors ./addvectors 0
- Поставить задачу в очередь:
qsub submit.sh
- После завершения в файле стандартного вывода будет получено примерно следующее:
Used device: 0 0 + 0 = 0 2 + 3 = 5 4 + 6 = 10 6 + 9 = 15 8 + 12 = 20 ... 246 + 369 = 615 248 + 372 = 620 250 + 375 = 625 252 + 378 = 630 254 + 381 = 635