Введение

Использование графического процессора для вычислений, обычно выполняемых центральным процессором, заключается в применении графического процессора (GPU), который традиционно предназначен для обработки вычислений, связанных с компьютерной графикой, для выполнения вычислений в приложениях, которые ранее обрабатывались центральным процессором (CPU). Использование нескольких видеокарт в одном компьютере или большого количества графических чипов еще больше усиливает изначально параллельный характер графической обработки. По сути, конвейер GPGPU – это вид параллельной обработки между одним или несколькими GPU и CPU, который анализирует данные, представляя их в виде изображения или другой графической формы. Несмотря на то, что GPU работают на более низких частотах, они обычно имеют значительно большее количество ядер. Таким образом, GPU способны обрабатывать гораздо больше изображений и графических данных в секунду, чем традиционный CPU. Преобразование данных в графический формат и последующее использование GPU для их сканирования и анализа может значительно повысить скорость вычислений. Конвейеры GPGPU были разработаны в начале XXI века для задач графической обработки (например, для улучшения шейдеров). Впоследствии было обнаружено, что они хорошо подходят для научных вычислений и с тех пор развиваются в этом направлении.

История

В принципе, любая произвольная булева функция, включая сложение, умножение и другие математические функции, может быть построена из функционально полного набора логических операторов. В 1987 году игра «Жизнь» Конвея стала одним из первых примеров универсальных вычислений с использованием раннего потокового процессора, называемого блиттером, для вызова специальной последовательности логических операций над битовыми векторами. Универсальные вычисления на графических процессорах стали более практичными и популярными примерно после 2001 года с появлением программируемых шейдеров и поддержки чисел с плавающей запятой на графических процессорах. Примечательно, что задачи, связанные с матрицами и/или векторами, особенно двух-, трех- или четырехмерными векторами, было легко перевести для графического процессора, который обеспечивает их нативную скорость и поддержку. Значительной вехой для GPGPU стал 2003 год, когда две исследовательские группы независимо друг от друга открыли подходы на основе GPU для решения общих задач линейной алгебры на GPU, которые выполнялись быстрее, чем на CPU. Эти ранние попытки использовать GPU в качестве универсальных процессоров требовали переформулировки вычислительных задач в терминах графических примитивов, поддерживаемых двумя основными API для графических процессоров: OpenGL и DirectX. Этот сложный перевод был упразднен с появлением языков программирования и API общего назначения, таких как Sh/RapidMind, Brook и Accelerator. За ними последовала CUDA от Nvidia, которая позволила программистам игнорировать базовые графические концепции в пользу более распространенных концепций высокопроизводительных вычислений. Более новые предложения, независимые от производителей оборудования, включают DirectCompute от Microsoft и OpenCL от Apple/Khronos Group. Nvidia выпустила CUDA в 2006 году — комплект разработки программного обеспечения (SDK) и интерфейс прикладного программирования (API), позволяющий использовать язык программирования C для кодирования алгоритмов для выполнения на GPU серии GeForce 8 и более поздних версий. ROCm, выпущенный в 2016 году, является ответом AMD на CUDA с открытым исходным кодом. По состоянию на 2022 год он сопоставим с CUDA по функциональности, но все еще уступает в поддержке потребителей. OpenVIDIA была разработана в Университете Торонто в период с 2003 по 2005 год в сотрудничестве с Nvidia. Altimesh Hybridizer, созданный Altimesh, компилирует Common Intermediate Language в бинарные файлы CUDA. Он поддерживает обобщения и виртуальные функции. Отладка и профилирование интегрированы с Visual Studio и Nsight. Он доступен в качестве расширения Visual Studio на Visual Studio Marketplace. Microsoft представила API для GPU-вычислений DirectCompute, выпущенный вместе с DirectX 11 API. Alea GPU, созданный QuantAlea, представляет возможности GPU-вычислений для языков Microsoft .NET F# и C#. Alea GPU также предоставляет упрощенную модель программирования GPU, основанную на GPU-параллельном выполнении и параллельной агрегации с использованием делегатов и автоматического управления памятью. MATLAB поддерживает ускорение GPGPU с помощью Parallel Computing Toolbox и MATLAB Distributed Computing Server, а также сторонних пакетов, таких как Jacket. GPGPU-обработка также используется для моделирования ньютоновской физики физическими движками. Apple представила проприетарный API Metal для приложений iOS, способный выполнять произвольный код через вычислительные шейдеры GPU Apple.

Поддержка оборудования

Видеокарты для компьютеров производятся разными производителями, такими как Nvidia и AMD. Видеокарты этих производителей различаются по поддержке форматов данных, включая целочисленные и форматы с плавающей точкой (32-битные и 64-битные). Microsoft разработала стандарт Shader Model, чтобы упростить классификацию возможностей графических карт по номеру версии Shader Model (1.0, 2.0, 3.0 и т.д.).

Целые числа

Видеокарты, предшествовавшие DirectX 9, поддерживали только палитровые или целочисленные цветовые типы. Существуют различные форматы, каждый из которых содержит красный, зеленый и синий компоненты. Иногда добавляется альфа-канал для использования в целях прозрачности. Распространенные форматы:

8 бит на пиксель – Иногда используется палитровая схема, где каждое значение является индексом в таблице, содержащей фактическое цветовое значение, заданное в одном из других форматов. В других случаях выделяется три бита на красный, три бита на зеленый и два бита на синий. 16 бит на пиксель – Обычно биты распределяются следующим образом: пять бит на красный, шесть бит на зеленый и пять бит на синий. 24 бита на пиксель – По восемь бит выделяется на каждый из красного, зеленого и синего. 32 бита на пиксель – По восемь бит выделяется на каждый из красного, зеленого, синего и альфа-канала.

Числа с плавающей запятой

Для ранних графических процессоров с фиксированной функциональностью или ограниченной программируемостью (то есть до и включая графические процессоры, совместимые с DirectX 8.1) этого было достаточно, поскольку такое же представление используется и в дисплеях. Данное представление имеет определенные ограничения. При достаточной графической вычислительной мощности даже графические программисты предпочли бы использовать более эффективные форматы, такие как форматы данных с плавающей точкой, для достижения эффектов, например, высокодинамического диапазона (HDR). Многие приложения GPGPU требуют точности вычислений с плавающей точкой, которая появилась с видеокартами, соответствующими спецификации DirectX 9. DirectX 9 Shader Model 2.x предлагал поддержку двух типов точности: полной и частичной. Полная точность могла быть FP32 или FP24 (32 или 24 бита на компонент с плавающей точкой) или выше, а частичная точность – FP16. Серия графических процессоров ATI Radeon R300 поддерживала точность FP24 только в программируемом фрагментном конвейере (хотя FP32 поддерживалась в вершинных процессорах), в то время как серия Nvidia NV30 поддерживала как FP16, так и FP32; другие производители, такие как S3 Graphics и XGI, поддерживали различные форматы до FP24. Реализации вычислений с плавающей точкой на графических процессорах Nvidia в основном соответствуют стандарту IEEE, однако это не относится ко всем производителям. Это имеет последствия для корректности, что важно для некоторых научных приложений. Хотя 64-битные значения с плавающей точкой (double precision float) обычно доступны на центральных процессорах, они не всегда поддерживаются на графических процессорах. Некоторые архитектуры графических процессоров жертвуют соответствием стандарту IEEE, а другие лишены поддержки двойной точности. Предпринимались попытки эмулировать вычисления с плавающей точкой двойной точности на графических процессорах, однако потеря производительности нивелирует любую выгоду от переноса вычислений на графический процессор.

Векторизация

Большинство операций на графическом процессоре выполняются в векторном режиме: одна операция может быть применена одновременно к до четырех значений. Например, если один цвет необходимо модулировать другим цветом, графический процессор может вычислить результирующий цвет за одну операцию. Эта функциональность особенно полезна в графике, поскольку почти каждый базовый тип данных является вектором (двумерным, трехмерным или четырехмерным). Примеры включают вершины, цвета, нормали и текстурные координаты. Многие другие приложения также могут эффективно использовать эту возможность, и благодаря более высокой производительности векторные инструкции, известные как Single Instruction, Multiple Data (SIMD), уже давно доступны и на центральных процессорах.

GPU против CPU

Первоначально данные просто передавались в одном направлении от центрального процессора (CPU) к графическому процессору (GPU), а затем к устройству отображения. Однако со временем стало ценным, чтобы графические процессоры сначала хранили простые, а затем сложные структуры данных для последующей передачи обратно в CPU, который анализировал изображение или набор научных данных, представленных в 2D или 3D формате, понятном для видеокарты. Поскольку GPU имеет доступ к каждой операции отрисовки, он может быстро анализировать данные в этих форматах, в то время как CPU должен опрашивать каждый пиксель или элемент данных значительно медленнее, так как скорость доступа между CPU и его большим объемом оперативной памяти (или, в худшем случае, жестким диском) ниже, чем у GPU и видеокарт, которые обычно содержат меньший объем более дорогой памяти, к которой доступ осуществляется гораздо быстрее. Передача части набора данных, предназначенного для активного анализа, в память GPU в виде текстур или других легко читаемых GPU форматов приводит к увеличению скорости. Отличительной особенностью архитектуры GPGPU является возможность двунаправленной передачи информации от GPU к CPU; как правило, пропускная способность данных в обоих направлениях максимально высока, что обеспечивает мультипликативный эффект на скорость выполнения конкретного ресурсоемкого алгоритма. Конвейеры GPGPU могут повысить эффективность при работе с особенно большими наборами данных и/или данными, содержащими 2D или 3D изображения. Они используются как в сложных графических конвейерах, так и в научных вычислениях; особенно в областях, работающих с большими объемами данных, таких как картирование генома, или где полезен двух- или трехмерный анализ, в частности, в современном анализе биомолекул, изучении белков и других сложных органических химических процессов. Такие конвейеры также могут значительно повысить эффективность обработки изображений и компьютерного зрения, а также параллельной обработки в целом. Некоторые высокооптимизированные конвейеры обеспечили увеличение скорости в несколько сотен раз по сравнению с исходным конвейером на основе CPU при выполнении одной ресурсоемкой задачи. Простым примером может служить GPU-программа, которая собирает данные о средних значениях освещенности при рендеринге изображения с камеры или компьютерной графической программы и отправляет их обратно в основную программу на CPU, чтобы CPU мог внести коррективы в общее изображение на экране. Более сложный пример может использовать обнаружение границ для возврата как числовой информации, так и обработанного изображения, представляющего контуры, в программу компьютерного зрения, управляющую, например, мобильным роботом. Поскольку GPU имеет быстрый и локальный аппаратный доступ к каждому пикселю или другому элементу изображения, он может анализировать и усреднять его (в первом примере) или применять фильтр Собеля или другой сверточный фильтр (во втором) значительно быстрее, чем CPU, которому обычно приходится обращаться к более медленным копиям графического изображения в оперативной памяти. GPGPU – это, по сути, программная концепция, а не аппаратная; это тип алгоритма, а не элемент оборудования. Однако специализированные аппаратные решения могут еще больше повысить эффективность конвейеров GPGPU, которые традиционно выполняют относительно небольшое количество алгоритмов на очень больших объемах данных. Масштабно параллелизуемые, гигантские задачи на уровне данных могут быть еще больше параллелизованы с помощью специализированных конфигураций, таких как вычислительные кластеры (множество схожих, высоконастроенных машин, объединенных в стойку), которые добавляют третий уровень, состоящий из множества вычислительных узлов, каждый из которых использует множество CPU для соответствия множеству GPU. Некоторые майнеры Bitcoin использовали такие конфигурации для высокопроизводительных вычислений.

Кэши

Исторически процессоры использовали кэши с аппаратным управлением, а ранние графические процессоры предоставляли только локальную память с программным управлением. Однако, по мере того как графические процессоры все чаще используются для задач общего назначения, современные графические процессоры проектируются с многоуровневыми кэшами с аппаратным управлением, что помогло им выйти на передовые позиции в области универсальных вычислений. Например, графические процессоры серии GeForce 200 с архитектурой GT200 не имели кэша L2, графический процессор Fermi имеет кэш последнего уровня 768 КиБ, графический процессор Kepler имеет кэш последнего уровня 1,5 МиБ, графический процессор Maxwell имеет кэш последнего уровня 2 МиБ, а графический процессор Pascal имеет кэш последнего уровня 4 МиБ.

Регистрационный файл

У графических процессоров очень большие файлы регистров, что позволяет им снизить задержку переключения контекста. Размер файла регистров также увеличивается с каждым новым поколением графических процессоров, например, общий размер файла регистров на GPU Maxwell (GM200), Pascal и Volta составляет 6 МБ, 14 МБ и 20 МБ соответственно. Для сравнения, размер файла регистров на центральных процессорах невелик, обычно измеряется десятками или сотнями килобайт.

Энергоэффективность

Высокая производительность графических процессоров достигается за счет высокого энергопотребления, которое при полной нагрузке фактически сопоставимо с потреблением всей остальной системы ПК. Максимальное энергопотребление GPU серии Pascal (Tesla P100) составляло 250 Вт.

Обработка потоков

Графические процессоры (ГПУ) разработаны специально для графики и, следовательно, сильно ограничивают операции и программирование. В силу своей архитектуры, ГПУ эффективны только для задач, которые можно решить с помощью потоковой обработки, а аппаратное обеспечение может использоваться лишь определенным образом. Последующее обсуждение, касающееся вершин, фрагментов и текстур, в основном относится к устаревшей модели программирования GPGPU, где графические API (OpenGL или DirectX) использовались для выполнения вычислений общего назначения. С появлением CUDA (Nvidia, 2007) и OpenCL (независимый от производителя, 2008) API для вычислений общего назначения, в новых кодах GPGPU больше не требуется сопоставлять вычисления с графическими примитивами. Потоковая природа ГПУ остается актуальной независимо от используемых API. (См. например,)

ГПУ могут обрабатывать только независимые вершины и фрагменты, но способны обрабатывать их параллельно в большом количестве. Это особенно эффективно, когда программисту необходимо обработать множество вершин или фрагментов одинаковым образом. В этом смысле ГПУ являются потоковыми процессорами, которые могут работать параллельно, выполняя одно ядро на множестве записей в потоке одновременно. Поток – это просто набор записей, требующих схожих вычислений. Потоки обеспечивают параллелизм данных. Ядра – это функции, применяемые к каждому элементу потока. В ГПУ вершины и фрагменты являются элементами потоков, а вершинные и фрагментные шейдеры – ядрами, которые выполняются над ними. Для каждого элемента можно только читать входные данные, выполнять над ними операции и записывать результат в выходные данные. Допускается наличие нескольких входов и выходов, но не допускается использование участка памяти одновременно для чтения и записи. Арифметическая интенсивность определяется как количество операций, выполняемых на единицу переданной памяти. Для приложений GPGPU важно иметь высокую арифметическую интенсивность, иначе задержка доступа к памяти ограничит прирост вычислительной скорости. Идеальные приложения GPGPU характеризуются большими объемами данных, высоким уровнем параллелизма и минимальной зависимостью между элементами данных.

Текстуры в виде потока

Наиболее распространенной формой потока данных в GPGPU является двумерная сетка, поскольку она естественно согласуется с моделью рендеринга, встроенной в графические процессоры. Многие вычисления естественно представляются в виде сеток: матричная алгебра, обработка изображений, физически обоснованное моделирование и тому подобное. Поскольку текстуры используются как память, доступ к текстурам используется как чтение из памяти. Благодаря этому определенные операции могут выполняться автоматически графическим процессором.

Контроль потока

В последовательном коде можно управлять потоком программы с помощью операторов if-then-else и различных видов циклов. Такие структуры управления потоком были добавлены в графические процессоры (GPU) относительно недавно. Условную запись можно было реализовать, используя тщательно подобранную последовательность арифметических и битовых операций, но циклы и условные переходы были невозможны. Современные GPU поддерживают условные переходы, но обычно с потерей производительности. Условные переходы следует избегать во внутренних циклах, как в CPU, так и в GPU коде, и для их реализации при отсутствии аппаратной поддержки можно использовать различные методы, такие как статическое разрешение переходов, предварительные вычисления, предикация, разделение циклов и Z-отсечение.

Карта

Операция map (преобразования) просто применяет заданную функцию (ядро) к каждому элементу потока. Простой пример – умножение каждого значения в потоке на константу (например, для увеличения яркости изображения). Операция map легко реализуется на GPU. Программист генерирует фрагмент для каждого пикселя на экране и применяет к нему фрагментный шейдер. Результирующий поток того же размера сохраняется в выходном буфере.

Уменьшить

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

Фильтрация потока

Фильтрация потока по сути представляет собой неравномерное уменьшение. Фильтрация подразумевает исключение элементов из потока на основании определенных критериев.

Сканирование

Операция сканирования, также называемая параллельной суммой префиксов, принимает на вход вектор (поток) элементов данных и (произвольную) ассоциативную бинарную функцию "+" с элементом нейтрали 'i'. Если входные данные – [a0, a1, a2, a3, ...], то исключительное сканирование выдает [i, a0, a0 + a1, a0 + a1 + a2, ...], а включительное сканирование выдает [a0, a0 + a1, a0 + a1 + a2, a0 + a1 + a2 + a3, ...] и не требует наличия элемента нейтрали. Несмотря на то, что операция может показаться изначально последовательной, существуют эффективные параллельные алгоритмы сканирования, которые были реализованы на графических процессорах. Операция сканирования применяется, например, в алгоритме быстрой сортировки и при умножении разреженных матриц на векторы.

Рассеивание

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

Соберите

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

Сортировка

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

Поиск

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

Молекулярная динамика

Описание приложения Поддерживаемые функции Ожидаемое ускорение† GPU‡ Поддержка нескольких GPU Статус выпуска AbaloneModels молекулярная динамика биополимеров для моделирования белков, ДНК и лигандовЯвный и неявный растворитель, гибридный Монте-Карло4–120xT 2075, 2090, K10, K20, K20XДоступно сейчас, версия 1.8.88 ACEMDGPU моделирование силовых полей молекулярной механики, явный и неявный растворительРазработано для использования на GPU160 нс/день GPU версия толькоT 2075, 2090, K10, K20, K20X✔Доступно сейчас AMBERНабор программ для моделирования молекулярной динамики биомолекулPMEMD: явный и неявный растворитель89.44 нс/день JAC NVE T 2075, 2090, K10, K20, K20X✔Доступно сейчас, версия 12 + исправления ошибок DL POLYМоделирование макромолекул, полимеров, ионных систем и т.д. на распределенной памяти параллельного компьютераДвухчастичные силы, связывание пар ячеек, силы Ewald SPME, Shake VV4xT 2075, 2090, K10, K20, K20X✔Доступно сейчас, версия 4.0 только исходный код CHARMMMD пакет для моделирования молекулярной динамики биомолекул. Неявный (5x), явный (2x) растворитель через OpenMMTBDT 2075, 2090, K10, K20, K20X ✔В разработке Q4/12 GROMACSSимирование биохимических молекул со сложными взаимодействиями связейНеявный (5x), явный (2x) растворитель165 нс/день DHFR T 2075, 2090, K10, K20, K20X Доступно сейчас, версия 4.6 в Q4/12 HOOMD BlueПакет динамики частиц, разработанный с нуля для GPUРазработано для GPU2x T 2075, 2090, K10, K20, K20X✔Доступно сейчас LAMMPSКлассический пакет молекулярной динамикиЛеннарда-Джонса, Морса, Баккингема, CHARMM, табличные, грубые SDK, анизотропные Gay Bern, RE squared, "гибридные" комбинации3–18xT 2075, 2090, K10, K20, K20X✔Доступно сейчас NAMDПредназначен для высокопроизводительного моделирования больших молекулярных системСпособен моделировать системы до 100 миллионов атомов6.44 нс/день STMV 585x 2050sT 2075, 2090, K10, K20, K20X✔Доступно сейчас, версия 2.9 OpenMMLibrary и приложение для молекулярной динамики для HPC с GPUЯвный и неявный растворитель, пользовательские силыНеявный: 127–213 нс/день; Явный: 18–55 нс/день DHFRT 2075, 2090, K10, K20, K20X✔Доступно сейчас, версия 4.1.1

† Ожидаемое ускорение сильно зависит от конфигурации системы. Производительность GPU сравнивается с многоядерным сокетом x86 CPU. Производительность GPU оценивается на поддерживаемых GPU функциях и может быть сравнением производительности ядра с ядром. Подробности об используемой конфигурации смотрите на веб-сайте приложения. Ускорение согласно внутренним тестам Nvidia или документации ISV. ‡ Q=Quadro GPU, T=Tesla GPU. Nvidia рекомендуемые GPU для этого приложения. Обратитесь к разработчику или ISV для получения информации о сертификации.