Библиотека сайта rus-linux.net
Внутренние функции компилятора GCC для обработки данных в векторной форме
Авторы: George Koharchik, Kathy Jones, Перевод: А.Панин
Это окончание статьи. Вернуться к началу.
Перейти к предыдущей части.
Советы относительно разработки кода для работы с векторами
Изучите все компромиссы
При разработке кода с использованием внутренних функций компилятора вам придется пойти на компромиссы. Ваша программа будет поддерживать баланс между скалярными и векторными операциями. Достаточно ли задач для решения их при помощи аппаратного обеспечения для обработки векторов, делающих использование векторов имеющим смысл? Вы должны соблюдать баланс между переносимостью кода на языке C и требованиями к скорости исполнения в сочетании со сложностью кода обработки векторов, особенно в том случае, когда вам необходимо поддерживать варианты скалярного и векторного кода одновременно. Вам нужно рассудить, что более необходимо: высокая скорость исполнения или точность. Может быть и так, что целочисленные математические операции будут исполняться с необходимой скоростью, причем точность расчетов будет соответствовать требованиям. Единственной возможностью для вынесения решения по этим вопросам является тестирование: разработайте программу с обработкой данных в скалярной и векторной форме и сравните.
Структуры данных
Начните с составления структур данных с учетом того, что будут использоваться внутренние функции компилятора. Это означает, что данные должны быть выровнены. Если вы можете сгруппировать данные для формирования однородных векторов, сделайте это.
Разработайте переносимый скалярный код и займитесь профилированием
После этого разработайте переносимый скалярный код и приступите к профилированию. Основываясь на этом коде, вы можете узнать скорость выполнения и получить корректные результаты для сравнения с векторным кодом. Профилирование кода позволит узнать участки скалярного кода, на которых снижается скорость исполнения. На этих участках для повышения скорости работы и должны быть применены векторные операции.
Разработайте код для обработки векторов
Во время разработки кода для работы с векторами, разделите файлы с непереносимым исходным кодом на основании используемой архитектуры. Создайте отдельные правила сборки в виде файлов Makefile для каждой архитектуры. Это позволяет без лишних сложностей выбрать файлы, которые необходимо скомпилировать и передать необходимые аргументы компилятору для каждой архитектуры. По возможности минимизируйте смешивание скалярного и векторного кода.
Используйте специальные макросы в директивах #ifdef
- __MMX__ - X86 MMX
- __SSE__ - X86 SSE
- __SSE2__ - X86 SSE2
- __VEC__ - Функции Altivec
- __ARM_NEON__ - Функции Neon
touch emptyfile.c gcc -E -dD emptyfile.c | more
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 предоставляет в распоряжение разработчикам внутренние функции, позволяющие получить контроль над большим количеством возможностей процессора без перехода к работе с ассемблером. Мы рассмотрели базовые типы данных и некоторые математические функции для обработки векторов. Когда вы используете внутренние функции компилятора, убедитесь, что вы протестировали приложение в полном объеме. Тестируйте скорость исполнения и правильность работы приложения с кодом обработки векторов, взяв за основу скалярный код. Различные возможности каждого из процессоров и то, насколько хорошо они работают предполагают обширное поле для экспериментов. Чем больше усилий вы вкладываете в разработку, тем больше возможностей вы получите на выходе.
Справочные материалы
- http://gcc.gnu.org/onlinedocs/gcc/Target-Builtins.html
- http://ds9a.nl/gcc-simd/
- http://softpixel.com/~cwright/programming/simd/index.php
- http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dht0002a/BABCJFDG.html
- http://www.arm.com/products/processors/technologies/neon.php
- http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dht0002a/ch01s04s02.html
- http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0205j/BABGHIFH.html
- http://www.tommesani.com/Docs.html
- http://www.linuxjournal.com/article/7269
- http://developer.apple.com/hardwaredrivers/ve/sse.html
- http://en.wikipedia.org/wiki/Multiplication_algorithm#Shift_and_add
- http://www.ibm.com/developerworks/power/library/pa-unrollav1/
- http://en.wikipedia.org/wiki/MMX_(instruction_set)
Это окончание статьи. Вернуться к началу. Перейти к предыдущей части.