- •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.7 Создание ‘.Cu‘ файла
Необходимо создать файл с расширением ‘.cu‘, который затем редактируется программой ‘Блокнот‘ или другим текстовым редактором. Можно открыть ‘.txt‘ файл ‘Блокнотом‘ и сохранить его как ‘.cu‘ файл, добавив после имени файла ‘.cu‘ и указать в типе файла ‘Все файлы‘ вместо ‘Текстовые документы (.txt)‘.
В ‘.cu‘ файле пишется код на языке программирования С с расширениями для CUDA в виде функций, которые вызываются либо из Java-программы, либо из других функций ‘.cu‘ файла.
Перед объявлением функции указывается директива ‘extern "C"‘. Затем указывается тип функции ‘__device__‘ (выполняется на GPU, вызывается из ‘.cu‘ файла), ‘__global__‘ (выполняется на GPU, вызывается из Java-программы) или ‘__host__‘ (выполняется на СPU, вызывается из Java-программы).
Например, необходимо выполнить на GPU функцию ‘func‘, вызываемую из Java-программы (как вызвать такую функцию из Java-программы было рассмотрено выше):
extern "C" __global__void func(<... список входящих переменных ...>){ <... тело функции ....> } |
Входящими переменными являются переменные Java-программы. При описании входящих переменных функции указывается их тип и имя через запятую (если входящей переменной является массив, то перед его именем указывается ‘*‘):
extern "C" __global__ func(int N,float two_pi,float *input,float *output){ <... тело функции ....>} |
Затем необходимо задать сетку и индексы потоков (переменными ‘threadIdx.x‘, ‘blockDim.x‘, ‘blockIdx.x‘). Данные переменные действительны только в пределах функции, которая выполняется на GPU. Также желательно провести синхронизацию потоков с помощью функции ‘__syncthreads()‘. Например, следующая функция вычисляет косинус для каждого элемента массива ‘input‘ и записывает результат в массив ‘output‘.
extern "C" __global__ void func(float *input, float *output){ int b = threadIdx.x + blockDim.x*blockIdx.x; output[b]=cos(input[b]); __syncthreads(); } |
При более сложной структуре функции сложно описать параллельные вычисления в одной функции, поэтому создается вторая вспомогательная функция, которая вызывается из первой:
extern "C" __device__ float loop(int b,int N, float two_pi, float *input){ <... тело функции ....> return (float) <... возвращаемое значение ...>; }
extern "C" __global__ void func(int N, float two_pi, float *input, float *output){ int b = threadIdx.x + blockDim.x*blockIdx.x; output[b]=loop(b,N,two_pi,input); __syncthreads(); } |
Таким образом получается готовый ‘.cu‘ файл, в котором все вычисления происходят в функции ‘loop‘:
extern "C" __device__ float loop(int b,int N, float two_pi, float *input){ float sum=0; for(int a=0;a<N;a++) if(input[a]>=input[b])sum+=cosf(two_pi*(input[b]-input[a])); return (float)(-2*sum)/(float)N; } extern "C" __global__ void func(int N,float two_pi,float *input,float *output){ int b = threadIdx.x + blockDim.x*blockIdx.x; output[b]=loop(b,N,two_pi,input); __syncthreads(); } |
