5. Параллельные вычисления Как? Одновременное исполнение независимых вычислений на разных процессорах с последующей синхронизацией Проблемы: Правило Амдала: ускорение=P – доля программы, которую можно распараллелить, N – число потоков исполнения Слишком маленькие независимые блоки много затрат на синхронизацию Сложно программировать (много объектов, синхронизация, критические секции)
7. Высокая производительность: объективные проблемы Задержки (Latency) Часто определяют производительностьПример: Умножение матриц наивным способом - медленный доступ «по столбцам»: особенности памяти (и кэшей, если не повезло) Синхронизация: всегда непроизводительное ожидание Скорость чтения/записи в память (Bandwidth) Часто определяет производительностьSSE на Core i7: две векторные (16 байт) операции на такт(96 байт/такт), скорость доступа к памяти порядка 6 байт/такт (или 1-1.5 байта на ядро) Нужна быстрая память, кэши частично спасают Нужно повышение сложности: больше операций над данными в регистрах. Распределенные системы: линейный рост bandwidth Арифметическая интенсивность (операций/единица данных) a = b+c: 1 операция, 2 чтения, 1 запись Распараллеливание может стать бессмысленным, весь выигрыш будет съеден пересылками Чтобы был выигрыш от параллельности, алгоритмы должны быть либо сложнее O(n),либо для O(n) – константапри O – большая.
9. «Старые видеокарты» (2003-2006) Специализация под 3D-графику (отрисовка2D-проекции сцены) Фиксированные стадии обработки (вершины-геометрия-пиксели) С 2001 – программируемые шейдеры (GeForce3, OpenGL 1.5) Высокая производительность за счет: Архитектуры, сделанной ровно под одну задачу Высокой скорости линейного чтения памяти Отсутствия синхронизации внутри стадии обработки
10. 2001-2006: «Многообещающие результаты» ~3000 научных публикацийС общим смыслом «вот на следующих поколениях оборудования все будет хорошо...» Первые средства разработки для не-графики: Brook, Sh Результаты: Ускорение линейной алгебры в разы (относительно CPU), но single precision Сортировка: ускорение в разы, GPUTeraSortвыиграла Sort Benchmark 2006г Промышленное использование: Обработка сигналов Обработка цифрового кино (поток 200-500Mb/sec)
11. Проблемы начального этапа Неудобная парадигма «Потоковых вычислений» (Stream Computing): «микропрограммы» (шейдеры) – обработка одного элемента входного потока Никакого взаимодействия между потоками Многие задачи очень неудобно программировать Только одинарная точность вычислений Неудобство средств разработки
12. 2006: NVidia CUDA и G80 Compute Unified Device Architecture Принципиально новая (для GPU) архитектура Взаимодействие между потоками вычислений Произвольный код вместо шейдеров Иерархическая структура памяти 345 GFLOP/s (single), 87Gb/sec memory bandwidth Средства программирования общего назначения (CUDA): Вычислительный код на языке «С» Компактная архитектура запуска вычислителей Много готовых вычислительных примеров Готовые библиотеки BLAS и FFT в поставке Прекрасная поддержка разработчиков и университетов 2007: NVidia Tesla – «Supercomputer on Desktop» - «видеокарты без видеовыхода», только вычисления
13. 2006: интерес других производителей ATI (AMD): Инициатива Close-To-Metal: публикация спецификаций кода, надежда на отказ от OpenGL и появление нормальных компиляторов FireStream – первый ускоритель для вычислений а не видеокарта Остаются в парадигме потоковых вычислений Cell (IBM-Sony-Toshiba) 8 ядер с локальной памятью (256кб на ядро) Быстрая внешняя шина (25Gb/sec) 230 GFLOP/s single, 15-20GFLOP/s double PlayStation 3 и плата в серверы
14. 2007-2008: быстрое взросление >2000 публикаций про использование CUDA Топовые конференции (Supercomputing) Хорошие готовые библиотеки Новое оборудование: Поддержка двойной точности у всех NVidia: G200 – 800 GFLOP/s single, 100 – double, ATI HD4870: 1000/240 GFLOP/s single/double, но все еще потоковое программирование IBM PowerXCell: ~200/100 GFLOP/s
15. Ускорение приложений на CUDA 2008 г.: NVidia 8800GTX в сравнении с AMD Opteron 248 (2.2GHz, 2 ядра)
16. Современный этап: оборудование Видеокарты: ATI R800 2.7TFLOP/s single, 500 GFLOP/s double Поддержка OpenCL (не только потоковые вычисления) NVidia GF100 (Fermi): Tesla 1.2 TFLOP/s single, 600 GFLOP/s double Geforce GTX 480: 1.3 TFLOP/s single, ~180 GFLOP/s double Для сравнения, топовые x86 CPU: 80-110GFLOP/s (double) GPU-кластеры Промышленные решения (Cray, IBM, Т-платформы) Суперкомпьютеры: Nebulae - №2 в Supercomputer Top500 по реальной производительности, №1 по пиковой.
17. Современный этап: программы Стандарт OpenCL (лето-осень 2009): переносимые GPU-программы Для пользователяускорение multimedia, обработка изображений и видео, 3D-моделирование, инженерные программы... Для программиста много готовых библиотек: линейная алгебра, обработка сигналов, computer vision Для ученого: поддержка в расчетных программах Общего назначения: Matlab, Mathematica Специализированные пакеты: проектирование, компьютерная химия, компьютерная биология, молекулярная динамика
19. Наглядная агитация 4xTesla == Cray XT4 на задачах молекулярной биологии (AMBER11)(слайд с NVidia GPU Technology conference, 22 сентября 2010)
20. NVidia CUDACompute Unified Device Architecture Общий обзор «сверху вниз» Как устроено оборудование И как это отражается в логической структуре программ Минимальные примеры программ Некоторые тонкие моменты Обзор средств программиста и дополнительных библиотек Нет задачи и возможности заменить чтение документации и самостоятельные упражнения
21. Общая схема исполнения программ Все стадии независимы, одновременно можно: Передавать в видеопамять Считать (другие данные) Передавать из видеопамяти
30. Логическая организация программ Десятки тысяч – миллионы независимых потоков в программе Поток – обрабатывает часть входных данных Потоки объединены в блоки Общая shared memory Синхронизация Блоки объединены в grid Синхронизации нет Grid of Thread Blocks
31. Логическая организация (2) Блок исполняется на одном мультипроцессоре Разные блоки – на разных Порядок не определен Может быть несколько блоков на SM одновременно Нумерация потоков в блоке: 1D, 2D или 3D Нумерация блоков: 1D,2D Каждый поток знает свое положение в сетке и блоке
33. Параметры SM – не догма G92: 16384регистров, до 1024 потоков на SM GF100 (Fermi) 32768 регистров и 32/48 процессоров на SM 1536 потоков (48 warp) на SM 64kb shared memory+L1-cache (16/48 или 48/16 shared/cache) Другие параметры другой оптимальный код Другие улучшения Атомарные операции (G86, G92) Поддержка двойной точности (G200, GF100)
34. Пример: cложение двух векторов Код GPU: складываем два элемента: __global__ void VecAdd(float* A,float* B, float* C, int N) { int i = blockDim.x * blockIdx.x + threadIdx.x; if (i < N) C[i] = A[i]+B[i]; } threadIdx – предопределенная переменная (3-компонентный вектор) – номер потока в блоке blockDim – размерность одного блока (3D) blockIdx – номер блока в сетке (2D)
35. код на CPU // аллоцируем память на GPU size_t size = N * sizeof(float); cudaMalloc((void**)&d_A, size); cudaMalloc((void**)&d_B, size); cudaMalloc((void**)&d_C, size); // копируем данные на GPU cudaMemcpy(d_A, h_A, size, cudaMemcpyHostToDevice); cudaMemcpy(d_B, h_B, size, cudaMemcpyHostToDevice); // запускаем kernel на GPU (1D сетка и блок): int Nthreads = 256; int Nblocks = (N+Nthreads-1)/Nthreads; VecAdd<<<Nblocks,Nthreads>>>(d_A, d_B, d_C, N); // получаем результаты с GPU cudaMemcpy(h_C, d_C, size, cudaMemcpyDeviceToHost);
36. CUDA:С/C++ с небольшими изменениями Спецификаторы типа функции: __global__- GPU, может быть вызвана с CPU (с параметрами) __device__ - GPU, вызывается из __global__ __host__ - CPUРекурсия и указатели на функции: только Fermi (GF100) Спецификаторы местоположения переменной __device__ - глобальная память, R/W __constant__ - константная память, R/O __shared__ - shared memory, R/W, локальна в блоке Автоматические переменные – могут быть в регистрах, если известен размер и регистров достаточно. Иначе – local memory, т.е. живут в медленной глобальной памяти
37. Типы данных и переменные Скалярные:char, short, int, long,longlong, float, double (и беззнаковые для целых) Векторные: <=32 бит/компонент – до 4 элементов (uchar2,float4) 64 бита – 2 элемента (double2, longlong2) Векторных операций НЕТ: c.x=a.x+b.x работает, c=a+b - нет dim3 – тип для задания размера сетки блоков Встроенные переменные: gridDim, blockDim– размер сетки и блока (dim3) blockIdx, threadIdx– номер блока и потока (dim3) warpSize – ширина SIMD (пока всегда 32)
38. Двумерные блоки например, для двумерных моделей На CPU: dim3 blockSize(16, 16); // некратным размером сетки пренебрегаем... dim3 gridSize(width/blockSize.x, height/blockSize.y); myKernel<<<gridSize,blockSize>>>(…..) На GPU uint x = blockIdx.x*blockDim.x+threadIdx.x; uint y = blockIdx.y*blockDim.y +threadIdx.y;
39. Синхронизация, атомарные операции Глобальная синхронизация всех блоков – завершение kernel __syncthreads() – синхронизация потоков блока Атомарные сложения, вычитания и т.п. (atomicAdd() и т.д.) - G86+ Shared или Global memory (G86 – только global) Атомарное исполнение Медленные
41. Тонкости Оптимальный доступ к глобальной памяти Особенности доступа к shared memory Условные операции Распределение блоков по процессорам
42. Доступ к глобальной памяти Сложение векторов, 16 сложений на thread: ___________________________________________________ int j,base = (blockDim.x*blockIdx.x+threadIdx.x)*16; for(j=0;j<16;j++) C[base+j] = A[base+j] + B[base+j] Thread0: A[0], A[1] Thread1: A[16], A[17] Весь Warp обращается c «размахом» в 512 слов ______________________________________________________ int j,base = (blockDim.x*blockIdx.x)*16+threadIdx.x; for(j=0;j<blockDim.x*16;j+=blockDim.x) C[base+j] = A[base+j] + B[base+j] Thread0: A[0], A[blockDim.x]... Thread1: A[1], A[blockDim.x+1].... Весь Warp обращается к соседним словам Второй вариант – coalesced access: доступ к соседним адресам
43. Доступ к глобальной памяти - 2 Прямые рекомендации из Programming Guide: Оптимально, когда все потоки одного (полу)-warp*обращаются к одному 128-байтному сегменту Для 16-байтных элементов (вектор) – два подряд 128-байтных сегмента. В правильном порядке – k-йthread к k-му элементу в сегменте. Идеальная ситуация: Array[base + treadIdx.x]Array[base] – выровнен на 128 байт
44. Разделяемая память Разделена на банки 16 банков на G80-G200, 32 на GF100 Interleave 4 байта Два потока в 1 банк =>конфликт, каждый конфликт – 4 такта ожидания Оптимальный доступ array[threadIdx]
45. Нет конфликта банков Bank 0 Thread 0 Bank 0 Thread 0 Bank 1 Thread 1 Bank 1 Thread 1 Bank 2 Thread 2 Bank 2 Thread 2 Bank 3 Thread 3 Bank 3 Thread 3 Bank 4 Thread 4 Bank 4 Thread 4 Bank 5 Thread 5 Bank 5 Thread 5 Bank 6 Thread 6 Bank 6 Thread 6 Bank 7 Thread 7 Bank 7 Thread 7 Bank 15 Thread 15 Bank 15 Thread 15
46. Конфликт банков Bank 0 Bank 1 Thread 0 Bank 2 Thread 1 Bank 3 Thread 2 Bank 4 Thread 3 Bank 5 Thread 4 Bank 6 Bank 7 Thread 8 Thread 9 Bank 15 Thread 10 Thread 11 GF100: несколько чтений разных потоков по одному адресу (не банку) – не приводят к конфликту (broadcast данных)
47. Условные операции SIMD поэтому если в одном warp разные ветки if, их исполнение будет последовательным: if (threadIdx.x %2) { четные ветки стоят, нечетные работают} else { четные работают, нечетные стоят }
48. Распределение блоков по SM Не меньше одного блока т.е. блок должен пролезать по ресурсам: Общее число потоковв блоке < лимита оборудования Требуемое количество shared memory< лимита Общее количество регистров (регистров на поток * число потоков) < лимита Несколько блоков на SM – если достаточно ресурсов (регистров, shared memory, потоков) G80-G200 – передача параметров в shared mem, 2 X 8192 – не влезет в 16 килобайт
49. Загрузка SM (occupancy) Отношение числа работающих warps к из возможному количеству В общем случае, к 100% надо стремиться: Прячется латентность доступа к памяти Но мало регистров (G92 – 16 регистров/thread) Альтернативный подход: Мало потоков (128-192-256 на SM) Зато много регистров на поток Полезно для блочных алгоритмов Подробнее: Volkov, V. 2010. Use registers and multiple outputs per thread on GPUhttp://www.cs.berkeley.edu/~volkov/volkov10-PMAA.pdf
51. Продолжение..... Документация NvidiaОчень хорошая!http://developer.nvidia.com/object/cuda_download.html CUDA Programming Guide CUDA Reference Guide CUDA Best Practices Guide Примеры SDK и документация к нимhttp://developer.download.nvidia.com/compute/cuda/sdk/website/samples.html Форумы Nvidiahttp://forums.nvidia.com/index.php?showforum=62
52. Программные средства CUDA Toolkit – компилятор + библиотеки Интегрируется с Visual C++ (добавление правил компиляции для .cu) Отладчик – gdb (командная строка) Visual Profiler – не на уровне отдельных строк кода Parallel Nsight Пошаговая отладка Глобальный профайлинг Standard (бесплатная) и Professional (бесплатная для ВУЗов) версии CUDA-x86 Был эмулятор, но в Cuda 3.x удален PGI обещает компилятор
53.
54. Компиляция в внешний .cubin-файл и загрузка на runtimeФинальную компиляцию реальные коды делает драйвер видеокарты NVCC CPU Code PTX Code Virtual Physical PTX to Target Compiler G80 … GPU Target code
55. Два API CUDA C API: реализуется внешней библиотекой cudart Высокоуровневое, более удобное удобная поддержка нескольких контекстов (например для использования в многопоточных программах) CUDA Driver API: Аналогично по возможностям Не требует дополнительных библиотек Больше работы программисту
56. Альтернативы CUDA OpenCL CAL/IL (Compute Abstraction Layer/Intermediate Language) Только видеокарты ATI Потоковое программирование Плохо поддерживается DirectX 11 (DirectCompute) Только Windows
57. OpenCL Многоплатформенный стандарт Необычайно похож на CUDA Driver API Детали: Язык C для kernels, другие ключевые слова Компиляция из исходных текстов на ходу («в драйвере») Уже две версии (1.0 и 1.1) Нестандартная конфигурация поддерживается через «расширения»
58. Сложение векторов Код GPU: __kernel void VectorAdd(__global const float* a,__global const float* b,__global float* c, int N) { int iGID = get_global_id(0); if (iGID < N) c[iGID] = a[iGID] + b[iGID]; }
59. Код CPU // Создаем контекст GPU (до того был поиск устройств...) Context = clCreateContext(0, 1, &cdDevice, NULL, NULL, &ciErr1); // и очередь команд CommandQueue = clCreateCommandQueue(Context, cdDevice, 0, &ciErr1); // Аллоцируем массив(ы) на GPU cmDevSrcA = clCreateBuffer(Context, CL_MEM_READ_ONLY, sizeof(cl_float) * szGlobalWorkSize, NULL, &ciErr1); …. И еще два аллоцирования .... // Создаем (компилируем) программу из исходных текстов cpProgram = clCreateProgramWithSource(Context, 1, (const char **)&cSourceCL, &szKernelLength, &ciErr1); // создаем из нее kernel ckKernel = clCreateKernel(cpProgram, "VectorAdd", &ciErr1); // задаем ему аргумент(ы) clSetKernelArg(ckKernel, 0, sizeof(cl_mem), (void*)&cmDevSrcA); .... И еще три аргумента ...... // В очередь команд помещаем копирование данных и запуск kernel, все асинхронно clEnqueueWriteBuffer(CommandQueue, cmDevSrcA, CL_FALSE, 0, sizeof(cl_float) * szGlobalWorkSize, srcA, 0, NULL, NULL); .... И еще одно копирование.... clEnqueueNDRangeKernel(CommandQueue, ckKernel, 1, NULL, &szGlobalWorkSize, &szLocalWorkSize, 0, NULL, NULL); // В очередь команд помещаем синхронное чтение результата clEnqueueReadBuffer(CommandQueue, cmDevDst, CL_TRUE, 0, sizeof(cl_float) * szGlobalWorkSize, dst, 0, NULL, NULL);
60. CUDA OpenCL AMD: Porting CUDA to OpenCLhttp://developer.amd.com/zones/OpenCLZone/Pages/portingcudatoopencl.aspx
61. Переносимость и производительность Переносимыйкод (NVidia, ATI,x86, Cell) Производительность не переносится: Либо используем векторные типы на векторных архитектурах (ATI, Cell) и получаем непереносимый код Либо код получается переносимый и в разы медленнее Перенос CUDA OpenCL – потеря 20-30%. Компилятор?? CUDA всегда будет впереди т.к. нет этапа согласований с рабочей группой
62. OpenCL и видеокарты ATI Человеческий интерфейс (в отличие от CAL/IL) HD5870 – производительность примеров SDK - на уровне GTX280 (в разы ниже ожидаемого) HD4xxx – производительность плохая т.к. нет shared memory (эмуляция через глобальную) Плохая документация (в сравнении с...) Но что делать, если попалось такое оборудование и не хочется ассемблера.....
66. На каких задачах GPU медленнее? Задачи, которые не распараллеливаются на тысячи независимых потоков State machine, операции со строками Обход (одного большого) графа Неподходящий размер working set: На GPU 1-3 Mbочень быстрой памяти На CPU 4-8-12-64Mb L2 cache Случайный доступ к большому массиву Видеопамять медленнее при случайном доступе Низкая арифметическая интенсивность
67. Как с этим жить? Дешевый способ увеличить производительность в 5-10-100 раз, в зависимости от задачи. Рекомендации по самостоятельному использованию: Учиться на CUDA Использовать OpenCL если действительно нужна переносимость Если нет GPU, а попробовать хочется Использовать ATI и CAL/IL если нужна максимальная производительность на потоковых операциях Используйте mixed-precision схемы (на CPU тоже очень полезно!)
68. Как с этим жить - 2 Используйте библиотеки: CUBLAS, CUFFT, CUSPARSE – входят в CUDA SDK CUDPP, MAGMA, GATLAS, OpenCV – 3rd party и бесплатны ACML-GPU для ATI .... библиотек уже много.... Расширения для Matlab Parallel Computing Toolbox (R2010b) Jacket GPUmat, ViennaCL
69. CUDA: Университетские курсы University of Urbana самый первыйдоступный курс курс: http://courses.engr.illinois.edu/ece498/al/Syllabus.html (доступны слайды и запись голоса). Курс по CUDA, читаемый на ВМКЗаписи 2009 года: http://esyr.org/lections/audio/cuda_2009_summer/ и http://esyr.org/video/cuda/Записи 2010 года: ftp://geophyslab.srcc.msu.ru/Events/CUDA2010/Google-группа к этому курсу: http://groups.google.ru/group/cudacsmsusu/
70. CUDA: книги На русском А. В. Боресков, А. А. Харламов Основы работы с технологией CUDA (+ CD-ROM)http://www.ozon.ru/context/detail/id/5080841/ На английском Programming Massively Parallel Processors: A Hands-on Approach, David B. Kirk, Wen-mei W. Hwuhttp://www.amazon.com/Programming-Massively-Parallel-Processors-Hands/dp/0123814723/ref=sr_1_1?ie=UTF8&s=books&qid=1284301217&sr=8-1 CUDA by Example: An Introduction to General-Purpose GPU Programming by Jason Sanders and Edward Kandrothttp://www.amazon.com/CUDA-Example-Introduction-General-Purpose-Programming/dp/0131387685/ref=sr_1_4?ie=UTF8&s=books&qid=1284301217&sr=8-4
71. Литература OpenCL Сайт с описанием стандарта: http://www.khronos.org/opencl/далее по ссылкам. Обзор «OpenCL: A Parallel Programming Standard for Heterogeneous Computing Systems» http://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=5457293 Документы AMD по миграции с CUDA на OpenCL: http://developer.amd.com/documentation/articles/pages/OpenCL-and-the-ATI-Stream-v2.0-Beta.aspx#four http://developer.amd.com/gpu/ATIStreamSDK/pages/TutorialOpenCL.aspx Документы NVIdia по программированию OpenCL: Programming Guide: http://developer.download.nvidia.com/compute/cuda/3_1/toolkit/docs/NVIDIA_OpenCL_ProgrammingGuide.pdf Best Practices Guide: http://developer.download.nvidia.com/compute/cuda/3_1/toolkit/docs/NVIDIA_OpenCL_BestPracticesGuide.pdf Code Samples: http://developer.download.nvidia.com/compute/opencl/sdk/website/samples.html Книги: The OpenCL Programming Book http://www.fixstars.com/en/company/books/opencl/
72. Литература: Разное Debunking the 100X GPU vs CPU Myth: an Evaluation of Throughput Computing on CPU and GPUhttp://www.hwsw.hu/kepek/hirek/2010/06/p451-lee.pdf Относиться с осторожностью, авторам статьи (инженерам Intel) обидно за свои процессоры и они тянут одеяло в свою сторону