CUDA-программирование. Часть 1

Если ещё пару лет назад мощная видеокарта была исключительно игровым компонентом ПК, то сейчас спектр её применений значительно расширился благодаря высокой производительности и что ещё более важно — удобной программируемости.

В первой части цикла мы рассмотрим основные идеи, согласно которым и создавались CPU и GPU, для каких целей, и некоторые особенности архитектуры, посредством которых реализуется многопоточное выполнение операций в центральном и графическом процессорах. В продолжении же будет рассказано о расширении языка программирования С — CUDA — с помощью которого можно непосредственно создавать приложения, использующие ресурсы GPU NVIDIA для ускорения вычислений.

Итак, у CPU и GPU два совершенно разных подхода к распараллеливанию задачи, что объясняется различиями в сферах применениях, спецификах задач, и следовательно, в архитектурах. Так, CPU изначально создавался для выполнения одного потока вычислений с максимальной скоростью. Для большей производительности в CPU были добавлены конвейеризация, когда одновременно на разных функциональных блоках CPU выполняются соответствующие команды, наборы инструкций SSE для параллельных векторных расчетов. Впрочем, несмотря на многие качественные улучшения, значительное время именно увеличение частоты было главным способом приумножить скорость процессора. Но, подойдя к отметке 4 ГГц, рост существенно замедлился в силу технологических причин, что привело к необходимости иного способа увеличить производительно CPU — а именно многоядерности. Intel к этому времени выпустил процессоры Pentium 4 с функцией Hyper-Threading, суть которой сводилась к созданию двух логических процессоров и более рациональному использованию ресурсов физического CPU. Но эффективность HT оказалась не столь велика (0-10% в зависимости от задачи), что можно обусловить уже значительной оптимизацией вычислений на одном ядре. Увеличение самих ядер, конечно же, даёт 100% увеличение теоретической производительности, но при этом существенно вырастает и цена производства. И как вы можете заметить, рост ядер в современных процессорах происходит очень медленно, что, конечно не вселяет особого оптимизма.
Видеокарта же, в отличие от процессора, изначально ориентирована на выполнение большого числа однотипных операций применительно к массивам данных — текстурам. И для этих целей значительно больше подходит не одно универсальное сложное ядро, а несколько простых ядер, содержащих сотни ALU (см. рис. 1), способных обеспечить высокую производительность и простоту управления — что, кстати, позволяет сэкономить на большом количестве транзисторов относительно ЦП. Переключение между потоками ядро GPU выполняет значительно быстрее в силу своей простоты, не нуждается в большом объёме кэш памяти. Здесь дело в том, что специфика работы с текстурами, родная для GPU, подразумевает доступ ко всему массиву данных, и если будут прочитаны данные в начале блока, то будут прочитаны и последующие, притом с достаточно большой скоростью и малыми задержками, которые для видеочипа к тому же не столь важны. А вот для CPU приходится делать большой кэш (у современных процессоров его площадь составляет около 50%) для приемлемых задержек. Таким образом, GPU рациональнее использует транзисторный ресурс и использует более простую логику.

Первыми программируемыми видеочипами стали NV20 и R200, однако их возможности были очень скудны, и говорить о каком-либо применении этих GPU кроме как для игр было невозможно. Но уже NV30 развязал руки программистам, и после этого начали развиваться технологии GPGPU (General-purpuse computations on GPU). Так, разработка Стенфордского Университета — BrookGPU (это вам не Политех с его не доступным простым смертным кластером) стала первой распространившейся средой для GPGPU — расширение языка С, отдельный компилятор позволяли программисту абстрагироваться от прослоек DirectX/OpenGL и писать привычный код — отметим, что ранее необходимо было знать какое-либо 3D API, чтобы получить доступ к ресурсам видеокарты и создать GPGPU-приложение. ATI и NVIDIA обратили внимание на эту технологию, которая открывала новые рынки сбыта. Первая после этого стала разрабатывать программируемый интерфейс CTM (Close-To-Metal), позже ставший частью Stream SDK, а вторая взялась за создание CUDA.

Продолжение — в ближайших номерах «НМБ»...


Рекомендуем почитать: