Наши партнеры

UnixForum





Библиотека сайта rus-linux.net

Внутренние функции компилятора GCC для обработки данных в векторной форме

Авторы: George Koharchik, Kathy Jones, Перевод: А.Панин
Это окончание статьи. Вернуться к началу. Перейти к предыдущей части.

Советы относительно разработки кода для работы с векторами

Изучите все компромиссы

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

Структуры данных

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

Разработайте переносимый скалярный код и займитесь профилированием

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

Разработайте код для обработки векторов

Во время разработки кода для работы с векторами, разделите файлы с непереносимым исходным кодом на основании используемой архитектуры. Создайте отдельные правила сборки в виде файлов Makefile для каждой архитектуры. Это позволяет без лишних сложностей выбрать файлы, которые необходимо скомпилировать и передать необходимые аргументы компилятору для каждой архитектуры. По возможности минимизируйте смешивание скалярного и векторного кода.

Используйте специальные макросы в директивах #ifdef

В файлах исходного кода, используемых на более чем одной архитектуре и при этом имеющих архитектурно-зависимые части, вы можете использовать директиву #ifdef со специальными макросами компилятора, указывающими на применение SIMD-инструкций. Допустимые макросы:
  • __MMX__ - X86 MMX
  • __SSE__ - X86 SSE
  • __SSE2__ - X86 SSE2
  • __VEC__ - Функции Altivec
  • __ARM_NEON__ - Функции Neon
Для того, чтобы увидеть основные макросы, заданные для других процессоров, используйте команды:
touch emptyfile.c
gcc -E -dD emptyfile.c | more
Для того, чтобы увидеть что добавляется при использовании опций командной строки компилятора для активизации SIMD-инструкций (см. Таблицу 1), можно использовать следующие команды:
touch emptyfile.c
gcc -E -dD emptyfile.c -mmmx -msse  -msse2 -mfpmath=sse | more

После этого сравните результаты.

Проверяйте тип процессора во время исполнения

Далее, ваш код должен во время исполнения установить, имеет ли ваш процессор поддержку векторных операций. Если в вашей программе не предусмотрено кода обработки векторов, совместимого с данным процессором, необходимо перейти к исполнению скалярного кода. Если же поддержка векторных операций имеется и они будут произведены быстрее, нужно перейти к коду обработки векторов. Список возможностей процессора на архитектуре X86 можно получить при помощи инструкции cpuid из заголовочного файла<cpuid.h>. (Вы видели примеры этой операции в файлах примеров, расположенных в samples/simple/x86/*.c.) Мы не смогли найти что-либо подобное для технологий Altivec и Neon, поэтому в примерах проводится исследование файла /proc/cpuinfo. (В коде приложений может производиться тестирование наличия инструкций SIMD. Если процессор генерирует сигнал SIGILL при получении тестовой инструкции, у вас не будет возможности использовать ее.)

Тестирование, тестирование, тестирование

Тестируйте все. Тестируйте время исполнения: обратите внимание на то, скалярный или векторный код исполняется быстрее. Тестируйте корректность получаемых результатов: сравните результаты после выполнения векторного кода с результатами, полученными после выполнения скалярного кода. Тестируйте различные уровни оптимизации: поведение программы может меняться в зависимости от уровней оптимизации. Тестируйте версии кода в которых производятся различные целочисленные математические действия. Наконец, следите за ошибками компилятора. Поддержка внутренних функций и SIMD-инструкций в компиляторе GCC находится в процессе разработки.

Рассмотрев эти аспекты, перейдем к последнему примеру кода. В директории samples/colorconv2 расположен исходный код библиотеки для преобразования цветового пространства, которая принимает изображения в формате non-planar YUV422 и преобразовывает цвета изображения в формат RGBA. Она работает на системах с процессорами PowerPC, используя технологию Altivec; ARM Cortex-A, используя Neon; и X86, используя MMX, SSE и SSE2. (Мы тестировали ее на PowerMac G5 под управлением Fedora 12, на Beagleboard под управлением Angstrom 2009.X-test-20090508 и на Pentium 3 под управлением Fedora 10.) Colorconv определяет возможности процессора и использует код для их эксплуатации. Предусмотрена возможность выполнения скалярного кода в том случае, когда функции обработки векторов не поддерживаются процессором.

Для сборки примеров распакуйте tar-архив и используйте команду make. Make использует команду "uname" для получения названия архитектуры и использования соответствующего файла Makefile. (К сожалению, вызов uname в Angstrom на Beagleboard возвращает "unknown", отсюда следует аналогичное название директории.)

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

В заключение приведем несколько советов для достижения максимальной производительности приложений.

Во-первых, используйте последнюю версию компилятора в сочетании с параметрами компилятора для генерации наиболее быстрого кода. (Обратитесь к странице руководства вашего компилятора для того, чтобы узнать больше о таких функциях, как -mcpu.)

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

В третьих, выполняйте максимально возможное количество работы в рамках каждой векторной операции с векторами, размеры типов данных которых максимально подходят к данным, которые необходимо обрабатывать. Выполняйте максимальный объем работы в каждый момент времени, получая достаточное количество данных и поддерживая аппаратное обеспечение для обработки векторов постоянно в рабочем состоянии. Используйте большие фрагменты данных. Если ваше аппаратное обеспечение может обрабатывать несколько векторов одновременно, используйте несколько векторов. Однако, превышение доступного количества регистров векторов может замедлить работу приложения. (Обратитесь к документации вашего процессора.)

В четвертых, не изобретайте колесо заново. Компании Intel, Freescale и ARM предоставляют библиотеки и примеры исходного кода для получения максимальной отдачи от произведенных ими процессоров. Эти программные компоненты включают в себя Integrated Performance Primitives от Intel, libmotovec от Freescale и OpenMAX от ARM.

Заключение

В заключение следует отметить, что компилятор GCC предоставляет в распоряжение разработчикам внутренние функции, позволяющие получить контроль над большим количеством возможностей процессора без перехода к работе с ассемблером. Мы рассмотрели базовые типы данных и некоторые математические функции для обработки векторов. Когда вы используете внутренние функции компилятора, убедитесь, что вы протестировали приложение в полном объеме. Тестируйте скорость исполнения и правильность работы приложения с кодом обработки векторов, взяв за основу скалярный код. Различные возможности каждого из процессоров и то, насколько хорошо они работают предполагают обширное поле для экспериментов. Чем больше усилий вы вкладываете в разработку, тем больше возможностей вы получите на выходе.

Справочные материалы

Заголовочные файлы компилятора GCC с прототипами встроенных функций (например, arm_neon.h) и информационные страницы GCC, описывающие эти функции:
RealView Compilation Tools Compiler Reference Guide (especially Appendix E)
RealView Compilation Tools Assembler Guide (esp chapter 5)

Это окончание статьи. Вернуться к началу. Перейти к предыдущей части.