CUDA.NET для .NET-разработчика (ч.1)

Автор: Topol Суббота, Май 5th, 2012 Нет комментариев

Рубрика: Операционные системы

Думаю, аббревиатура CUDA в достаточной степени на слуху, и знакома многим из вас. Википедия подсказывает нам, что CUDA расшифровывается как Compute Unified Device Architecture, что можно вольно перевести как «архитектура устройств с поддержкой универсальных вычислений». Этими устройствами являются видеокарты производства NVidia. Поддержку CUDA обеспечивают карточки GeForce, начиная с 8xxx и выше, а также модельный ряд «профессиональной серии» — Quadro и Tesla. Таких устройств выпущено очень большое количество — от самых дешевых до супердорогих, и распространены они повсеместно. Тем интереснее становится сама технология — успешное её применение сулит хорошее преимущество по производительности. Разумеется, не всё так просто. Создание многопоточных программ, даже исполняемых на привычном CPU, сопряжено с определённым количеством трудностей. Лишь небольшое число приложений являются «распараллеленными», и дело тут не столько в технической сложности, сколько в характере задач и подходов к их решению. Чего уж тут говорить про GPGPU-вычисления — они имеют свои характерные особенности, ограничивающие сценарии применения. Вообще, это тема отдельного обсуждения, и я не стану в неё углубляться — предлагаю вместо этого подробнее познакомиться с CUDA и возможностями применения её в .NET приложениях.

Часть 1. Аппаратная платформа
Технология CUDA представляет собой аппаратно-программный комплекс, обеспечивающий использование мощностей видеоадаптера для неграфических вычислений. Чтобы лучше понимать суть процессов, происходящих в GPU при выполнении таких вычислений, давайте ознакомимся с её аппаратной частью.

Современные видеоадаптеры являются технически достаточно сложными устройствами, фактически, это «мини-компьютер в компьютере»: на одной плате расположены вычислительные модули, память, контроллеры и др. устройства. Рассмотрим наиболее важные для нас как прикладных программистов детали: устройство CUDA-процессора и особенности его работы.

GPU NVidia по характеру работы являются SIMD-устройствами: Single Instruction, Multiple Data, т.е. некоторый входной поток данных, состоящий из однородных элементов, обрабатывается одной инструкцией (или набором инструкций). В качестве результата мы также получаем однородный поток элементов. При этом каждый элемент может быть обработан независимо от остальных, что позволяет обрабатывать элементы параллельно:

Строго говоря, используется не принцип SIMD, а SIMT — single instruction, multiple threads. Общая задача — обработка входного потока данных — делится на множество мелких подзадач, каждая из которых обрабатывается своей нитью. Нити же исполняются параллельно на вычислительных модулях, в качестве которых используется набор потоковых мультипроцессоров (SM, Streaming Multiprocessor в англоязычной терминологии). Давайте посмотрим, из чего состоит SM архитектуры Tesla 8:

Роль «рабочей лошадки» здесь играют SP — потоковые процессоры (streaming processor), или CUDA-процессоры. Непосредственно они выполняют инструкции по обработке данных. Из рисунка видно, один мультипроцессор включает в себя 8 таких исполнительных блоков. Кроме них есть два специальных модуля SFU — special function unit, предназначенных для выполнения сложных вычислений (например, тригонометрических).

Далее, в состав SM входит набор регистров (на схеме — Registers). Их количество в SM архитектуры Tesla 8 равно 8192, разрядность каждого из них — 32 бита. Кроме того, SM содержит ещё и общую для всех CUDA-ядер память (Shared Memory), суммарным объёмом 16 Кбайт. Фактически, и блок регистров, и общая память — наиболее близкая к потоковому процессору единица памяти, взаимодействие с ней очень быстрое, значительно быстрее, чем с DRAM — модулями памяти, распаянными на плате. Оно и понятно — Register и Shared Memory находятся на одном кристалле с SP. Главное отличие между регистровой и разделяемой памятью в том, что регистры распределяются на этапе компиляции, и во время выполнения строго разграничены между потоками, выполняющимися на CUDA-ядрах. Т.е. их распределением управляет компилятор. В то время как Shared Memory могут использовать все нити выполнения программы, и эта память доступна программисту посредством ключевого слова shared.

Именно такие SM используются при построении видеоадаптеров поколения 8xx. Например, видеокарта GeForce 8500GT включает в себя 2 потоковых мультипроцессора (т.е. 16 CUDA-процессоров), а GeForce 8800GT на чипе G92b — целых 14 штук (т.е. 112 процессоров)! Разумеется, чем больше CUDA-процессоров доступно и на чем более высокой частоте работает шейдерный блок, тем выше максимальная производительность. Впрочем, на возможности устройства влияет ещё много факторов: например, производительность подсистемы памяти и т.д. Традиционно для DRAM используется GDDR2 и GDDR3 память с самыми различными характеристиками. Однако, стали появляться решения на базе GDDR5 чипов — к ним относятся флагманские продукты NVidia.

Потоковые мультипроцессоры в свою очередь находятся внутри TPC — Texture Processing Claster, т.е. блока обработки текстур. Схематически его можно изобразить так:

Здесь TEX — это блок доступа к текстурам. Видно, что один блок обработки текстур содержит 2 мультипроцессора. Напомню, это относится к архитектуре Tesla 8, у более поздней продукции схема немного иная. Собственно, TPC — наиболее крупная структурная единица. Шейдерный блок видеокарты состоит из набора таких вот модулей и набора различных вспомогательных схем служащих, например, для организации доступа к DRAM.

SM архитектуры Tesla 10 (чип GT200) содержат также 16 процессоров, но в дополнение имеют блок для выполнения сложных операций с повышенной точностью (Double precision):

Очевидно, что выполнение инструкций двойной точности достаточно «дорого» — соответствующий блок всего один внутри SM, да и работает он медленнее потокового процессора. В реальности соотношение производительности DP по сравнению с SP колеблется около 1/4. Мультипроцессоры такого вида содержатся, например, в видеоадаптерах GTX260, GTX280 и др. Соответственно, и блок обработки текстур (TPC) выглядит немного иначе:

Т.е. содержит в себе уже 3 мультипроцессора. Этим и объясняется кратность тройке количества CUDA-процессоров у видеокарт на основе GT200. Например, есть модификация GeForce GTX260, которая имеет 216 потоковых процессоров. Легко догадаться, что 216 SP «рассчитывается» как 8 (SP внутри SM) х 3 (SM в TPC) x 9 (количество TPC).

С выходом первых DX11-совместимых решений от AMD конкуренция на рынке усилилась, и в первом квартале этого года NVidia представила образцы продукции на базе новейшей архитектуры Fermi — видеоадаптеры GTX480 и GTX470. Потоковые мультипроцессоры, входящие в состав GF100 (именно такой код имеет новое детище), имеют «на борту» целых 32 ядра CUDA, улучшенный планировщик выполнения вычислений. Соответственно, увеличилось количество регистров, блоков специальных функций SFU, объём разделяемой памяти и т.д. SM в новой архитектуре объединяются в кластеры Graphics Processing Cluster, каждый из которых содержит четыре SM и один блок растеризации Raster Engine. Если посмотреть ещё «на уровень выше», то чип GF100 имеет 4 кластера GPC, снабжен шестью 64-битными контроллерами памяти с поддержкой GDDR5, и т.о. максимально содержит 512 ядер CUDA. В конечных продуктах часть ядер может быть недоступна, поскольку уровень выхода годных чипов пока не так высок, как ожидалось, и приходится «отрезать» некачественные блоки. Это же позволяет варьировать мощность разных видеокарт и, в конечном итоге, их цену. GeForce GTX480 имеет 480 потоковых процессоров, а GTX470 и того меньше — всего 448. Кроме того, значительно ускорены вычисления с двойной точностью, и с этим связан интересный момент. Видеокарты игровой серии GeForce потенциально имеют значительно большую пиковую производительность, которая в серийно выпускаемых образцах снижена, что позволяет выпустить т.н. «профессиональные» ускорители серии Tesla, которые уже будут работать на полную мощность, иметь схемы коррекции ошибок ECC и т.д. И стоить они будут уже гораздо дороже.

Ок, что «под капотом» CUDA-совместимых карточек, примерно понятно. В чём же принципиальная разница между многоядерным CPU и GPGPU? На мой взгляд, лучше всего это отражает следующая картинка из руководства NVidia CUDA programming guide:

Значительная часть кристалла у GPU отводится на собственно вычислительные блоки, в то время как доля кэша и управляющих элементов ниже. У CPU же иной принцип — довольно «жирный» кэш, всего несколько ядер, которые при это умеют исполнять большой набор инструкций и работают обычно на более высокой частоте. Дальше, при рассмотрении программной модели и способа выполнения CUDA-программ, мы увидим, как такая разница в архитектуре отражается на принципах программирования. Чтобы обеспечить столь большое количество ядер данными, GPU просто необходим широкий канал доступа к памяти. Что, собственно и реализовано (картинка опять же из руководства):

Эта диаграмма наглядно показывает, насколько шире канал доступа к DRAM у GPU NVidia по сравнению с CPU производства Intel, выпускаемыми в аналогичный период времени. Наличие большого количества вычислительных блоков не могло сказаться бесследно на максимальной производительности. Приведу заключительный график из того же документа:

Характеристики пиковой производительности поистине впечатляют! Разумеется, чтобы выпустить этого «джинна» и получить значительный прирост при исполнении на GPU, нужно потрудиться и адаптировать свои алгоритмы для исполнения на CUDA-ядрах. Конечно, такие мощности не даются бесплатно, и за высокую производительность приходится платить достаточно высоким энергопотреблением. В частности, флагман CUDA-обработки GTX480 потребляет при номинальных частотах до 250Вт.

Названия вроде 8800GT, GTX285 и пр. удобны для маркетологов, однако по ним довольно сложно догадаться, что собой представляет устройство с точки зрения CUDA. Чтобы устранить это недоразумение, было введено понятие CUDA compute capability — этакая «версия» возможностей устройства. В руководстве NVidia CUDA programming guide, размещенном на сайте NVidia, приведён список видеочипов с их характеристиками, в т.ч. compute capability, а также описание возможностей каждой «версии». С помощью него вы легко можете определить, на что с точки зрения CUDA способна ваша видеокарта.

Итак, вкратце основные выводы:

  • CUDA использует принцип SIMT;
  • нити выполняются на потоковых процессорах;
  • потоковые процессоры размещаются в потоковых мультипроцессорах;
  • мультипроцессор содержит различного вида память, планировщик нитей, модули специальных функций;
  • вычислительная мощность прямо пропорциональна числу мультипроцессоров;
  • с CUDA совместимо огромное число видеоадаптеров, начиная от GeForce 8300 и заканчивая GeForce GTX480.

Источник: thevista.ru

 

Оставить комментарий

Чтобы оставлять комментарии Вы должны быть авторизованы.

Похожие посты