Первая лекция:
- Статья 01: как установить NVIDIA драйвер и CUDA, пример запуска профилировщика NVIDIA Nsight Compute для задачи суммирования двух векторов
- Статья 02: как запустить санитайзер compute-sanitizer --tool initcheck для проверки что вся видеопамять была инициализирована (не считываем случайный мусор)
Вторая лекция:
- Статья 03: на примере задачи суммирования элементов массива исследуем и профилируем:
- 3.1) Как отладить ошибку через
printf
со стороны кернела на видеокарте - 3.2) Иногда кэш спасает скорость работы кернела вопреки non-coalesced паттерну доступа
- 3.3) Как помешать кэшу спасать нас
- 3.4) Как форсировать чтения памяти быть non-coalesced
- Статья 04: на примере задачи суммирования элементов массива через редукцию в локальной памяти:
- 4.1) Как проверить нет ли в кернеле гонок в обращениях к локальной памяти (compute-sanitizer --tool racecheck)
- 4.2) Какие есть виды гонок (на примере RAW, WAR гонок)
- Статья 05: на примере задачи суммирования элементов массива через редукцию проверяем что обращение за пределами массива легко поймать (compute-sanitizer --tool memcheck)
- Статья 06: на примере задачи суммирования элементов массива через редукцию анализируем поведение программы через Timeline визуализацию в NVIDIA Nsight Compute
-
Пример профилирования и ускорения: случай когда тормозит трансфер данных на Vulkan (
VRAM -> CPU
) - помогает увидеть timeline + видеть в логе строку видаprocessing done in 1234 s = 20% IO + 10% CPU + 5% upload to VRAM + 30% GPU + 35% read from VRAM
+ взвешивать килограммами -
Пример профилирования и ускорения: в случае когда CPU-часть занимает существенную долю времени (например 40%) - какой потенциальный прирост от обработки в два потока? А если ситуация другая - 80% времени CPU и 20% GPU? Что с этим делать?
-
Пример профилирования и ускорения: если есть переход из локальной системы координат камеры A в мир, и затем из мира в локальную систему координат второй камеры B
-
Пример профилирования и ускорения: если какой-то кусок-патч картинки нужно целиком подвергнуть сложной трансформации - вместо проецирования каждого пикселя этого патча - проецируем центр и производные рассчитываем, получили чуть загрубленую трансформацию
-
Пример подхода: пусть обработка выглядит как IO -> CPU -> GPU, как решить сколько потоков нужно на IO? Сколько на CPU?
-
Пример подхода: если есть рандомность в алгоритме - как реализовать ее в модели массового параллелизма? А если хочется детерминизма результата?
-
Пример подхода: как в растеризационном пайплайне рассчитать площадь треугольников измеряемых в количестве их пикселей-фрагментов (которые выжили после depth-test в фрагментном шейдере)?
-
Пример подхода: как распределить по узлам кластера задачу "для каждого треугольника перечислить перечен камер которые его видят". А что если треугольники не влезают в память? Как сделать это с out-of-core свойством?
-
Пример подхода: если надо писать что-то многопоточно в пиксели картинки, как это делать без точки синхронизации, т.е. без одного общего лока? (случай CPU, случай GPU, случай 64-битных данных, случай произвольных по размеру)
-
Пример подхода: если задача на некоторой 2D решетке (например картинка) и не требует абсолютной точности - то можно часто прорядить и работать в каждом втором ряду-столбике. А потом несколько итераций уточнить ответ уже для каждого пикселя.
-
Пример подхода: если задача на некоторой 2D решетке (например картинка) и не требует абсолютной точности - то можно сначала работать на х32 раза меньшей детальности, затем на х16, ..., наконец, на оригинальном разрешении. Очень много общего с предыдущей идеей про прореживание.
-
Пример подхода: как ускорить растеризацию треугольников?
-
Пример алгоритма: пусть на видеокарте есть code divergence в coarse-to-fine схеме в задаче подобной приложению чартов в атлас. Как оставить схему но получить ускорение разобравшись с code divergence?
-
Пример алгоритма: пусть на CPU есть алгоритм СНМ (система непересекающихся множеств) с тяжелыми inplace вычислениями "кто из соседних пикселей действительно сосед - т.е. дорогая проверка стоит ли провести с ним union". Как ускорить за счет делегирования на видеокарту проверку "соседства" оставив СНМ-операции на процессоре?
-
Пример алгоритма: пусть на CPU есть алгоритм СНМ (система непересекающихся множеств), как ускорить за счет многопоточности?
-
Пример алгоритма: если есть алгоритм в котором трассировка лучей, как его ускорить на видеокарте (возможно с потерей точности)? Как переделать алгоритм в котором есть не разбиваемая задача "трассируй луч перешагивая по ячейкам пространства" в парадигму "есть N задач каждая O(1)".
Этот проект иллюстрирует как написать код для видеокарты посредством OpenCL и затем скомпилировать его в т.ч. для исполнения через CUDA. Это дает замечательную возможность использовать инструментарий CUDA:
- профилировщик - NVIDIA Nsight Compute: позволяет посмотреть timeline выполнения кернелов, насколько какой кернел насытил пропускную способность видеопамяти/локальной памяти или ALU, число используемых регистров и локальной памяти (и соответственно насколько высока occupancy), а так же он часто явно подскажет в чем может быть основная потеря скорости работы кернела
- санитайзер - compute-sanitizer --tool memcheck (бывший cuda-memcheck): позволяет проверить что нет out-of-bounds обращений к памяти (если есть - укажет проблемную строку в кернеле)
- санитайзер - compute-sanitizer --tool racecheck (бывший cuda-memcheck): позволяет проверить что нет гонок между потоками рабочей группы при обращении к локальной памяти (т.е. что нигде не забыты барьеры). Если гонка есть - укажет на ее характер (RAW/WAR/WAW) и на строки в кернеле (обеих операций участвующих в гонке)
Здесь предложена трансляция в CUDA на примере задачи C=A+B, трансляция не исчерпывающая, но во многих простых случаях должна работать. Она реализована благодаря файлу с макросами которые транслируют OpenCL вызовы в CUDA вызовы - libs/gpu/libgpu/cuda/cu/opencl_translator.cu.
Дополнительные ориентиры:
- CMakeLists.txt: Поиск CUDA-компилятора, добавление для NVCC компилятора флажка 'сохранять номера строк' (нужно чтобы cuda-memcheck мог указывать номера строк с ошибками), компиляция через
cuda_add_executable
. - aplusb.cu: CUDA-кернел транслируется из OpenCL-кернела посредством макросов, вызов кернела через функцию
cuda_aplusb
- main01_aplusb.cpp: декларация функции
cuda_aplusb
, инициализация CUDA-контекста, вызов функции вызывающий кернел - Запись лекции с одного из прошлых прочтений - есть пояснения про этот транслятор и про отличия CUDA