
- •1 Обзор cuda
- •1.2 Введение в cuda
- •1.3 Терминология cuda
- •1.4 Модель программирования cuda
- •1.5 Версии вычислительных возможностей cuda и программного обеспечения cuda Toolkit
- •1.6 Cuda Toolkit версии 5 и версия вычислительных возможностей 3
- •1.7 Полезные ссылки
- •2 Технология jcuda
- •2.1 Введение в jcuda
- •2.2 Требования к техническому и программному обеспечению для jcuda-программ
- •2.3 Настройка jcuda для среды разработки Eclipse
- •2.4 Решение возможных проблем при работе с cuda и jcuda
- •2.5 Определение параметров cuda-устройства и системных параметров в Java-программе
- •2.6 Подключение cuda модулей и выполнение их на cuda-устройстве из Java-программы
- •2.7 Создание ‘.Cu‘ файла
- •2.8 Сборка программы
- •3 Изучение особенностей jcuda на примере
- •3.1 Пример jcuda-программы
- •3.2 Скорость выполнения программы
- •3.3 Размер блока
- •3.4 Отклонения в результатах вычислений и двойная точность
2.6 Подключение cuda модулей и выполнение их на cuda-устройстве из Java-программы
CUDA модуль представляет собой файл с расширением ‘.cu‘. Подключение CUDA модуля представляет собой подключение функции из ‘.cu‘ файла:
String folder="t_cuda//p_task_1//";//директория в которой находится .cu файл KernelLauncher kernelLauncher=KernelLauncher.create(folder+ "func.cu","func","");//подключение функции “func” из “func.cu” файла |
Как видно в переменной ‘folder‘ прописан относительный путь, который указывает местонахождение файла ‘.cu‘, если он находится в корневой папке Java-проекта.
Особенности распараллеливания программы при выполнении на GPU задаются параметрами ‘BLOCKSIZE‘, ‘nThreads‘, ‘nBlocks‘ (‘N‘ - размер одномерного массива, который обрабатывается на GPU). ‘N‘ должен делиться на ‘BLOCKSIZE‘ без остатка, иначе расчет на GPU будет неверным.
int BLOCKSIZE = 50; dim3 nThreads = new dim3(BLOCKSIZE, 1, 1); dim3 nBlocks = new dim3(N/BLOCKSIZE, 1, 1); |
Создание указателей на массив, расположенный в GPU:
CUdeviceptr dOutput = new CUdeviceptr(); CUdeviceptr dInput = new CUdeviceptr(); |
Выделение памяти для указателя (для массива типа float размером N значений):
cuMemAlloc(dOutput, N*Sizeof.FLOAT); cuMemAlloc(dInput, N*Sizeof.FLOAT); |
Помещение массива ‘masInput‘ в память GPU (‘cudaMemcpyHostToDevice‘):
cudaMemcpy(dInput,Pointer.to(masInput),N*Sizeof.FLOAT,cudaMemcpyHostToDevice); |
Выполнение функции ‘func‘ из файла ‘func.cu‘ на GPU:
kernelLauncher.setup(nBlocks, nThreads).call(N,pi,dInput,dOutput); |
В методе ‘call‘ указываются входные параметры функции ‘func‘. Выделение памяти под простые типы данных (переменные ‘N‘, ‘pi‘) не производится. Массив, на который указывает ‘dOutput‘, создается при вычислениях на GPU, поэтому для него не производится помещение в память GPU до вычислений на GPU.
После вычислений на GPU результат в виде массива возвращается (‘cudaMemcpyDeviceToHost‘) в основную часть программы и записывается в массив ‘masOutput‘:
cudaMemcpy(Pointer.to(masOutput),dOutput,N*Sizeof.FLOAT,cudaMemcpyDeviceToHost); |
Затем необходимо освободить память GPU:
cuMemFree(dInput); cuMemFree(dOutput); |
Рекомендуется вычисление на GPU кода, на выполнение которого будет затрачиваться время длительностью не менее десятых долей секунды, при том, что количество таких вычислений с возвратом результата в основную часть программы должно быть минимизировано. В обратном случае (при большом количестве непродолжительных вычислений на GPU c почти непрерывным обменом данными между GPU и основной частью программы) не будет получено большое ускорение вычислений на GPU по сравнению с CPU.
Таким образом получается код, который при выполнении на GPU в количестве ‘number‘ раз в цикле ‘for‘ примет вид:
String folder="t_cuda//p_task_1//"; KernelLauncher kernelLauncher=KernelLauncher.create(folder+"func.cu","func",""); int BLOCKSIZE = 50; dim3 nThreads = new dim3(BLOCKSIZE, 1, 1); dim3 nBlocks = new dim3(N/BLOCKSIZE, 1, 1); CUdeviceptr dOutput = new CUdeviceptr(); CUdeviceptr dInput = new CUdeviceptr(); for(int i=1;i<number;i++){ cuMemAlloc(dOutput, N*Sizeof.FLOAT); cuMemAlloc(dInput, N*Sizeof.FLOAT); cudaMemcpy(dInput,Pointer.to(masInput),N*Sizeof.FLOAT, cudaMemcpyHostToDevice); kernelLauncher.setup(nBlocks, nThreads).call(N,pi,dInput,dOutput); cudaMemcpy(Pointer.to(masOutput),dOutput,N*Sizeof.FLOAT,cudaMemcpyDeviceToHost); cuMemFree(dInput); cuMemFree(dOutput); } |