Универсальные вычисления на графических процессорах ( GPGPU или реже GPGP ) — это использование графического процессора (GPU), который обычно обрабатывает вычисления только для компьютерной графики , для выполнения вычислений в приложениях, традиционно обрабатываемых центральным процессором (CPU). [1] [2] [3] [4] Использование нескольких видеокарт в одном компьютере или большого количества графических чипов ещё больше распараллеливает и без того параллельную природу обработки графики. [5]
По сути, конвейер GPGPU — это своего рода параллельная обработка между одним или несколькими графическими процессорами и центральными процессорами, которая анализирует данные так, как если бы они были в виде изображения или другой графической формы. Хотя графические процессоры работают на более низких частотах, они обычно имеют во много раз больше ядер . Таким образом, графические процессоры могут обрабатывать гораздо больше изображений и графических данных в секунду, чем традиционный центральный процессор. Перенос данных в графическую форму и последующее использование графического процессора для их сканирования и анализа может создать большое ускорение .
Конвейеры GPGPU были разработаны в начале 21-го века для обработки графики (например, для лучших шейдеров ). Было обнаружено, что эти конвейеры хорошо подходят для научных вычислительных нужд, и с тех пор развиваются в этом направлении.
Наиболее известными GPGPU являются Nvidia Tesla , которые используются в Nvidia DGX , а также AMD Instinct и Intel Gaudi.
В принципе, любая произвольная булева функция , включая сложение, умножение и другие математические функции, может быть построена из функционально полного набора логических операторов. В 1987 году игра «Жизнь» Конвея стала одним из первых примеров вычислений общего назначения, использующих ранний потоковый процессор, называемый блиттером, для вызова специальной последовательности логических операций над битовыми векторами. [6]
Универсальные вычисления на графических процессорах стали более практичными и популярными примерно после 2001 года с появлением как программируемых шейдеров , так и поддержки операций с плавающей точкой на графических процессорах. В частности, проблемы, связанные с матрицами и/или векторами — особенно двух-, трех- или четырехмерными векторами — было легко перевести на графический процессор, который действует с собственной скоростью и поддержкой этих типов. Значительной вехой для GPGPU стал 2003 год, когда две исследовательские группы независимо друг от друга открыли основанные на графических процессорах подходы для решения общих задач линейной алгебры на графических процессорах, которые работали быстрее, чем на центральных процессорах. [7] [8] Эти ранние попытки использовать графические процессоры в качестве универсальных процессоров потребовали переформулирования вычислительных задач в терминах графических примитивов, поддерживаемых двумя основными API для графических процессоров, OpenGL и DirectX . Этот громоздкий перевод был устранен с появлением универсальных языков программирования и API, таких как Sh / RapidMind , Brook и Accelerator. [9] [10] [11]
За ними последовала CUDA от Nvidia , которая позволила программистам игнорировать базовые графические концепции в пользу более общих концепций высокопроизводительных вычислений . [12] Более новые, независимые от поставщика оборудования предложения включают DirectCompute от Microsoft и OpenCL от Apple/Khronos Group . [12] Это означает, что современные конвейеры GPGPU могут использовать скорость графического процессора, не требуя полного и явного преобразования данных в графическую форму.
Марк Харрис, основатель GPGPU.org, ввел термин GPGPU .
Любой язык, который позволяет коду, работающему на CPU, опрашивать шейдер GPU для возвращаемых значений, может создать фреймворк GPGPU. Стандарты программирования для параллельных вычислений включают OpenCL (независимый от поставщика), OpenACC , OpenMP и OpenHMPP .
По состоянию на 2016 год [обновлять]OpenCL является доминирующим открытым языком вычислений общего назначения на GPU и открытым стандартом, определенным Khronos Group . [ требуется ссылка ] OpenCL предоставляет кроссплатформенную платформу GPGPU, которая дополнительно поддерживает параллельные вычисления данных на CPU. OpenCL активно поддерживается на платформах Intel, AMD, Nvidia и ARM. Khronos Group также стандартизировала и реализовала SYCL , высокоуровневую модель программирования для OpenCL как однородный встраиваемый язык, специфичный для домена, на основе чистого C++11.
Доминирующей фирменной средой является Nvidia CUDA . [13] В 2006 году Nvidia запустила CUDA — комплект для разработки программного обеспечения (SDK) и интерфейс прикладного программирования (API), позволяющий использовать язык программирования C для кодирования алгоритмов для выполнения на графических процессорах серии GeForce 8 и более поздних моделях.
ROCm , запущенный в 2016 году, является ответом AMD на CUDA с открытым исходным кодом. По состоянию на 2022 год он находится на одном уровне с CUDA в отношении функций, но все еще не имеет поддержки потребителей.
OpenVIDIA была разработана в Университете Торонто в 2003–2005 годах [14] в сотрудничестве с Nvidia.
Altimesh Hybridizer, созданный Altimesh, компилирует Common Intermediate Language в двоичные файлы CUDA. [15] [16] Он поддерживает универсальные и виртуальные функции. [17] Отладка и профилирование интегрированы с Visual Studio и Nsight. [18] Он доступен как расширение Visual Studio на Visual Studio Marketplace.
Microsoft представила API вычислений на графических процессорах DirectCompute , выпущенный вместе с API DirectX 11 .
Alea GPU ,[19]созданный QuantAlea,[20]представляет собственные вычислительные возможности GPU для языков Microsoft .NETF#[21]иC#. Alea GPU также предоставляет упрощенную модель программирования GPU на основе GPU parallel-for и параллельного агрегата с использованием делегатов и автоматического управления памятью.[22]
MATLAB поддерживает ускорение GPGPU с использованием Parallel Computing Toolbox и MATLAB Distributed Computing Server [23] , а также сторонних пакетов, таких как Jacket .
Обработка GPGPU также используется для моделирования ньютоновской физики физическими движками [ 24] , а коммерческие реализации включают Havok Physics, FX и PhysX , оба из которых обычно используются для компьютерных и видеоигр .
C++ Accelerated Massive Parallelism ( C++ AMP ) — это библиотека, которая ускоряет выполнение кода C++ за счет использования аппаратных средств параллельной обработки данных на графических процессорах.
Благодаря тенденции к увеличению мощности мобильных графических процессоров, универсальное программирование стало доступно также на мобильных устройствах под управлением основных мобильных операционных систем .
Google Android 4.2 позволила запускать код RenderScript на графическом процессоре мобильного устройства. [25] С тех пор RenderScript был устарел в пользу первых вычислительных шейдеров OpenGL [26] и более поздних вычислений Vulkan. [27] OpenCL доступен на многих устройствах Android, но официально не поддерживается Android [ нужна ссылка ] . Apple представила собственный API Metal для приложений iOS , способный выполнять произвольный код с помощью вычислительных шейдеров GPU Apple [ нужна ссылка ] .
Видеокарты для компьютеров выпускаются различными производителями, такими как Nvidia , AMD . Карты от таких производителей отличаются реализацией поддержки форматов данных, таких как целочисленные и с плавающей точкой (32-битные и 64-битные). Microsoft представила стандарт Shader Model , чтобы помочь ранжировать различные функции графических карт в простой номер версии Shader Model (1.0, 2.0, 3.0 и т. д.).
Видеокарты до DirectX 9 поддерживали только палитровые или целочисленные типы цветов. Иногда добавляется еще одно альфа-значение, используемое для прозрачности. Распространенные форматы:
Для ранних графических процессоров с фиксированной функцией или ограниченной программируемостью (т. е. вплоть до совместимых с DirectX 8.1 графических процессоров) этого было достаточно, поскольку это также представление, используемое в дисплеях. Это представление имеет определенные ограничения. При наличии достаточной вычислительной мощности даже программисты графики хотели бы использовать лучшие форматы, такие как форматы данных с плавающей точкой , для получения таких эффектов, как визуализация с высоким динамическим диапазоном . Многие приложения 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 ; однако это не относится ко всем поставщикам. [28] Это имеет последствия для корректности, которые считаются важными для некоторых научных приложений. Хотя 64-битные значения с плавающей точкой (двойная точность с плавающей точкой) обычно доступны на центральных процессорах, они не поддерживаются повсеместно на графических процессорах. Некоторые архитектуры графических процессоров жертвуют соответствием IEEE, в то время как другие не поддерживают двойную точность. Были предприняты попытки эмулировать значения с плавающей точкой двойной точности на графических процессорах; однако компромисс скорости сводит на нет любые преимущества от выгрузки вычислений на графический процессор в первую очередь. [29]
Большинство операций на GPU работают векторизованным образом: одна операция может быть выполнена над четырьмя значениями одновременно. Например, если один цвет ⟨R1, G1, B1⟩ должен быть модулирован другим цветом ⟨R2, G2, B2⟩ , GPU может создать результирующий цвет ⟨R1*R2, G1*G2, B1*B2⟩ за одну операцию. Эта функциональность полезна в графике, поскольку почти каждый базовый тип данных является вектором (либо 2-, 3-, либо 4-мерным). [ требуется цитата ] Примеры включают вершины, цвета, векторы нормалей и координаты текстур. Многие другие приложения могут использовать это с пользой, и из-за их более высокой производительности векторные инструкции, называемые одиночными инструкциями, множественными данными ( SIMD ), уже давно доступны на CPU. [ требуется цитата ]
Первоначально данные просто передавались в одну сторону от центрального процессора (ЦП) к графическому процессору (ГП), а затем к устройству отображения . Однако со временем для ГП стало ценным хранить сначала простые, а затем сложные структуры данных, которые должны были передаваться обратно в ЦП, который анализировал изображение, или набор научных данных, представленных в формате 2D или 3D, который может понимать видеокарта. Поскольку ГП имеет доступ к каждой операции рисования, он может быстро анализировать данные в этих формах, тогда как ЦП должен опрашивать каждый пиксель или элемент данных гораздо медленнее, поскольку скорость доступа между ЦП и его большим пулом памяти с произвольным доступом (или, в еще худшем случае, жестким диском ) ниже, чем у ГП и видеокарт, которые обычно содержат меньшие объемы более дорогой памяти, доступ к которой осуществляется намного быстрее. Передача части набора данных для активного анализа в эту память ГП в виде текстур или других легко читаемых форм ГП приводит к увеличению скорости. Отличительной особенностью конструкции GPGPU является возможность двунаправленной передачи информации обратно от графического процессора к центральному процессору; как правило, пропускная способность данных в обоих направлениях в идеале высока, что приводит к мультипликативному эффекту на скорость конкретного часто используемого алгоритма .
Конвейеры GPGPU могут повысить эффективность на особенно больших наборах данных и/или данных, содержащих 2D или 3D изображения. Он используется в сложных графических конвейерах, а также в научных вычислениях ; больше в областях с большими наборами данных, таких как картирование генома , или там, где полезен двух- или трехмерный анализ — особенно в современном анализе биомолекул , изучении белков и другой сложной органической химии . Примером таких приложений является программный пакет NVIDIA для анализа генома .
Такие конвейеры также могут значительно повысить эффективность обработки изображений и компьютерного зрения , среди прочих областей; а также параллельной обработки в целом. Некоторые очень сильно оптимизированные конвейеры дали увеличение скорости в несколько сотен раз по сравнению с исходным конвейером на базе ЦП на одной высоконагруженной задаче.
Простым примером может служить программа GPU, которая собирает данные о средних значениях освещения , поскольку она отображает некоторый вид с камеры или программы компьютерной графики обратно в основную программу на CPU, так что CPU затем может вносить коррективы в общий вид экрана. Более продвинутый пример может использовать обнаружение краев для возврата как числовой информации, так и обработанного изображения, представляющего контуры, в программу компьютерного зрения, управляющую, скажем, мобильным роботом. Поскольку GPU имеет быстрый и локальный аппаратный доступ к каждому пикселю или другому элементу изображения на изображении, он может анализировать и усреднять его (для первого примера) или применять фильтр краев Собеля или другой сверточный фильтр (для второго) с гораздо большей скоростью, чем CPU, который обычно должен получать доступ к более медленным копиям памяти произвольного доступа рассматриваемой графики.
GPGPU по сути является программной концепцией, а не аппаратной концепцией; это тип алгоритма , а не часть оборудования. Специализированные конструкции оборудования могут, однако, еще больше повысить эффективность конвейеров GPGPU, которые традиционно выполняют относительно немного алгоритмов на очень больших объемах данных. Таким образом, масштабно распараллеленные задачи на уровне гигантских данных могут быть распараллелены еще больше с помощью специализированных установок, таких как стоечные вычисления (множество похожих, тщательно настроенных машин, встроенных в стойку ) , которые добавляют третий уровень — множество вычислительных блоков, каждый из которых использует множество ЦП для соответствия множеству ГП. Некоторые «майнеры» биткоинов использовали такие установки для обработки большого количества данных.
Исторически ЦП использовали аппаратно-управляемые кэши , но более ранние ГП предоставляли только программно-управляемую локальную память. Однако, поскольку ГП все чаще используются для приложений общего назначения, современные ГП разрабатываются с аппаратно-управляемыми многоуровневыми кэшами, которые помогли ГП перейти к основным вычислениям. Например, ГП архитектуры GT200 серии GeForce 200 не имели кэша L2, ГП Fermi имеет 768 КиБ кэша последнего уровня, ГП Kepler имеет 1,5 МБ кэша последнего уровня, [30] ГП Maxwell имеет 2 МБ кэша последнего уровня, а ГП Pascal имеет 4 МБ кэша последнего уровня.
Графические процессоры имеют очень большие файлы регистров , что позволяет им сократить задержку переключения контекста. Размер файла регистра также увеличивается с каждым поколением графических процессоров, например, общий размер файла регистра на графических процессорах Maxwell (GM200), Pascal и Volta составляет 6 МиБ, 14 МиБ и 20 МиБ соответственно. [31] [32] Для сравнения, размер файла регистра на центральных процессорах невелик, обычно десятки или сотни килобайт.
Высокая производительность графических процессоров достигается за счет высокого энергопотребления, которое при полной нагрузке фактически равно энергопотреблению всей остальной системы ПК вместе взятой. [33] Максимальное энергопотребление графического процессора серии Pascal (Tesla P100) было указано на уровне 250 Вт. [34]
Графические процессоры разработаны специально для графики и поэтому очень ограничены в операциях и программировании. Из-за своей конструкции графические процессоры эффективны только для задач, которые могут быть решены с помощью потоковой обработки , а оборудование может использоваться только определенным образом.
Последующее обсуждение, касающееся вершин, фрагментов и текстур, касается в основном устаревшей модели программирования GPGPU, где графические API ( OpenGL или DirectX ) использовались для выполнения вычислений общего назначения. С введением вычислительных API общего назначения CUDA (Nvidia, 2007) и OpenCL (независимый от поставщика, 2008) в новых кодах GPGPU больше нет необходимости отображать вычисления в графические примитивы. Потоковая природа обработки графических процессоров остается действительной независимо от используемых API. (См., например, [35] )
Графические процессоры могут обрабатывать только независимые вершины и фрагменты, но могут обрабатывать многие из них параллельно. Это особенно эффективно, когда программист хочет обработать много вершин или фрагментов одинаковым образом. В этом смысле графические процессоры являются потоковыми процессорами — процессорами, которые могут работать параллельно, запуская одно ядро на многих записях в потоке одновременно.
Поток — это просто набор записей, требующих схожих вычислений. Потоки обеспечивают параллелизм данных. Ядра — это функции, которые применяются к каждому элементу в потоке. В графических процессорах вершины и фрагменты — это элементы в потоках, а вершинные и фрагментные шейдеры — это ядра, которые должны быть запущены на них. [ сомнительно — обсудить ] Для каждого элемента мы можем только читать из ввода, выполнять операции с ним и записывать на вывод. Допустимо иметь несколько входов и несколько выходов, но никогда не иметь фрагмент памяти, который одновременно доступен для чтения и записи. [ неопределенно ]
Арифметическая интенсивность определяется как количество операций, выполненных на слово переданной памяти. Для приложений GPGPU важно иметь высокую арифметическую интенсивность, иначе задержка доступа к памяти ограничит вычислительное ускорение. [36]
Идеальные приложения GPGPU имеют большие наборы данных, высокий параллелизм и минимальную зависимость между элементами данных.
На графическом процессоре доступны различные вычислительные ресурсы:
Фактически, программа может заменить выходную текстуру только для записи вместо кадрового буфера. Это делается либо через Render to Texture (RTT), Render-To-Backbuffer-Copy-To-Texture (RTBCTT), либо через более поздний stream-out.
Наиболее распространенной формой потока для GPGPU является 2D-сетка, поскольку она естественным образом соответствует модели рендеринга, встроенной в GPU. Многие вычисления естественным образом отображаются в сетках: матричная алгебра, обработка изображений, физическая симуляция и т. д.
Поскольку текстуры используются как память, поиск текстур затем используется как чтение памяти. Благодаря этому некоторые операции могут выполняться автоматически графическим процессором.
Вычислительные ядра можно рассматривать как тело циклов . Например, программист, работающий с сеткой на ЦП, может иметь код, который выглядит следующим образом:
// Входные и выходные сетки имеют размер 10000 x 10000 или 100 миллионов элементов.void transform_10k_by_10k_grid ( float in [ 10000 ][ 10000 ], float out [ 10000 ][ 10000 ]) { for ( int x = 0 ; x < 10000 ; x ++ ) { for ( int y = 0 ; y < 10000 ; y ++ ) { // Следующая строка выполняется 100 миллионов раз out [ x ][ y ] = do_some_hard_work ( in [ x ][ y ]); } } }
На графическом процессоре программист указывает только тело цикла как ядро и какие данные следует обрабатывать в цикле, вызывая обработку геометрии.
В последовательном коде можно контролировать поток программы с помощью операторов if-then-else и различных форм циклов. Такие структуры управления потоком были добавлены в графические процессоры совсем недавно. [37] Условные записи могли выполняться с помощью правильно созданной серии арифметических/битовых операций, но циклы и условные ветки были невозможны.
Последние [ когда? ] графические процессоры позволяют ветвление, но обычно с потерей производительности. Ветвление следует избегать во внутренних циклах, будь то в коде CPU или GPU, и различные методы, такие как статическое разрешение ветвления, предварительное вычисление, предикация, разделение цикла [38] и Z-cull [39], могут использоваться для достижения ветвления, когда аппаратная поддержка отсутствует.
Операция 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, ...] и не требует наличия идентичности . Хотя на первый взгляд операция может показаться последовательной по своей сути, эффективные алгоритмы параллельного сканирования возможны и были реализованы на графических процессорах. Операция сканирования используется, например, в быстрой сортировке и умножении разреженной матрицы на вектор. [35] [40] [41] [42]
Операция рассеивания наиболее естественно определяется на вершинном процессоре. Вершинный процессор может регулировать положение вершины , что позволяет программисту контролировать, где на сетке размещается информация. Возможны и другие расширения, например, управление размером области, на которую влияет вершина.
Процессор фрагментов не может выполнить прямую операцию рассеивания, поскольку местоположение каждого фрагмента на сетке фиксируется во время создания фрагмента и не может быть изменено программистом. Однако логическая операция рассеивания иногда может быть переделана или реализована с другим шагом сбора. Реализация рассеивания сначала выдаст как выходное значение, так и выходной адрес. Сразу последующая операция сбора использует сравнение адресов, чтобы увидеть, отображается ли выходное значение на текущий выходной слот.
В специализированных вычислительных ядрах разброс может выполняться с помощью индексированных записей.
Gather — это обратный scatter. После того, как scatter переупорядочивает элементы в соответствии с картой, gather может восстановить порядок элементов в соответствии с используемым scatter карты. В выделенных вычислительных ядрах gather может выполняться индексированными чтениями. В других шейдерах он выполняется с помощью текстурных поисков.
Операция сортировки преобразует неупорядоченный набор элементов в упорядоченный набор элементов. Наиболее распространенной реализацией на графических процессорах является использование сортировки по радиксу для целочисленных и плавающих данных, а также крупнозернистой сортировки слиянием и мелкозернистых сетей сортировки для общих сопоставимых данных. [43] [44]
Операция поиска позволяет программисту найти заданный элемент в потоке или, возможно, найти соседей указанного элемента. В основном используемый метод поиска — это бинарный поиск по отсортированным элементам.
На графическом процессоре могут быть представлены различные структуры данных:
Ниже приведены некоторые области, в которых графические процессоры использовались для вычислений общего назначения:
Использование GPGPU в биоинформатике: [59] [83]
† Ожидаемое ускорение сильно зависит от конфигурации системы. Производительность графического процессора сравнивается с многоядерным сокетом x86 CPU. Производительность графического процессора измеряется на поддерживаемых графическим процессором функциях и может быть сравнением производительности ядра с ядром. Подробную информацию об используемой конфигурации см. на веб-сайте приложения. Ускорение согласно внутреннему тестированию Nvidia или документации ISV.
‡ Q= Quadro GPU , T= Tesla GPU . Nvidia рекомендовала GPU для этого приложения. Обратитесь к разработчику или ISV, чтобы получить информацию о сертификации.
что Lowry использует графические процессоры Nvidia Tesla (графические процессоры), запрограммированные в CUDA (Compute Unified Device Architecture) компании для реализации алгоритмов. Nvidia утверждает, что графические процессоры примерно на два порядка быстрее вычислений центрального процессора, что сокращает время обработки до менее одной минуты на кадр.
ускоряет моделирование целостности сигнала на рабочих станциях с графическими процессорами (GPU) на базе архитектуры Nvidia Compute Unified Device Architecture (CUDA)
В ходе внутреннего тестирования Tesla S1070 продемонстрировал 360-кратное увеличение скорости работы алгоритма определения сходства по сравнению с популярным центральным процессором Intel Core 2 Duo, работающим на тактовой частоте 2,6 ГГц.