Подготовка к портежам-2. Вопросы оптимизации
Алексей Федорчук
alv@linux-online.ru
Переменная USE - эффективный способ настройки Gentoo под задачи
пользователя. Однако редактированием файла /etc/make.conf можно добиться
большего - задать глобально условия компиляции. То есть, по простому говоря,
те флаги компилятора gcc, которые при ручной сборке программ обычно задаются
в командной строке утилиты make.
Рассмотрение флагов компилятора gcc само по себе далеко выходит за рамки
этой заметки (см. библиографию). Наша сегодняшняя цель сугубо
практическая - добиться наибольшей оптимизации как базовой системы, так и
всех компилируемых приложений под конкретное железо, благо текущая (3.2.x)
версия gcc позволяет сделать это для любых процессоров.
Итак, возвращаемся к файлу /etc/make.conf, в котором видим три строки,
описывающие флаги компиляции. По умолчанию, в свежеустановленной из
тарбаллов, системе они имеют вид вроде:
CHOST='i686-pc-linux-gnu'
CFLAGS='-march=pentium4 -mcpu=pentium4 -O3 -pipe'
CXXFLAGS='-march=pentium4 -mcpu=pentium4 -O3 -pipe'
Первая строка определяет архитектуру хост-машины, то есть компьютера, на
котором компиляция выполняется. А поскольку, если иное не указано явным
образом, тип целевой машины принимается совпадающим с хостом, то
характеризует и компьютер, для которого компилируется система. Исключение -
случаи кросс-платформенной компиляции, тогда и host, и target нужно
определять отдельно. С типом все просто - для всех процессоров от PentiumPro
и выше (включая Athlon'ы разного рода и Pentium4) строка по умолчанию
отражает действительность. Для Pentium просто здесь следовало бы
поставить
CHOST='i586-pc-linux-gnu'
и так далее, но собирать на таких машинах Gentoo - занятие весьма
скучное.
А следующие две строки и содержат флаги, определяющие условия оптимизации
для неких процессоров. Верхняя из них действенна для сборки исходников на Си
просто, нижняя - для Си++.
Рассмотрим эти флаги в обратном обычному порядке (справа налево). Флаг
-pipe предписывает использовать при компиляции т.н. программные каналы вместо
временных файлов для передачи данных между разными стадиями компиляции.
Насколько я понимаю, она не влияет на быстродействие генерируемого кода, но
способствует ускорению самого процесса компиляции. И, следовательно, не
лишней не будет.
Флаг -O# задает собственно уровень оптимизации - от самого низшего, O (или
O1, что идентично) до высшего - O3 (уровень O0 означает отсутствие всякой
оптимизации). Собственно, каждый из этих флагов - некая совокупность опций,
способствующих быстродействию сгенерированного кода, но для нас это не
существенно.
Как можно видеть, по умолчанию в Gentoo предписывается самый высокий
уровень оптимизации (O3). С чем трудно не согласиться - какой же русский не
любит быстрой езды. Правда, у быстрой езды есть оборотная сторона -
теоретически рассуждая, не каждое приложение (особенно тех, код которых в
основном написан достаточно давно) обязано собираться при высшей степени
оптимизации.
Не будучи программером, не могу сказать, каким образом решается эта
проблема в Gentoo. Но факт остается фактом: абсолютно все требующиеся мне
программы собирались благополучно при флаге -O3. Типичный пример - оконная
система X. Сколько я ни пытался собирать вручную первозданный (с http://www.xfree86.org) ее исходник с
флагом -O3 - рано или поздно, но регулярно получал сообщение об ошибке. А из
Gentoo-портежей - компилируется без сучка, без задоринки, и при еще более
жестких условиях оптимизации. Не для того ли существуют Gentoo-специфичные
патчи? Буду признателен за разъяснение от профессионалов.
Так что смело оставляем умолчальный флаг -O3 и переходим к двум
следующим. Смысл их близок и сводится к указанию конкретного процессора, под
который производится сборка. В качестве значения обоих флагов могут выступать
(в gcc 3.2 и выше, и не выходя за пределы PC-архитектуры) абсолютно все
Intel-совместимые процессоры, от i386, K6, Duron до Athlon-XP, -MP и
Pentium4. Полный список поддерживаемых камней можно посмотреть в комментариях
к файлу /etc/make.conf или, еще лучше - на man (1) gcc.
В чем различие флагов -march и -mcpu? Первый генерирует код с учетом
особенностей указанного процессора, без возможности запуска на более младших
моделях. Второй делает то же самое, но сохраняет совместимость с братьями
меньшими. То есть при сборке системы на настольной машине и для нее же
представляется избыточным. Так что в реальности можно ограничиться указанием
лишь -march=модель.
В принципе, этими тремя флагами можно и ограничиться. Однако на freehackers.org можно обнаружить
страничку, специально посвященную флагам оптимизации для дистрибутива Gentoo
(к слову замечу, что указанный сайт функционирует под управлением именно
этого дистрибутива). Так вот, там в числе безопасных (safe) приведен, причем
для всех типов процессоров, еще один флаг - -fomit-frame-pointer. Не возьмусь
объяснять точное его значение, но из классического описания Ричарда Столлмана
(см. библиографию) можно понять, что его наличие способствует
исключению некоторых необязательных команд и таким образом также способствует
быстродействию. Так что, вероятно, пренебрегать им не следует.
В итоге строки с gcc=флагами оптимизации для Pentiun4 примут следующий
вид:
CFLAGS="-march=pentium4 -O3 -pipe -fomit-frame-pointer"
CXXFLAGS="-march=pentium4 -O3 -pipe -fomit-frame-pointer"
Каковой и можно принять как оптимальный в обычных условиях. В случае
реликтового (по цене, но не по производительности) Pentium-III, значение
-march= следует изменить на pentium3, для Athlon разных видов - на athlon
(athlon-tbird, athlon-xp, athlon-mp), и так далее.
На том же freehackers.org можно
обнаружить предложения и более жесткой оптимизации, а именно - указание
флагов -ffast-math (оптимизация с нарушением некоторых правил ANSI и IEEE),
-fforce-addr, -funroll-loops и некоторые другие. За отсутствием собственного
опыта, не могу ничего сказать об их действенности. Но то, что они не отнесены
к safe-флагам, говорит, что использовать их следует осторожно. И не особо
удивляться, получив сообщение об ошибке после пары часов сборки какого-либо
тяжелого приложения.
Библиография вопроса
Прежде чем приступать к оптимизации, очень не вредно почитать вводную
часть классического труда Ричарда Столлмана сотоварищи "Компилятор GNU CC",
русский перевод которого можно найти на многих сайтах с подборками
документации, например, на Linux.Yaroslavl. А конкретные
рекомендации применительно к сборке Gentoo-портежей есть, как уже было
сказано, на двух страницах freehackers.org, первая из которых
посвящена безопасным
флагам, а вторая - сильно
оптимизирующим.
Спасибо участнику Gentoo-форума universal'у за приведенные выше ссылки.
Буду также благодарен за все примеры конкретного применения всяких сильно
оптимизирующих флагов.