15 лет назад 25 сентября 2009 в 17:10 774

Аббревиатура эта, наверное, уже не раз попадалась вам на глаза, а даже если и не попадалась, то уж большая часть ее вам знакома – уж про GPU-то вы точно знаете. Интересное тут заключается в двух дополнительных «GP», добавив которые мы получаем «General Purpose GPU» – «GPU широкого профиля». Получается неувязка: как может быть видеокарта чем-то еще? И какое это имеет отношение к тому, что можно получить стократный прирост производительности? И где вообще будет этот прирост? Вопросов много, но ответов, что удивительно, еще больше, так что не будем терять время и приступим к рассмотрению темы.

Из пункта А во все пункты сразу…
Лень – двигатель прогресса. Все великие открытия совершались, чтобы успеть сделать все необходимое и отдохнуть побольше. В большинстве случаев ускорить процесс можно как минимум одним способом: делать его быстрее. Метод эффективен на все сто процентов, но, увы, не всегда применим. Было бы просто замечательно, если бы можно было бесконечно ускорять работу процессоров, но на данный момент они все ощутимее признаются в своем бессилии, пасуя перед высокими частотами. Приходится искать альтернативы, и одна из таких альтернатив – возложение части труда на ближнего своего.

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

Folding@home – проект распределенных вычислений для проведения компьютерной симуляции свертывания молекул белка. С этой задачей GPU справляется куда быстрее процессора
Folding@home – проект распределенных вычислений для проведения компьютерной симуляции свертывания молекул белка. С этой задачей GPU справляется куда быстрее процессора

Другое дело, если нам надо отвезти два груза в два города, равноудаленных относительно друг друга и точки отправления, – тогда использование двух одинаковых транспортных средств позволит сократить время доставки ровно вдвое по сравнению с тем, как если бы это делал один грузовик с той же скоростью. Конечно, можно взять транспорт вдвое быстрее эталонного, и тогда можно обойтись и им одним, но что, если городов не два, а сто? Да и машина развалиться может от таких скоростей… Вот то-то и оно – иначе как большим количеством медленных машин не обойтись. Как видите, все очень сильно зависит от поставленной задачи.
Давайте посмотрим теперь на это с технической стороны. Чтобы понять, что такое GPGPU и зачем он нужен, каковы его преимущества, недостатки и отличия относительно простых CPU, надо разобраться, что именно умеют делать традиционные процессоры и, что важно, как именно они это делают.

Не CPU единым
Современный процессор – специалист широкого профиля. Он умеет очень многое. Каждую новую задачу он берет и детально рассматривает, тщательно планируя. Процессор никогда заранее не знает, что ему понадобится, – он может это узнать, только ознакомившись с инструкциями. Инструкция может «приказать» ему взять совершенно произвольный кусок памяти, прочитать оттуда нечто и произвести над этим какое-то действие. И каждая новая инструкция может быть совершенно не такой, какой была предыдущая.

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

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

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

Благодаря тому, что современные GPU имеют гораздо больше вычислительных блоков (правда, куда более примитивных), чем CPU, они эффективнее обсчитывают кучу мельких задач...
Благодаря тому, что современные GPU имеют гораздо больше вычислительных блоков (правда, куда более примитивных), чем CPU, они эффективнее обсчитывают кучу мельких задач…

Например, GPU имеют последовательную исполнительную архитектуру – вряд ли какой-то пиксель срочно потребуется обработать быстрее, чем его собратьев: все равны, как перед присягой. Отсутствие произвольных обращений к памяти позволяет несколько изменить ее и ускорить. Никогда не задумывались, почему DDR3, работая на частоте около 2 ГГц, не дает ожидаемого двукратного прироста по сравнению с DDR2 на 1066 МГц, а переход с GDDR2 на GDDR3 приводит к подобному ускорению? Именно по причине упрощенности за счет оптимизации под потоковые операции.

По той же причине нет простой памяти DDR5, а видеопамять GDDR5 живет и здравствует на частоте 4 ГГц. Также можно отметить, что двухканальный режим работы обычной оперативной памяти не дает особых преимуществ по сравнению с одноканальным, а вот удвоение битности шины видеопамяти влечет за собой ускорение, которое видно невооруженным глазом, – у этого явления тоже оттуда ноги растут. Однако в качестве минуса такого подхода можно отметить, что при отсутствии данных в видеопамяти (случай экстраординарный по современным меркам, когда у каждой второй карточки объем памяти не ниже полугигабайта) иногда приходится ждать несколько сотен тактов, пока нужные данные смогут быть прочитаны. Однако спасает положение то, что даже во время простоя блока выборки текстур видеокарте есть чем заняться: над одной и той же текстурой столько нужно всего интересного провернуть, что мало не покажется.

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

Затенители углов
Шейдеры – это то, без чего невозможно себе представить как современные игры, так и GPGPU. В узком понимании это слово начали использовать, когда понадобилось накладывать затенение (drop shadow) на объекты в игре, изменяя цвет пикселей на более темный. Раньше в видеокартах было всего несколько жестко заданных алгоритмов с ограниченным набором функций. Так как места для маневра было мало, приходилось делать игры, которые были похожи друг на друга в плане графики, – сотворить нечто индивидуальное было очень и очень сложно, зато унификация позволяла видеокарте «пережевывать» картинку быстро, а сам процесс создания игрушек был достаточно простым по сегодняшним меркам.

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

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

По ту сторону игр
Давайте разберемся, как именно работают вычисления на графических ядрах. Несмотря на то что AMD и NVIDIA все время акцентируют внимание на каких-то особенностях своих карточек, непомерно выпячивая их, по сути их архитектуры очень похожи – иначе и быть не могло, если рассуждать логически. Так что будем отмечать детали по ходу дела, по мере надобности заостряя на них внимание. За основу возьмем по одному чипу от именитых соперников – RV770 и GT200.
В центре внимания – SP, который ранее был известен только как «Shader Processor», а ныне расшифровывается еще и как «Streaming Processor».

По сути это конвейеризированный блок с последовательной исполнительной архитектурой и собственными регистрами (16 000 32-битных на SP у NVIDIA и 16 000 128-битных на SP у AMD), состоящий из двух ALU и одного FPU с функциями MUL (умножение) и ADD (сложение). Кроме простого SP есть еще и SFU – Super Functional Unit. Отличие SP от SFU заключается в том, что у NVIDIA последний занимается более сложными вычислениями – синусы, интерполяция и так далее, причем работая на удвоенной частоте. У AMD этот блок делает то же самое, что и у NVIDIA, но плюс к этому может еще и выполнять все те же функции, что и простой SP. То есть каждый SFU у HD4870 может считаться полноценным SP, в то время как у GТX280 они таковыми не являются, резервируясь для особых операций.

Однако сами по себе вычислительные блоки беспомощны, как студент без шпоры, так как помимо собственно выполнения математических операций они ничего не могут. Им требуется механизм, который бы грамотно и своевременно «кормил» их материалом для обработки, предварительно выбрав его из памяти. «Как же так? Это же типичная архитектура процессора, тут ничего нового», – скажут некоторые. Однако я совсем не кривил душой, говоря, что GPU и CPU здесь сильно различаются: так как видеокарта рассчитана на параллельные потоковые вычисления без произвольного доступа, то блок планирования операций и планирования нагрузок здесь один на множество процессоров и к тому же он очень прост сам по себе, а потому занимает мало места и работает очень быстро. Некоторое количество вычислительных блоков вместе с планировщиком и общей памятью образуют один вычислительный кластер, который называется SIMD Cluster у AMD и Streaming Multiprocessor у NVIDIA. Внутри они устроены примерно одинаково, как я уже сказал, но есть различия, которые серьезно изменяют их направленность.

Главное отличие заключается в том, что AMD лепит вместе четыре SP и один SFU, и таких неразрывных блоков в одном кластере насчитывается 16 штук, итого – 80 SP (64 SP + 16 SFU). Конкурент же не лепит SP вместе, а просто располагает их рядом по 8 штук, добавляя два SFU. Получается весомая разница – в 10 раз, если считать только количество процессоров. Однако не все так просто – NVIDIA объединяет три таких кластера в один суперкластер, называя его Texture Processing Cluster. Получается, что крупных блоков обработки и у RV770, и у GT200 поровну (по десять), но суммарно многозадачных процессоров у первого 800, а у второго – 240 (+60 SFU, которые могут быть как задействованными, так и простаивать). В каждом кластере есть локальная общая память (local store) объемом по 16 Кбайт на каждый кластер. Причем это не кэш для данных, а именно память, которую кодоваятели могут использовать по своему усмотрению, то есть и как кэш для данных, и как кэш для результатов вычислений (например, чтобы в следующий заход брать данные оттуда, а не из других кэшей, позволяя более эффективно загрузить кэш инструкций и данных).

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

Помимо local store у AMD есть еще и глобальная общая память (global store), тоже объемом 16 Кбайт, которая существует уже за пределами кластеров. Она отличается от простой памяти тем, что доступ к ней осуществляется быстрее. Наличие как local store, так и global store обусловлено тем, что это единственная возможность быстро использовать данные вычислений внутри кластера. Особенность GPU такова, что данными можно обмениваться только внутри кластера и только посредством этой local store, иначе придется каждый результат сохранять в графическую память. А поодиночке такие вещи не записываются, только кучей – это вам не CPU, в котором на два ядра, например, есть огромный кэш L2, данные из которого доступны обоим ядрам и куда можно засунуть всякую мелочь.

Благодаря global store можно быстро делиться данными между кластерами, так что NVIDIA тут немного отстает от конкурента. Сам процесс планирования достаточно прост: планировщик разбивает все данные для обработки на несколько потоков и каждый поток «скармливает» вычислительным блокам. Одна такая сформированная группа потоков называется «warp» у NVIDIA и «wavefront» у AMD. Особенность планирования в том, что каждый поток данных отдается на растерзание минимальному вычислительному блоку, которым у «зеленых» является SP, а у «красных» – группа из четырех SP и одного SP / SFU.

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

К счастью, планировщик как раз и призван следить за тем, чтобы на вычислительные блоки посылались не зависящие друг от друга инструкции. Бывает и так, что каких-то данных в памяти не оказывается, и тогда спасает от простоя смена контекста – переключение на другой warp / wavefront. В отличие от простых ЦП, в которых смена контекста может занимать много тактов, потому как каждый поток данных как товарный поезд по тяжести, в графических ядрах такое переключение происходит почти мгновенно, за один-два такта, благодаря легковесности потоков. При таком раскладе гораздо быстрее переключиться на обработку параллельного потока, чем ждать, пока придут данные для того, который обрабатывался изначально.

GTX280 одновременно планирует по 32 потока в каждом из 32 «варпов», по «варпу» на кластер, 3 кластера в суперкластере, 10 суперкластеров – итого 30 720 запланированных потоков, из которых одновременно выполняются 960. HD4870 планирует 16 384 потоков. Но не забывайте, что он может гораздо быстрее расправиться с тяжелыми потоками данных, в то время как NVIDIA ориентируется на большее количество легких задач – на это указывает и отсутствие global store, и маленькие объемы регистров, и множество кластеров (но не суперкластеров). Это огромная вычислительная мощь, которая ранее в основном использовалась для игр. Теперь же ее можно направить и в другие русла, но для этого нужен правильный API.

Краеугольный камень
К видеокарте за неимением у нее отчества можно обратиться по драйверу или API (который, опять-таки, либо постучится в драйвер, либо будет сам себе драйвер). Конечно, самый простой способ – использовать API, но их сегодняшняя реализация в лице DirectX 10.1 и OpenGL 3.0 с точки зрения GPGPU совершенно бесполезна: они просто не умеют этого делать. Ситуацию должны исправить DirectX 11 и OpenCL 1.0. Первый, по сути, будет расширенной версией DirectX 10.1, но всякие относительно программируемые тесселяторы можно оставлять за бортом – важно то, что 11-я версия позволит обращаться с видеокартой именно как с процессором.

Что касается OpenCL, то у него есть пара козырей в рукаве. Например, он оупенсорсный (что отражено в его названии). А значит, по определению популярный в среде линуксоидов и вообще программистов, так как вся документация лежит в открытом доступе и можно посмотреть на то, как экспериментируют с ним другие, ведь OpenSource-архитектуры всегда славились широким набором практических пособий. OpenCL позволит использовать одни и те же аппаратные ресурсы, вплоть до адресов памяти, совместно с одновременно запущенным OpenGL.

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

Однако ни DX11, ни OCL 1.0 пока не готовы – только сказочные обещания разработчиков плавают на волнах прессы, пенясь от кипящих страстей. На сегодняшний день все GPGPU реализуются через драйвер непосредственно. У AMD это называется FireStream, использующий Brook+, а у NVIDIA – CUDA. И то и другое, по сути, является надстройкой над языком «Си», который компилируется особым образом – конечный бинарный файл дергает за ниточки драйвера (если программирование выполнено через высокоуровневые алгоритмы) либо непосредственно общается с GPU. Преимуществ такого подхода просто тьма. Это и скорость работы, и стремление чипмейкеров оптимизировать API, и тут же большая гибкость: хочешь – выбирай алгоритм полегче и помедленнее, а хочешь – копайся в самых низах и выжимай последние соки возможностей из плодов прогресса. Был бы пламенный мотор рядом с печенью да знание английского.

Однако такой подход не без подножки. Предположим, некий программист решил написать софт для GPGPU. У него по определению сейчас не получится создать программу, работающую на продукции обеих фирм-производителей видеокарт. То есть придется либо писать две разные программы, либо ограничиться GPU только одной компании, ведь секреты своих API никто не будет выдавать, а значит, сотрудничества AMD и NVIDIA на данной стадии не предвидится. Именно для этого и нужны универсальные API, по которым мы вскользь прошлись чуть выше, – чтобы можно было написать программу, которая будет работать на любом железе, поддерживающем этот API (а в недалеком будущем разве что некоторые модели миксеров не будут их поддерживать). А вопрос скорости работы будет определяться оптимизацией софтины под определенную архитектуру, что является процессом не менее бесконечным, чем инфляция (смайл).

Профит
Прочитав все это, можно горестно покачать головой и поцокать языком: мол, «сколько же разных сложных технических штук придется узнать и выучить господам программистам, чтобы донести прогресс до нас, простых смертных». Теперь нам было бы неплохо узнать, какую же практическую выгоду дают нам все эти ухищрения.
Итак, суть GPGPU в том, что обрабатывается одновременно огромное количество относительно простых данных. Это полезно как в тех системах, где потеря времени неприемлема, так и там, где вопрос времени вовсе некритичен, но все равно хотелось бы побыстрее. Основное условие – чтобы можно было обрабатывать параллельно большое количество несложных элементов, никак не связанных друг с другом. В первую очередь приходят на ум операции, где требуется сравнение большого количества с эталоном.

Например, антивирус, в котором каждый файл будет проходить через все потоки, каждый из которых будет сравнивать его с эталонами (тут как нельзя более кстати будет и global store у AMD, с помощью которого можно послать сигнал всем веткам вычислений, что файл определен как зараженный и нет нужды проверять его дальше, да и сама архитектура RV770 позволит легко расправляться с такими тяжелыми задачами). Или, например, NIDS (Network Intrusion Detection System), система обнаружения сетевых вторжений, которая анализирует сетевые пакеты, сравнивая статистику с правилами-закономерностями, – здесь уже сильнее должна быть продукция NVIDIA с ее страстью к большому количеству легких потоков.

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

К слову сказать, такой проект уже есть, и он называется GNORT. Вот еще пример: одно из первейших приложений на NVIDIA CUDA, Badaboom Video Converter, позволяющее конвертировать видео, используя ресурсы GPU и сокращая в разы время обработки. Еще стоит упомянуть программу vReveal, улучшающую качество видеороликов, как бы реставрируя их. И конечно же, нельзя обойти такую тему, как, э-э-э, гм-м-м, восстановление пользователем «непременно своих» забытых паролей, назовем это так. Такие задачи очень легко распараллеливаются и идеально подходят для GPGPU. Примером подобных программ можно назвать Distributed Password Recovery. Использование GPGPU совершит переворот в области информационной безопасности, ведь то, над чем могли неделями биться многокластерные серверные системы, ныне сможет за сутки выполнить один компьютер с четырьмя совершенно заурядными видеокартами внутри.

Да, к сожалению, пока что ни одна реализация GPGPU для пользователей не поддерживает многочиповые конфигурации, но это только вопрос времени – ведь суперпроцессоры FireStream и Tesla же используют, хотя сейчас они по сути являются скоплениями видеокарт. Те алгоритмы защиты, которые работали на периодической смене ключа, заведомо становятся неэффективны, и в первую очередь это шифрование каналов беспроводной связи. Погуглив, например, по поводу «Pyrit», понимаешь, что на сетевой безопасности Wi-Fi при должной старательности можно поставить жирный крест. Не в последнюю очередь от вычислений на видеокарте выигрывают и игры (простите за тавтологию) – одну такую ситуацию мы уже рассмотрели на примере обработки графики и физики ящика.

Но самое важное в GPGPU, пожалуй, это польза для науки от всех этих новшеств. Например, появляется возможность «параллельной реализации расчета парных межчастичных взаимодействий в методе молекулярной динамики при нулевых граничных условиях на графических процессорах с применением платформы NVIDIA CUDA. <…> На видеокарте NVIDIA GeForce 8800 GTX по сравнению со скалярной версией на процессоре AMD Athlon64 2,1 ГГц достигнуто ускорение до 660 раз для системы из 49152 частиц», как сообщает нам «Гугл» и журнал «Вычислительные методы и программирование».

Или, например, то, с чего по большому счету все началось, – Folding@Home. Не зря все-таки этот кризис вылез наружу – вместо того чтобы продолжать бешеную гонку мегагерц, все наконец-то начали думать, как выжать максимум из существующего железа. По большому счету уже наступил тот день, когда, собирая знакомому компьютер, уже нельзя вычеркивать из конфигурации приличную видеокарту только потому, что человек вообще не играет в игры. Да и ноутбуки получают преимущество благодаря GPGPU, а в особенности нетбуки и неттопы на новехонькой платформе Ion, ведь они основаны на чипе NVIDIA 9-й серии, который поддерживает GPGPU.

Да, кстати, самое приятное в этой ситуации то, что у многих уже стоят в ноутбуках / компьютерах видеочипы, способные не завтра, не послезавтра, а уже вчера ускорять работу всего, до чего дотянутся руки «параллелизирующих» программистов. В общем будущее уже постучалось к нам в дверь…

С чего все началось
Году эдак в 2000-м был запущен проект Стэнфордского университета по расчету симуляции свертывания белка. Он помогает детальнее изучить поведение белка, проливая свет на пути излечения от многих болезней, например от болезни Альцгеймера. Проект использует распределенные вычисления – любому человеку, установившему клиент на компьютере, присылается порция данных для обработки, а результат вычислений отсылается обратно на сервер. В 2006 году был создан специальный клиент для владельцев карт Radeon на чипах R580 и R520, который первым использовал вычисления на GPU в таких масштабах. Даже в самых первых версиях производительность в зависимости от мощности эталонного компьютера и видеокарты возрастала более, чем в 20 раз.

Никто не прокомментировал материал. Есть мысли?