УДК: 004.04
Бахтенко Е. А.
аспирант,
Кафедра Автоматики и вычислительной техники (АВТ) Вологодский государственный университет
Баланин Е. О.
аспирант,
Кафедра Информационных систем и технологий (ИСиТ) Вологодский государственный университет
Струков П. С.
магистр,
Кафедра Информационных систем и технологий (ИСиТ) Вологодский государственный университет
АНАЛИЗ МЕТОДОВ И СРЕДСТВ ИСПОЛЬЗОВАНИЯ ПРОЦЕССОРОВ РАЗЛИЧНОЙ АРХИТЕКТУРЫ ДЛЯ ВЫПОЛНЕНИЯ ВЫЧИСЛЕНИЙ
В работе произведен анализ основных принципов использования процессоров разной архитектуры и средств использования возможностей графического процессора. Рассмотрены возможности основных современных решений для общих вычислений на графическом процессоре, а также особенности следующих типов памяти: регистры, локальная, глобальная, разделяемая, константная и текстурная.
Ключевые слова: процессор, архитектура, память, видео, изображение, обработка.
Развитие информационных технологий предъявляет новые требования к производительности систем. Вызвано ли это повышением сложности задач, увеличением объёма данных для обработки, или конкуренцией, но в конечном итоге всё упирается в возможности компьютеров. Это особенно заметно в коммерческом секторе, где очень многое зависит от того, кто быстрее получит обработанные данные, на основе которых можно принимать экономические решения.
Производительность компьютерных систем всегда будет одной из таких задач, решение которых не становится проще со временем, потому что на любую сверхпроизводительную систему всегда найдётся подходящая задача. Однако задача в нахождении дополнительной производительности не становится от этого менее актуальной [1].
Центральный процессор не всегда является оптимальным инструментом для расчетов. В случае если данные не зависят друг от друга и расчеты хорошо поддаются распараллеливанию, графический процессор справится намного лучше, за счёт большего количества ядер, и следовательно — потоков. Прирост производительности может быть в несколько десятков раз.
В качестве примера можно привести расчеты положений объектов относительно друг друга в ситуации, когда таких объектов множество. Другой пример — редактирование изображений. Именно обработкой изображений и занимается моё программное средство, ведь это как раз та задача, где полученная производительность будет значительна.
Основные принципы использования процессоров разной архитектуры. Из-за различной архитектуры процессоров вытекает множество серьёзных последствий. В зависимости от задачи, процессоры разной архитектуры показывают различную производительность. Очевидно, что выбор оптимального инструмента для каждой конкретной проблемы является крайне приоритетной задачей.
Поэтому стоит рассмотреть плюсы и минусы центрального и графического процессоров для того чтобы адекватно оценить их возможности, и, следовательно, не ошибиться при выборе инструмента для будущих задач.
Центральный процессор состоит из нескольких достаточно производительных ядер. Обычно используется от 2-х до 6 (иногда до 8) ядер. В серверных процессорах ядер, конечно, больше, но на самом деле это не настолько важно, когда речь идёт о сравнении центрального и графического процессоров. Потому что в последних используется несколько сотен менее производительных ядер.
Именно из-за этого отличия следует основное требование к задачам, решение которых более оптимально на графическом процессоре, нежели на центральном — задачи должны быть хорошо распараллеливаемыми, чтобы каждое ядро независимо могло выполнять свою часть задачи. Очевидно, что для абсолютного большинства задач нескольких ядер с парой потоков на каждом у центрального процессора будет достаточно, однако в тех немногих задачах, где необходимо задействовать множество параллельных потоков графический процессор поможет сэкономить достаточно большое количество времени.
Обработка изображений является отличным примером, где использование графического процессора должно дать значительное преимущество, ведь каждый пиксель изображения можно обрабатывать независимо от другого. Либо можно разбить изображение на группы по несколько пикселей и обрабатывать их, это зависит от подхода и алгоритмов.
Анализ и выбор средств для использования возможностей графического процессора. Использование графического процессора для общих вычислений называется GPGPU (General purpose graphics processing units) — ГПУ общего назначения. Существует несколько различных реализаций GPGPU. Часто крупные корпорации делают свою реализацию для обретения конкурентного преимущества, именно поэтому многие технологии привязаны к аппаратной платформе определенного производителя. Необходимо провести анализ наиболее распространенных.
nVidia CUDA. CUDA — программно-аппаратная архитектура параллельных вычислений, позволяющая использовать возможности графических процессоров nVidia для общих вычислений. CUDA SDK (Software Development Kit, комплект средств разработки) позволяет программистам реализовывать на специальном упрощённом диалекте языка программирования C алгоритмы, выполнимые на графических процессорах nVidia, и включать специальные функции в текст программы на C. Архитектура CUDA даёт разработчику возможность по своему усмотрению организовывать доступ к набору инструкций графического ускорителя и управлять его памятью.
Рассмотрим вычислительную модель архитектуры на рисунке 1.
_1
Рисунок 1 — Вычислительная модель CUDA
Ядро GPU состоит из блоков, которые группируются в сетку размерностью N1*N2*N3. Каждый такой блок состоит из нитей (рисунок 2), которые и выполняют вычисления. Нити сформированы в виде трёхмерного массива.
Рисунок 2 — Устройство блока GPU
Сама технология CUDA (компилятор nvcc.exe) вводит ряд дополнительных расширений для языка C, которые необходимы для написания кода для GPU:
- спецификаторы функций, которые показывают, как и откуда буду выполняться функции;
- спецификаторы переменных, которые служат для указания типа используемой памяти GPU;
- спецификаторы запуска ядра GPU;
- встроенные переменные для идентификации нитей, блоков и др. параметров при исполнении кода в ядре GPU;
- дополнительные типы переменных.
При использовании GPU разработчику доступно несколько видов памяти: регистры, локальная, глобальная, разделяемая, константная и текстурная память. Каждая из этих типов памяти имеет определенное назначение, которое обуславливается её техническими параметрами (скорость работы, уровень доступа на чтение и запись). Иерархия типов памяти представлена на рисунке 3.
Block(0,0)
01оск(1,0)
Shared memory
Shared memory
Register Register
tit
Thread (0, 0) Thread (0, 1)
t fit I
Register Register
t I I
Thread (0, 0)
I
Thread (0, 1)
I
Рисунок 3 — Типы памяти видеокарты
Регистровая память (register) является самой быстрой из всех видов. Определить количество регистров доступных GPU можно с помощью функции cudaGetDeviceProperties. Рассчитать количество регистров, доступных одной нити GPU, так же не составляет труда, для этого необходимо разделить общее число регистров на произведение количества нитей в блоке и количества блоков в гриде. Все регистры GPU 32 разрядные. В CUDA нет явных способов использования регистровой памяти, всю работу по размещению данных в регистрах берет на себя компилятор.
Локальная память (local memory) может быть использована компилятором при большом количестве локальных переменных в какой-либо функции. По скоростным характеристикам локальная память значительно медленнее, чем регистровая. В документации от nVidia рекомендуется использовать локальную память только в самых необходимых случаях. Явных средств, позволяющих блокировать использование локальной памяти, не предусмотрено, поэтому при падении производительности стоит тщательно проанализировать код и исключить лишние локальные переменные.
Глобальная память (global memory) — самый медленный тип памяти, из доступных
GPU. Глобальные переменные можно выделить с помощью спецификатора _global_, а
также динамически, с помощью функций из семейства cudMallocXXX. Глобальная память в основном служит для хранения больших объемов данных, поступивших на device с host^, данное перемещение осуществляется с использованием функций cudaMemcpyXXX. В алгоритмах, требующих высокой производительности, количество операций с глобальной памятью необходимо свести к минимуму.
Разделяемая память (shared memory) относиться к быстрому типу памяти. Разделяемую память рекомендуется использовать для минимизации обращение к глобальной памяти, а также для хранения локальных переменных функций. Адресация разделяемой
памяти между нитями потока одинакова в пределах одного блока, что может быть использовано для обмена данными между потоками в пределах одного блока. Для размещения данных в разделяемой памяти используется спецификатор_shared_.
Константная память (constant memory) является достаточно быстрой из доступных GPU. Отличительной особенностью константной памяти является возможность записи данных с хоста, но при этом в пределах GPU возможно лишь чтение из этой памяти, что и обуславливает её название. Для размещения данных в константной памяти предусмотрен
спецификатор_constant_. Если необходимо использовать массив в константной памяти, то
его размер необходимо указать заранее, так как динамическое выделение в отличие от глобальной памяти в константной не поддерживается. Для записи с хоста в константную память используется функция cudaMemcpyToSymbol, и для копирования с device^ на хост cudaMemcpyFromSymbol, как видно этот подход несколько отличается от подхода при работе с глобальной памятью.
Текстурная память (texture memory), как и следует из названия, предназначена главным образом для работы с текстурами. Текстурная память имеет специфические особенности в адресации, чтении и записи данных.
DirectCompute. Данное решение является API (интерфейс программирования приложений), входящий в DirectX, начиная с 10 версии. Использование ограничено ОС семейства Windows.
DirectCompute поддерживается всеми основными компаниями на рынке производства графических процессоров: AMD и nVidia. На графических процессорах производства AMD технология AMD FireStream работает «поверх» DirectCompute. AMD в сотрудничестве с Pixelux Entertainment и Эрвином Кумансом в рамках проекта Open Physics Initiative работает над переносом физических вычислений на GPU. Кроме этого, в сотрудничестве с компанией CyberLink AMD работает над «переносом» на DirectCompute алгоритмов кодирования и декодирования видеоданных, редактирования видео, распознавания лиц.
На графических процессорах производства nVidia DirectCompute работает «поверх» CUDA.
OpenCL. Данное решение — открытый фреймворк для общих вычислений на графическом процессоре. Первая версия стандарта была представлена в конце 2008 года. Группа компаний, состоящая из Apple, nVidia, AMD, IBM, Intel, AMD и Motorola и многих других участвовали в его разработке. Последняя версия OpenCL 1.2 вышла в декабре 2012 года.
Основные возможности, которые реализовали создатели стандарта для ускорения вычислений:
- можно объявлять в качестве переменных 2, 4, 8 и 16-мерные вектора. Это делается соответственно: int2, int4, int8, int16. Так же можно объявлять double, byte и все остальные типы. Вектора соответствующей размерности можно складывать, вычитать, делить или умножать, а также любой вектор можно делить или умножать на число;
- есть ряд функций, оптимизированных под вектора и позволяющих работать непосредственно с ними. К таким функциям относятся функции вычисления расстояния, функции векторного произведения;
- возможность сливать вектора, беря части от одного и другого, а также склеивать в более большие;
- кроме стандартного набора math.lib в OpenCl имеются так называемые native функции. Это функции, основаны непосредственно на использовании некоторых функций видеокарт и на загрублённой математике. Не советуется применять их при сверхточных расчётах, но в случае фильтрации изображений разницу невозможно заметить;
- кроме «простых функций» разработчики создали целый ряд, называемый common function. Это функции, часто встречающиеся при обработке изображений. Например:
mad(a,b,c) = a * b + c, mix(a,b,c) = a + (b - a)*c. Эти функции выполняются быстрее, чем соответствующие им математические действия;
- синхронизация достигается несколькими способами. Первый — использование барьеров — команд, на которых остановится процесс пока все прочие процессы или процессы его рабочей группы не достигнут барьера.
Второй вариант синхронизации между потоками — atomic функции. Это функции, предотвращающие одновременное обращение к памяти.
Код OpenCL выполняется драйвером, поэтому фреймворк не ограничивает разработчика в выборе языка, необходимы только нужные библиотеки для среды разработки. Модель платформы представлена на рисунке 4.
Рисунок 4 — Модель платформы OpenCL
Как видно на рисунке 4, вычислительное устройство состоит из множества узлов, в каждом из которых находятся вычислительные элементы, которые и выполняют вычисления. На рисунке 5 изображена модель памяти.
Рисунок 5 — Архитектура памяти Ореп^
Все рабочие единицы независимы и работают параллельно, у каждой из них есть своя закрытая память, к которой кроме самой единицы никто не может обратиться, она является самой быстрой из доступных типов памяти. Если рабочей единице необходимо обменять данные с другими рабочими единицами, то они могут использовать локальную память, но только в пределах одной рабочей группы. Если они принадлежат разным рабочим группам,
то следует воспользоваться глобальной памятью, но она работает заметно медленнее.
Литература
1. Перепёлкин Е. Е., Садовников Б. И., Иноземцева Н. Г. Вычисления на графических процессорах (GPU) в задачах математической и теоретической физики. — URSS Москва, 2014. — С. 176.