«ТЕХНОЛОГИЯ CUDA» Автор: Приймак А. В. Лекция на

Скачать презентацию «ТЕХНОЛОГИЯ CUDA» Автор: Приймак А. В. Лекция на Скачать презентацию «ТЕХНОЛОГИЯ CUDA» Автор: Приймак А. В. Лекция на

39628-cuda_2012.ppt

  • Количество слайдов: 25

>«ТЕХНОЛОГИЯ CUDA»  Автор: Приймак А. В. Лекция на тему: «ТЕХНОЛОГИЯ CUDA» Автор: Приймак А. В. Лекция на тему:

>Определение Compute unified device architecture (CUDA) - архитектура параллельных вычислений, разработанная NVIDIA. CUDA обеспечивает Определение Compute unified device architecture (CUDA) - архитектура параллельных вычислений, разработанная NVIDIA. CUDA обеспечивает возможность вычислений общего назначения на GPU от NVIDIA, которые обычно производит CPU. General-purpose computing on graphics processing units (GPGPU) – проведение вычислений общего назначения на графических процессорах. В отличие от CPU, GPU имеют производительность параллельной архитектуры, что означает выполнение многих потоков параллельно и медленно, а не выполнение одного потока быстро. Альтернативные GPGPU-технологии, подобные CUDA: AMD FireStream, DirectCompute, OpenCL и др.

>Устройства с поддержкой CUDA  CUDA появилась в 2007 году с выходом чипа NVIDIA Устройства с поддержкой CUDA CUDA появилась в 2007 году с выходом чипа NVIDIA восьмого поколения G80 и присутствует в последующих сериях графических чипов, которые используются в семействах ускорителей GeForce, Quadro и Tesla. Наиболее важные параметры устройств CUDA: - вычислительная мощность – число операций с плавающей точкой в секунду (FLOPS); - число ядер CUDA; - размер памяти; - версия вычислительных возможностей CUDA.

>Языки программирования и CUDA Программисты используют язык программирования Языки программирования и CUDA Программисты используют язык программирования "С для CUDA" (язык C с расширениями и некоторыми ограничениями NVIDIA) для написания алгоритмов, выполняемых на GPU. CUDA предоставляет вычислительные интерфейсы для Python, Perl, Fortran, Java, Ruby, Lua, Mathematica, MATLAB и других языков программирования и прикладных программ.

>CUDA API CUDA включает два API: - высокого уровня (CUDA Runtime API); - низкого CUDA API CUDA включает два API: - высокого уровня (CUDA Runtime API); - низкого (CUDA Driver API).

>Модель программирования CUDA  Потоки объединяются в блоки потоков (thread block). Программа (ядро, kernel) Модель программирования CUDA Потоки объединяются в блоки потоков (thread block). Программа (ядро, kernel) исполняется над сеткой (grid) блоков потоков. Одновременно исполняется одна сетка. Потоки (thread) выполняются на ядрах видеокарты.

>Версии вычислительных возможностей Устройства CUDA имеют различные версии вычислительных возможностей, каждая версия имеет определенные Версии вычислительных возможностей Устройства CUDA имеют различные версии вычислительных возможностей, каждая версия имеет определенные характеристики. Основное отличие между версиями 1 и 2 – вторая версия поддерживает вычисления с двойной точностью. В 2012 году появилась 3-я версия вычислительных возможностей http://en.wikipedia.org/wiki/CUDA http://ru.wikipedia.org/wiki/CUDA

>CUDA версии 5 (октябрь 2012) Основные нововведения: -  динамический параллелизм. Потоки GPU могут CUDA версии 5 (октябрь 2012) Основные нововведения: - динамический параллелизм. Потоки GPU могут динамически рождать новые потоки. Сводя к минимуму пересылку данных в CPU и обратно, динамический параллелизм упрощает параллельное программирование; - вызываемые из кода на GPU библиотеки. Такая поддержка обеспечивает процесс создания больших приложений под GPU; - поддержка GPUDirect – обеспечивает прямую связь между GPU и другими устройствами PCI-E и поддерживает прямой доступ к памяти между сетевыми картами и GPU. Это уменьшает задержки между узлами GPU в кластере и повышает общую скорость работы приложения; - NVIDIA Nsight Eclipse Edition – быстрое и простое генерирование кода CUDA. Эта функция позволяет программистам создавать приложения под GPU в среде на базе Eclipse. Встроенный редактор CUDA и примеры кода ускоряют генерирование кода CUDA, а автоматический рефакторинг кода позволяет легко портировать циклы CPU на ядра CUDA.

>Недостатки CUDA - не поддерживаются алгоритмы, которые не поддаются распараллеливанию, такие как рекурсия; - Недостатки CUDA - не поддерживаются алгоритмы, которые не поддаются распараллеливанию, такие как рекурсия; - архитектуру CUDA поддерживает и развивает только производитель NVidia; многие видеокарты не поддерживают вычисления в двойной точности (double), в то время как вычисления в одинарной точности (float) не всегда обеспечивают необходимую точность вычислений; скорость вычислений в двойной точности примерно в 10 раз меньше, чем в одинарной точности, в отличие от CPU, которые оптимизированы под вычисления в двойной точности (где скорость вычислений в двойной точности и в одинарной совпадают ); высокая стоимость т.н. «профессиональных GPU» (оптимизированных для вычислений общего назначения и имеющих высокую скорость вычислений в двойной точности).

>JCUDA JCUDA обеспечивает взаимодействие с CUDA API и драйверами из Java-программы.  http://jcuda.org/ - JCUDA JCUDA обеспечивает взаимодействие с CUDA API и драйверами из Java-программы. http://jcuda.org/ - сайт JCUDA Основное применение JCUDA - взаимодействие с существующими специализированными библиотеками, построенными на основе CUDA API: - JCublas - Java-привязка для CUDA-библиотеки подпрограмм основной линейной алгебры (Basic Linear Algebra Subprograms (BLAS)); - JCufft - Java-привязка для CUDA-библиотеки быстрого трансформирования Фурье (Fast Fourier Transforms (FFT)); - JCudpp - Java-привязка для CUDA-библиотеки примитивов параллельных даных (Data Parallel Primitives (DPP)); - JCurand - Java-привязка для CUDA-генератора случайных чисел; - JCusparse - Java-привязка для CUDA-библиотеки для разреженных матриц (sparse matrix).

>Требования к ПО для JCUDA Developer Drivers (NVIDIA драйвер для CUDA-устройства);   Требования к ПО для JCUDA Developer Drivers (NVIDIA драйвер для CUDA-устройства); CUDA Toolkit (компилятор NVIDIA CUDA compiler (NVCC) и CUDA-библиотеки); Java Development Kit (JDK); среда разработки для Java, например Eclipse; файлы JCUDA (Binaries for); JCUDA-утилита ‘jcudaUtils‘; компилятор языка С ‘cl.exe‘ (входит в Microsoft Visual Studio 9.0 (2008) и другие среды разработки)

>Ошибка CUDA_ERROR_LAUNCH_TIMEOUT Ошибка CUDA_ERROR_LAUNCH_TIMEOUT при выполнении CUDA-программы связана с превышением интервала ожидания GPU. Интервал Ошибка CUDA_ERROR_LAUNCH_TIMEOUT Ошибка CUDA_ERROR_LAUNCH_TIMEOUT при выполнении CUDA-программы связана с превышением интервала ожидания GPU. Интервал ожидания для ОС WindowsXP обычно равен 20 секунд, для ОС Windows7 - 5 секунд. Данная ошибка означает, что GPU не может проводить расчеты дольше установленного интервала ожидания без ответа в основную часть JCUDA-программы. Для разрешения проблемы необходимо открыть редактор реестра ‘Пуск‘->‘Выполнить‘(‘regedit‘). В редакторе реестра найти папку ‘GraphicsDrivers‘ и добавить в нее и в папку ‘DCI‘ (вложенную в ‘GraphicsDrivers‘) параметр ‘TdrLevel‘ со значением 0.

>Настройка JCUDA для среды разработки Eclipse  После скачивания файлов JCUDA и файла jcudaUtils, Настройка JCUDA для среды разработки Eclipse После скачивания файлов JCUDA и файла jcudaUtils, необходимо поместить файлы в корневую папку проекта Java и подключить их к проекту в Eclipse. Обязательны для копирования в проект ‘.jar‘ файлы ‘jcuda‘ и ‘jcudaUtils‘ и ‘.dll‘ файлы ‘JCudaDriver‘ и ‘JCudaRuntime‘. Остальные ‘.jar‘ файлы-библиотеки и соответствующие им ‘.dll‘ файлы можно не включать в проект, если их не использовать в программах. Этапы подключения файлов к проекту в Eclipse: - 1 - после создания проекта необходимо в навигаторе проекта (Project Explorer) выбрать корень проекта и нажать правой кнопкой мыши; - 2 - в выпавшем меню необходимо выбрать пункт ‘Properties‘; - 3 - в появившемся окне в списке слева необходимо выбрать пункт ‘Java Build Path‘; - 4 - в том же окне справа нажать кнопку ‘Add JARs...‘; - 5 - появится окно, в котором необходимо указать ‘.jar‘ файл из библиотеки JCUDA и нажать ‘ОК‘. Таким образом необходимо добавить все требуемые ‘.jar‘ файлы. Затем необходимо нажать кнопку справа ‘Add Library...‘, в появившемся окне выбрать ‘User Library‘; - 6 - в следующем окне нажать ‘User Libraries...‘, в следующем окне нажать ‘New‘ и ввести название библиотеки. Затем необходимо вернуться на ‘User Library‘, где выбрать созданную библиотеку и нажать ‘Finish‘; - 7 - затем в окне свойств проекта необходимо раскрыть дерево добавленной библиотеки, в котором выбрать ‘Native Library Location: (None)‘ и нажать кнопку справа ‘Edit...‘; - 8 - в появившемся окне нажать кнопку ‘Workspace‘, в котором выбрать название проекта (то есть выбирается местоположение ‘.dll‘ файлов) и нажать ‘ОК‘; - 9 - в окне свойств проекта нажать ‘ОК‘. Таким образом будет завершена настройка JCUDA.

>Параметры CUDA-устройства  При запуске программы полезно делать проверку на наличие CUDA-устройства: if(cudaGetDeviceCount(new int[1])!=cudaSuccess) Параметры CUDA-устройства При запуске программы полезно делать проверку на наличие CUDA-устройства: if(cudaGetDeviceCount(new int[1])!=cudaSuccess) System.out.println("Устройство с поддержкой CUDA отсутствует!"); Получение количества CUDA-устройств: int deviceCountArray[] = new int[1]; cudaGetDeviceCount(deviceCountArray); String cudaDeviceCount="CUDA устройств подключено: "+deviceCountArray[0]; Для получения параметров CUDA-устройства необходимо создать объект класса ‘cudaDeviceProp‘ и получить параметры методом ‘cudaGetDeviceProperties‘, вторым параметром которого указывается порядковый номер CUDA-устройства (при наличии одного CUDA-устройства его порядковый номер равен 0, для следующего - 1 и т.д.): cudaDeviceProp deviceProp = new cudaDeviceProp(); cudaGetDeviceProperties(deviceProp, 0); Затем с помощью ‘deviceProp‘ можно получить параметры CUDA-устройства: "Видеокарта: "+new String(deviceProp.name).substring(0, new String(deviceProp.name).indexOf(0)); "Число ядер: "+deviceProp.multiProcessorCount*8+" (может быть неверным)";"Глобальная память: "+new BigDecimal((float)(deviceProp.totalGlobalMem)/ 1048576).setScale(0,RoundingMode.UP).floatValue()+" Мбайт"; "Максимальное число потоков на блок: "+deviceProp.maxThreadsPerBlock; "Максимальный размер блока: "+deviceProp.maxThreadsDim[0]+"x"+ deviceProp.maxThreadsDim[1]+"x"+deviceProp.maxThreadsDim[2]; "Максимальный размер сетки: "+deviceProp.maxGridSize[0]+ "x"+deviceProp. maxGridSize[1]+"x"+deviceProp.maxGridSize[2]; "Частота: "+new BigDecimal((float)(deviceProp.clockRate)/1000000).setScale (2, RoundingMode.UP).floatValue()+" ГГц"; "Лимит времени выполнения: "+(deviceProp.kernelExecTimeoutEnabled!= 0?"Yes":"No");

>Параметры CUDA-устройства. Продолжение Получение ‘CUDA Driver Version‘ и ‘CUDA Runtime Version‘: int driverVersionArray[] = Параметры CUDA-устройства. Продолжение Получение ‘CUDA Driver Version‘ и ‘CUDA Runtime Version‘: int driverVersionArray[] = new int[1]; cudaDriverGetVersion(driverVersionArray); int runtimeVersionArray[] = new int[1]; cudaRuntimeGetVersion(runtimeVersionArray); "CUDA Driver Version: "+(driverVersionArray[0]/1000)+"."+ (driverVersionArray[0]%100); "CUDA Runtime Version: "+(runtimeVersionArray[0]/1000)+"."+ (runtimeVersionArray[0]%100); Параметры ОС и другие параметры можно определить с помощью метода ‘System.getProperty‘: "Операционная система: "+System.getProperty("os.name"); "Архитектура ОС: "+System.getProperty("os.arch"); "Версия ОС: "+System.getProperty("os.version");

>Подключение CUBIN модулей и выполнение их на CUDA-устройстве из Java-программы  String folder= Подключение CUBIN модулей и выполнение их на CUDA-устройстве из Java-программы String folder="cuda//";//директория в которой находятся .cu и .cubin KernelLauncher kernelLauncher=KernelLauncher.create(folder+ "func.cu","func","");//подключение функции “func” из “func.cu” файла int BLOCKSIZE = 50;//Особенности распараллеливания программы при выполнении на GPU задаются параметрами ‘BLOCKSIZE‘, ‘nThreads‘, ‘nBlocks‘ dim3 nThreads = new dim3(BLOCKSIZE, 1, 1); dim3 nBlocks = new dim3(N/BLOCKSIZE, 1, 1); CUdeviceptr dOutput = new CUdeviceptr(); //Создание указателя на массив, расположенный в GPU CUdeviceptr dInput = new CUdeviceptr(); //Создание указателя на массив, расположенный в GPU cuMemAlloc(dOutput, N*Sizeof.FLOAT);//Выделение памяти для указателя (для массива типа float длиной N значений) cuMemAlloc(dInput, N*Sizeof.FLOAT); cudaMemcpy(dInput,Pointer.to(masInput),N*Sizeof.FLOAT, cudaMemcpyHostToDevice);//Помещение массива ‘masInput‘ в память GPU (‘cudaMemcpyHostToDevice‘) kernelLauncher.setup(nBlocks, nThreads).call(N, dInput,dOutput);//Выполнение функции ‘func‘ из файла ‘func.cu‘ на GPU cudaMemcpy(Pointer.to(masOutput),dOutput,N*Sizeof.FLOAT,cudaMemcpyDeviceToHost);//После вычислений на GPU результат в виде массива возвращается (‘cudaMemcpyDeviceToHost‘) в основную часть программы cuMemFree(dInput);//Затем необходимо освободить память GPU cuMemFree(dOutput);

>Создание ‘.cu‘ файла  Необходимо создать файл с расширением ‘.cu‘, который затем редактируется программой Создание ‘.cu‘ файла Необходимо создать файл с расширением ‘.cu‘, который затем редактируется программой ‘Блокнот‘ или другим текстовым редактором. Можно открыть ‘.txt‘ файл ‘Блокнотом‘ и сохранить его как ‘.cu‘ файл, добавив после имени файла ‘.cu‘ и указать в типе файла ‘Все файлы‘ вместо ‘Текстовые документы (.txt)‘. В ‘.cu‘ файле пишется код на языке программирования С с расширениями для CUDA. В ‘.cu‘ файле пишутся функции, которые вызываются либо из Java-программы, либо из других функций ‘.cu‘ файла. Перед объявлением функции указывается директива ‘extern "C"‘. Затем указывается тип функции ‘__device__‘ (выполняется на GPU, вызывается из ‘.cu‘ файла), ‘__global__‘ (выполняется на GPU, вызывается из Java-программы) или ‘__host__‘ (выполняется на СPU, вызывается из Java-программы).

>Вычисление синуса Входящими переменными являются переменные Java-программы. При описании входящих переменных функции указывается их Вычисление синуса Входящими переменными являются переменные Java-программы. При описании входящих переменных функции указывается их тип и имя через запятую (если входящей переменной является массив, то перед его именем указывается ‘*‘). Затем необходимо задать сетку и индексы потоков (переменными ‘threadIdx.x‘, ‘blockDim.x‘, ‘blockIdx.x‘). Данные переменные действительны только в пределах функции, которая выполняется на GPU. Следующая функция вычисляет синус для каждого элемента массива ‘input‘ и записывает результат в массив ‘output‘ extern "C" __global__ void func(float *input, float *output){ int i = threadIdx.x + blockDim.x*blockIdx.x; output[i]=sin(input[i]); }

>Сложная структура функции  При более сложной структуре функции сложно описать параллельные вычисления в Сложная структура функции При более сложной структуре функции сложно описать параллельные вычисления в одной функции, поэтому создается вторая вспомогательная функция, которая вызывается из первой extern "C" __device__ float loop(int b,int N, float *input){ <... тело функции ....> return (float) <... возвращаемое значение ...>; } extern "C" __global__ void func(int N, float *input, float *output){ int b = threadIdx.x + blockDim.x*blockIdx.x; output[b]=loop(b,N,input); }

>Компилятор NVCC и создание ‘.cubin‘ файла  Созданный ‘.cu‘ файл необходимо откомпилировать NVCC компилятором, Компилятор NVCC и создание ‘.cubin‘ файла Созданный ‘.cu‘ файл необходимо откомпилировать NVCC компилятором, чтобы получить ‘.cubin‘ файл. Имена обеих файлов должны совпадать. Для компиляции необходимо открыть ‘Visual Studio 2008 Command Prompt‘ (‘Пуск‘->‘Все программы‘->‘Microsoft Visual C++ 2008 Express Edition‘-> Visual Studio Tools‘->‘Visual Studio 2008 Command Prompt‘). Затем необходимо выполнить команду ‘cd‘ - установить адрес, где находится ‘.cu‘ файл, например: cd "C:\" Затем необходимо откомпилировать ‘.cu‘ файл, выполнив команду (‘func.cu‘ – имя файла): nvcc -cubin func.cu -o func.cubin 64-разрядная версия NVCC компилирует ‘.cu‘ файл в 64-bit режиме, а 32-разрядная в 32-bit режиме. 32-разрядная версия NVCC может скомпилировать ‘.cu‘ файл в 64-bit режиме с использованием параметрa -m64, а 64-разрядная в 32-bit режиме с использованием параметра -m32: nvcc -m64 -cubin func.cu -o func.cubin nvcc -m32 -cubin func.cu -o func.cubin При компиляции ‘.cu‘ файла для GPU с вычислительными возможностями версии 2 необходимо добавлять параметр -arch=sm_20, иначе возникает ошибка ‘CUDA_ERROR_INVALID_SOURCE‘.

>Сборка программы  Созданный Java-проект можно экспортировать в запускаемый ‘.jar‘ файл. Для этого в Сборка программы Созданный Java-проект можно экспортировать в запускаемый ‘.jar‘ файл. Для этого в навигаторе проекта (Project Explorer) необходимо нажать правой кнопкой мыши и в выпавшем меню выбрать пункт ‘Export‘. В открывшемся окне в разделе ‘Java‘ необходимо выбрать пункт ‘Runnable JAR file‘. В следующем окне в поле ‘Launch configuration‘ указывается Java-класс, с которого начинается выполнение программы, в поле ‘Export destination‘ указывается месторасположение экспортируемого файла, в ‘Library handling‘ выбирается пункт ‘Extract required libraries into generated JAR‘. Созданный ‘.jar‘ файл помещается в отдельную папку, в которую также необходимо поместить ‘.dll‘ файлы JCUDA-библиотек. Можно поместить ‘.dll‘ файлы для различных версий ОС, чтобы повысить платформонезависимость программы. В созданную папку помещаются ‘.cu‘ и ‘.cubin‘ файлы, с учетом относительных путей размещения этих файлов, прописанных в программе. Можно поместить ‘.cu‘ и ‘.cubin‘ файлы для различных версий ОС. В таком случае имена файлов должны отличаться, а написанная программа должна определять, какой ‘.cu‘ файл подключать для компьютера, на котором данная программа запускается.

>Параметр ‘BLOCKSIZE’  Параметр ‘BLOCKSIZE’ (размер блока - показывает число потоков в блоке) должен Параметр ‘BLOCKSIZE’ Параметр ‘BLOCKSIZE’ (размер блока - показывает число потоков в блоке) должен быть кратным числу элементов в массиве, иначе вычисления будут происходить неправильно. В то же время изменение ‘BLOCKSIZE’ не приводит к существенному изменению скорости выполнения программы, за исключением случаев, когда ‘BLOCKSIZE’ существенно меньше числа ядер в GPU и больше 250 (в данных случаях программа выполняется медленнее).

>Отклонения в результатах вычислений  Если вычисления на GPU выполняются в одинарной точности - Отклонения в результатах вычислений Если вычисления на GPU выполняются в одинарной точности - возникают отклонения в результатах, от результатов, полученных при вычислениях на CPU. При одних параметрах счета отклонения невелики, при других - отклонения возрастают. При необходимости повышение точности вычислений на GPU должно быть обеспечено использованием GPU с поддержкой двойной точности вычислений.

>Основные характеристики при выполнении программы на CUDA - доля времени расчета на GPU от Основные характеристики при выполнении программы на CUDA - доля времени расчета на GPU от времени выполнения программы; - ускорение вычислений на GPU по сравнению с CPU; - оптимальный размер блока BLOCKSIZE; - отклонения в результатах вычислений на GPU от CPU.

>Прогнозирование времени выполнения программы для различных GPU  Прогнозирование времени выполнения программы для различных Прогнозирование времени выполнения программы для различных GPU Прогнозирование времени выполнения программы для различных GPU происходит путем сравнения теоретической производительности для GPU, на котором происходили вычисления, с теоретической производительностью других GPU. Теоретическая производительность для двойной точности для видеокарт семейства GeForce примерно в 10 раз меньше, чем для одинарной точности, для семейства Tesla примерно в 2 раз меньше.