itmo_conspects

Лекция 13. Обучение на графических процессорах

В процессе развития технологий появились графические процессоры (Graphical Processing Unit, GPU), которые создавались для отрисовки (то есть рендеринга) изображения

Сама задача рендеринга представляет из себя огромное количество однотипных численных операций над пикселями, вершинами и матрицами. Очень быстро стало понятно, что под такие задачи выгодно иметь устройство, способное выполнять много одинаковых операций параллельно

Центральный процессор (Central Processing Unit, CPU) устроен как универсальный исполнитель команд. Он хорошо справляется с разнородными задачами, сложной логикой, ветвлениями, обработкой исключений. Внутри процессора есть сложная архитектура управления: конвейеры, кэш-память нескольких уровней, механизмы предсказания переходов и так далее. Всё это нужно, чтобы ускорить выполнение последовательного кода с большим количеством условных операторов

Далее появился стандарт IEEE 754, определяющий числа с плавающей точкой для представления дробных чисел в памяти компьютера. Стандарт определил числа с плавающей точкой из 16 бит, 32 бит и 64 бит

Операции с плавающей точкой выполняются в специальных блоках процессора - FPU (Floating Point Unit). Для целочисленных операций используются ALU (Arithmetic Logic Unit). В современных CPU таких блоков немного, зато они очень сложные и универсальные. Однако центральный процессор выполняет сравнительно небольшое число операций над числами с плавающей точкой за такт

У графических процессоров внутри видеокарт ядер больше (порядка сотен или тысяч), они не имеют сложного потока управления, однако операции над числами с плавающей точкой сильно оптимизированы

Именно поэтому, когда говорят про вычислительную производительность, вводят понятие флопс (FLoating point Operations Per Second, FLOPS) - число выполненных операций с числами с плавающей точкой за одну секунда. Если речь идет о миллиардах таких операций в секунду, используется единица гигафлопс, например, графический процессор игровой консоли Playstation 5 может выполнять 10.29 терафлопс с 32-битным числами с плавающей точкой

Среди базовых операций, которые часто встречаются в численных алгоритмах, особенно важны операции вида умножение плюс сложение. Раньше это были две отдельные инструкции, часто обозначаемые как MAD (Multiple ADd). В современных архитектурах широко используется FMA (Fused Multiply-Add) - это операция, которая вычисляет a * b + c для чисел с плавающей точкой за одну инструкцию и с одной операцией округления. Это одновременно быстрее и точнее с точки зрения численной стабильности

Современные центральные процессоры придерживаются принципа SIMD (Simple Instruction, Multiple Data), из-за чего одна инструкция выполняется на нескольких данных. Для процессоров на архитектуре x86 это представлено расширениями архитектуры SSE (Streaming SIMD Extensions), SSE2, SSE3, SSSE3, SSE4.x, AVX, AVX2 и другими

Также параллелизм вычислений можно реализовывать средствами языков программирования, например с помощью встроенных функций (intrinsic function) - специальных функций или инструкций, позволяющие одной командой обработать сразу несколько чисел. Однако даже с SIMD ширина вектора ограничена (например, 256 или 512 бит), и это всё ещё сравнительно небольшой параллелизм

Видеокарта устроена принципиально иначе. Она жертвует сложной логикой управления ради максимальной пропускной способности вычислений. В графическом процессоре очень много простых вычислительных ядер, каждое из которых умеет выполнять базовые арифметические операции. За счёт этого современные видеокарты действительно могут выполнять в десятки раз больше операций с плавающей точкой, чем центральный процессор, если задача хорошо поддается параллелизации. В последнее время графический процессор способен выполнять около в 50 раз больше операций над числами с плавающей точкой, чем центральный процессор

Вместо сложного управления потоком исполнения графический процессор использует модель массового параллелизма. Это приводит к архитектурной модели SIMT (Single Instruction, Multiple Threads). Идея похожа на SIMD, но формулируется иначе: запускается большое количество потоков, которые исполняют одну и ту же программу, но над разными данными. Аппаратно эти потоки группируются (например, в так называемые warps или wavefronts), и, если все они выполняют одинаковые инструкции, вычисления происходят максимально эффективно

Здесь становится понятно, почему ветвления — проблема для видеокарт. Если внутри одной группы потоков условие выполняется по-разному, потоки вынуждены исполнять разные ветки по очереди, что резко снижает производительность

Также графический процессоры имеет очень высокую пропускную способность памяти, но при этом доступ к ней дорог по задержкам. Поэтому применяют подход Coalesced Memory Access Pattern — согласованный шаблон доступа к памяти: если соседние потоки обращаются к соседним адресам памяти, аппарат может объединить эти обращения в одну широкую транзакцию. Если же каждый поток читает случайное место, производительность резко падает

Для того чтобы программист мог использовать вычислительные возможности видеокарт не только для графики, появились специальные программные модели. Одной из таких моделей является OpenCL - открытый стандарт для вычислений, который позволяет писать программы, исполняемые на разных устройствах. Он описывает модель памяти, модель исполнения потоков и язык для написания вычислительных ядер

OpenCL опирается на особенности архитектуры GPU: массовый параллелизм, SIMT-модель, высокую вычислительную плотность и чувствительность к шаблонам доступа к памяти