Многопоточность и Intel® Integrated Performance Primitives

Threading and Intel® Integrated Performance Primitives [Eng., PDF 230KB]

Аннотация

В задаче параллелизации приложения не существует единого решения. Подобным образом, есть множество путей реализации многопоточности в приложениях на базе Intel® Integrated Performance Primitives (Intel® IPP): она может быть реализована на уровне примитивов (в рамках библиотеки Intel® IPP) или на более высоком уровне в приложении. В данной статье будут рассмотрены несколько способов, которые приложение на основе Intel IPP может использовать для безопасного и эффективного параллельного выполнения.

Вводные данные

Intel IPP представляет из себя набор высокооптимизированных функций для приложений, направленных на медиа-обработку и обработку данных. В библиотеку включены оптимизированные функции часто используемых фундаментальных алгоритмов из разных областей науки, включая обработку сигналов, кодирование и декодирование изображений, звука и видео, сжатие данных, обработку строк и шифрование. Здесь используюься инструкции SIMD (single instruction multiple data) и SSE (streaming SIMD extensions), а также аппаратные возможности по параллельному многопоточному выполнению, доступные в современных процессорах Intel. Многие из инструкций SSE, присутствующие в современных процессорах, являются аналогами команд в алгоритмах цифровой обработки сигналов и идеально подходят для оптимизации алгоритмов, работающих с массивами данных и векторами.

Библиотека Intel IPP может использоваться в программах для операционных систем Windows*, Linux*, Mac OS* X, QNX* и VxWorks*. Она совместима с компиляторами Intel® C и Fortran, Microsoft Visual Studio* C/C++, а также gcc, включённого в большинство дистрибутивов Linux. Библиотека была протестирована на разных поколениях процессоров Intel и AMD, включая Intel® Core™ и Intel® Atom™. Поддерживаются 32-х и 64-разрядные архитектуры и операционные системы.

Введение

TБиблиотека Intel IPP была создана для помощи в реализации разных методов параллелизации. Библиотека по своему устройству является потокобезопасной, что означает, что её функции могут одновременно вызываться из разных потоков одного приложения. К тому же существуют варианты со встроенной многопоточностью на базе библиотеки Intel OpenMP*, что позволяет повысить производительность приложения, без необходимости переписывать исходный код.

Примитивы Intel IPP (функции низкого уровня, составляющие основу библиотеки Intel IPP) являются собранием алгоритмических элементов, спроектированных для многократного применения над векторами и матрицами – идеальное свойство для использования в многопоточных приложениях. Примитивы не зависят от операционной системы, они не используют блокировки, семафоры или глобальные переменные и обращаются только к стандартным библиотечных функциям С по выделению памяти (malloc/realloc/calloc/free)для получения памяти для временного или постоянного хранения данных. Чтобы ещё сильнее уменьшить зависимость от внешних системных функций, можно использовать интерфейс i_malloc, чтобы заменить собственные функции выделения памяти на стандартные функции С.

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

Даже в однопоточном приложении применение библиотеки Intel IPP значительно увеличивает производительность, предоставляя лёгкий доступ к инструкциям SIMD (MMX, SSE, и т.д.) через набор функций, созданных для использования в алгоритмах с большим количеством вычислений.

На рис. 1 показано относительное среднее увеличение производительности, измеренное для продуктов на Intel IPP из разных областей обработки данных, сравнительно с эквивалентными функциями, реализованными без использования команд MMX/SSE. Реальная картина производительности может слегка отличаться.



Рис. 1. Относительное среднее увеличение производительности в разных областях применения продуктов на Intel® IPP.

Конфигурация системы: процессор Intel® Xeon® Quad-Core, 2.8ГГц, память 2ГБ, ОС Windows* XP с библиотекой Intel IPP 6.0

Рекомендации

Самым простым и быстрым способом задействовать эффект многопоточности с помощью библиотеки Intel IPP является подключение распараллеленного варианта библиотеки. Можно также встроить один из примеров, прилагаемых к библиотеке. Это не требует значительных изменений кода и может дать дополнительные преимущества в производительности – в дополнение к полученной с помощью простого применения Intel IPP.

Три основных варианта библиотеки распространяются (как в версии 6.1) в виде: однопоточной статической библиотеки, многопоточной статической библиотеки и многопоточной динамической библиотеки. Все варианты являются потокобезопасными. Однопоточный вариант может использоваться в программах с привилегированным режимом работы (kernel-mode) или в случаях, когда использование OpenMP либо недопустимо, либо невозможно (например, в ОС реального времени).

Два варианта потоков: OpenMP и Intel IPP
Примитивы низкого уровня в Intel IPP являются базовыми атомическими операциями, что ограничивает объём возможного параллелизма 15 процентами функций библиотеки. Библиотека Intel OpenMP использовалась для реализации этого находящегося «за сценой» параллелизма и подключается по умолчанию при использовании одного из многопоточных вариантов библиотеки.

Полный список многопоточных примитивов дан в файле ThreadedFunctionsList.txt, находящемся в папке документации Intel IPP.

Замечание: тот факт, что библиотека Intel IPP была собрана с помощью компилятора Intel C и OpenMP не означает, что программа также должна использовать эти средства. Примитивы библиотеки Intel IPP распространяются в двоичном формате, совместимом с компиляторами С, для релевантных операционных систем, и сразу готовы для подключения к приложениям. Разработчик может создавать программы, использующие Intel IPP, с помощью инструментов Intel или любых других средств разработки.

Контроль потоков OpenMP в примитивах Intel IPP
По умолчанию количество потоков OpenMP, используемых распараллеленными примитивами Intel IPP, равно количеству аппаратных потоков системы, что определяется числом имеющихся процессоров. Например, четырёхядерный процессор с поддержкой технологии Intel® Hyper-Threading (Intel® HT Technology) имеет восемь аппаратных потоков (каждое из четырёх ядер поддерживает два потока). Двухъядерный процессор без поддержки технологии Intel HT имеет два аппаратных потока.

Два примитива Intel IPP предоставляют функции контроля и получения статистики по потокам OpenMP в многопоточных вариантах Intel IPP: ippSetNumThreads() и ippGetNumThreads(). Вызов ippGetNumThreads возвращает текущее максимальное количество потоков, а ippSetNumThreadsизменяет максимальное количество потоков. Функция ippSetNumThreads не позволяет задавать количество потоков большее, чем есть в наличии аппаратных потоков – это количество служит верхним пределом числа программных потоков OpenMP, которое может быть использовано в многопоточном примитиве. Некоторые функции Intel IPP могут использовать меньшее количество потоков, чем указано, если это повышает их общую эффективность, но никогда не будут использовать большего количества.

Чтобы запретить использование OpenMP в параллельном варианте библиотеки Intel IPP, в начале приложения нужно поставить вызов ippSetNumThreads(1) или подключить к приложению однопоточную статическую библиотеку Intel IPP.

Библиотека OpenMP обращается к нескольким конфигурационным переменным окружения. Конкретно, OMP_NUM_THREADS устанавливает число потоков по умолчанию (верхний предел), которое используется библиотекой OpenMP. Однако это значение игнорируется, если оно больше количества аппаратных потоков в системе (о чём говорилось выше) или с помощью функции ippSetNumThreads установлено меньшее значение. Приложения на основе OpenMP, которые не используют Intel IPP, могут всё же реагировать на значение переменной OMP_NUM_THREADS. С другой стороны, на эти приложения не влияет вызов функции ippSetNumThreads из приложений, использующих Intel IPP.

Вложенные OpenMP
Если приложение на Intel IPP одновременно распараллелено с помощью OpenMP, параллельные примитивы Intel IPP, вызываемые из программы, могут выполняться как однопоточные примитивы. Это происходит, если примитив вызывается из параллелизованной OpenMP секции кода, и в библиотеке Intel OpenMP запрещён вложенный параллелизм (что установлено по умолчанию).

Вложенные параллельные участки OpenMP создают опасность создания одновременно большого количества потоков, что может вызывать ситуацию oversubscription на аппаратные потоки. Создание параллельного участка всегда имеет свои издержки, и издержки на создание вложенных параллельных регионов могут перевесить выгоду от них. В общем случае, приложения, распараллеленные с помощью OpenMP и использующие примитивы Intel IPP, должны запрещать параллельность в библиотеке Intel IPP либо с помощью вызова ippSetNumThreads(1), либо используя однопоточную статическую библиотеку Intel IPP.

Привязка к ядрам
Некоторые из примитивов Intel IPP, предназначенных для операций по обработке сигналов, запускают параллельные потоки, которые используют объединённый L2 кэш. Эти функции (БПФ одинарной и двойной точности, деление, корень и т.д.) для достижения максимальной параллельной производительности должны использовать общий кэш. Другими словами, потоки этих примитивов должны выполняться на ядрах, расположенных на одном кристалле, с общим кэшем. Для выполнения этого условия перед запуском приложения, которое использует Intel IPP, необходимо выставить следующую переменную окружения:
KMP_AFFINITY=compact
Для процессоров с двумя или более ядрами, расположенными на одном кристалле, это условие выполняется автоматически, и установка данной переменной является лишней. Однако для систем с двумя или более кристаллами (например, процессор Pentium® D на мультисокетной материнской плате), где кэш на каждом кристалле отдельный, не использование данной переменной может привести к существенному снижению производительности данного класса многопоточных примитивов Intel IPP.

Указания к применению

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

При использовании примитивов в распараллеленном приложении, рекомендуется отключать встроенную многопоточность библиотеки Intel IPP с помощью любого из методов, описанных в предыдущем разделе. При этом будет гарантия отсутствия конкуренции встроенного в библиотеку и в приложение механизмов многопоточности, что поможет избежать ситуации oversubscription.

Большинство примитивов библиотеки ускоряют операции над массивами данных, поскольку в библиотеке Intel IPP используются процессорные SIMD инструкции, которые хорошо подходят для векторных операций. Параллельная обработка является естественной для операций над многочисленными элементами данных, которые в основном независимы друг от друга. В общем случае, самым простым способом распараллелить программы с помощью библиотеки является использование декомпозиции данных, или разбиение больших блоков данных на маленькие и обработка этих блоков с помощью нескольких одинаковых параллельных потоков.

Выравнивание памяти и кэша
При работе с данными в больших блоках, неправильное их выравнивание обычно снижает пропускную способность шины. Для решения этой проблемы в библиотеку включён набор функций выделения и выравнивания памяти. К тому же большинство компиляторов можно настроить таким образом, чтобы структуры данных располагались по определённым адресам, что позволяет эффективно использовать шину.

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

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

Самым простым решением является такое расположение этих контрольных структур данных, чтобы они занимали несколько длин линий кэша (обычно 64 байта). Разработчики также могут использовать выравнивающие операторы компиляторов для гарантии того, что эти структуры и массивы таких структур всегда выравниваются по границам линий кэша. Лишние байты, затраченные на выравнивание контрольных структур, многократно окупятся за счёт экономии циклов шины, которые требуются на обновление линии кэша при каждой итерации примитивов.

Конвейерная обработка с помощью DMIP
В идеале, приложения могли бы на ходу адаптироваться и оптимизироваться согласно доступным инструкциям SIMD, количеству аппаратных потоков и размеру высокоскоростного кэша. Оптимальное использование этих трёх ключевых ресурсов может придавать приложению почти идеальное параллельное выполнение, что является целью библиотеки DMIP, которая является частью Intel IPP.

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

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

Узнать больше об этой методике можно в статье "A Landmark in Image Processing: DMIP."

Итоговая производительность при параллельной обработке
Дополнительные инструменты высокого уровня, включённые в библиотеку Intel IPP, позволяют достичь значительного повышения производительности при работе в многоядерной среде. Например, библиотека компрессии данных Intel IPP обеспечивает совместимость с библиотеками таких популярных стандартов сжатия данных без потерь, как ZLIB, BZIP2, GZIP и LZO. Версии Intel IPP библиотек BZIP2 и GZIP используют преимущества многопоточной работы при делении больших файлов GZIP на множество мелких частей для параллельного сжатия, или обработки разных файлов отдельными потоками. Используя этот метод, библиотека GZIP может повысить свою производительность в 10 раз на четырёхядерном процессоре, по сравнению с эквивалентной программой без Intel IPP.

В сфере мультимедиа (например, в обработке видео и изображений), Intel IPP версия кодека H.264 и VC 1 может достигать теоретического предела масштабируемости при использовании нативных потоков для параллелизации декодирования, реконструкции и деблокинга видеофреймов. Работа этого улучшенного Intel IPP декодера H.264 на четырёхядерном процессоре даёт рост производительности от 3 до 4 раз на битовом потоке высокого разрешения.

Дополнительные замечания

Нет единого подхода, гарантирующего наилучшую производительность в каждой ситуации. Улучшение производительности, достигаемое с помощью многопоточности и библиотеки Intel IPP, зависит от природы алгоритма (например, насколько легко он может быть распараллелен), специфики набора используемых в нём примитивов Intel IPP (параллельные с непараллельными, частота использования и т.д.), и аппаратной платформы, на которой он работает (количество ядер, пропускная способность памяти, размер кэша и т.д.).

Для получения подробной информации о возможностях оптимизации компилятора обратитесь к нашему Уведомлению об оптимизации.
Возможность комментирования русскоязычного контента была отключена. Узнать подробнее.