GCC optimization/ru

Это руководство предлагает введение в оптимизацию компилируемого кода используя безопасные, разумные флаги CFLAGS и CXXFLAGS. Оно также описывает теорию оптимизации в общих чертах.

Что такое CFLAGS и CXXFLAGS?
Флаги CFLAGS и CXXFLAGS - это переменные среды, которые используются для сообщения компиляторам GNU Compiler Collection, , какие виды ключей использовать при компиляции исходного кода. CFLAGS используются для кода написанного на C, в то время как флаги CXXLFAGS - для кода написанного на C++.

Они могут быть использованы для уменьшения количества отладочных сообщений программы, увеличения уровня сообщений об ошибках, и, конечно же, оптимизации производимого кода. GNU gcc handbook поддерживает полный список доступных параметров и их предназначений.

Как их использовать?
Флаги CFLAGS и CXXFLAGS могут использоваться двумя путями. Во-первых, они могут использоваться на уровне единственной программы с помощью make-файлов, генерируемых утилитой automake.

Однако, Вы не должны делать этого при установке пакетов, находящихся в дереве портежей. Вместо этого, установите Ваши CFLAGS и CXXFLAGS флаги в. Таким образом все пакеты будут скомпилированы с использованием параметров, которые Вы укажете.

Флаги CFLAGS в /etc/portage/make.conf

Как видите, CXXFLAGS настроены на использование всех параметров, присутствующих в CFLAGS. Это то, что Вам потребуется почти безусловно. Вам вовсе не надобно указывать дополнительные параметры в CXXFLAGS.

Заблуждения
В то время как CFLAGS и CXXFLAGS могут быть очень эффективными средствами генерации менее объемных или более быстрых двоичных файлов из исходного кода, они также могут нарушить функционирование Вашего кода, увеличить его объем, замедлить время исполнения, или вызвать серьезные ошибки компиляции!

CFLAGS - это не панацея; они не смогут автоматически заставить Вашу систему работать быстрее или двоичные файлы занимать меньше места на диске. Добавление все большего и большего количества флагов в попытке оптимизировать (или "разогнать") систему - верный рецепт для неудачи. Существует точка, в которой Вы достигнете худших результатов.

Вопреки хвастовству, которое Вы найдете в Интернете, агрессивные флаги компиляции CFLAGS и CXXFLAGS, скорее всего, принесут больше вреда, чем пользы для Ваших программ. Держите в уме, что причина, по которой флаги существуют с самого начала - это потому что они созданы для использования в определенном месте с определенной целью. Просто потому что один отдельный CFLAG хорош для одного участка кода, вовсе не означает что он подходит для компиляции всего, что Вы можете установить на Ваш компьютер!

Готовы?
Теперь, когда Вы знаете о некоторых рисках, давайте посмотрим на некоторые из разумных, безопасных оптимизаций для Вашего компьютера. Они окажут Вам большую пользу и расположат к Вам разработчиков в следующий раз, когда Вы будете сообщать о проблеме на Bugzilla. (Разработчики обычно просят вас перекомпилировать пакет с минимальным количеством переменных CFLAGS для того, чтобы определить, продолжает ли проблема существовать. Запомните, агрессивные флаги могут разрушить код.)

Основы
Целью использования CFLAGS и CXXFLAGS является создание кода, приспособленного под Вашу систему; он должен отлично функционировать, будучи легковесным и быстрым, если это возможно. Иногда это взаимоисключающие условия, поэтому мы будем придерживаться комбинаций, о которых известно, что они работают хорошо. В идеале, они являются легко доступными на любой архитектуре CPU. Мы упомянем два агрессивных флага позже, так чтобы Вы знали, чего следует остерегаться. Мы не будем обсуждать каждый параметр, перечисленный в руководстве по , но мы опишем основные, наиболее общие флаги.

-march
Самой первым, и наиболее важным параметром, является. Он сообщает компилятору какой код он должен генерировать для Вашего типа процессора architecture (или архитектуры ); он сообщает компилятору, что тот должен генерировать код для определенного типа CPU. Разные типы CPU имеют разные возможности, поддерживают различные наборы команд, и обладают разными способами исполнения кода. Флаг  проинструктирует компилятор генерировать код специально для Вашего типа CPU, со всеми доступными возможностями, особенностями, наборами команд, интересными функциями, и так далее.

Хотя переменная CHOST в и указывает основную используемую архитектуру, параметр   все еще должен использоваться, так чтобы программы были оптимизированы для Вашего конкретного процессора. Процессоры x86 и x86-64 (в числе других) должны использовать флаг.

Какой вид CPU Вы имеете? Чтобы это узнать, введите следующую команду:

Давайте теперь рассмотрим  в действии. Этот пример приведен для более старого чипа Pentium III:

/etc/portage/make.conf: Pentium III

А это другой пример для 64-разрядного AMD CPU:

/etc/portage/make.conf: AMD64

Если Вы все еще не уверены, каким видом CPU Вы обладаете, вы можете просто использовать параметр. Когда используется этот флаг, GCC попытается распознать Ваш процессор и автоматически установит для него подходящие флаги. Однако, Вы не должны его использовать, если Вы собираетесь компилировать пакеты для другого CPU!

Итак, если вы компилируете пакеты на одном компьютере, но собираетесь запускать их на другом (например, используя сборку на быстром компьютере для более медленного, старого компьютера), тогда не используйте. Native означает, что генерируемый код будет запускаться только на том типе CPU, на котором он был собран. Приложения скомпилированные с  на процессоре AMD Athlon 64 CPU не смогут запуститься на более старом VIA C3 CPU.

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

На процессорах x86 и x86-64, параметр  будет генерировать код, предназначенный специально для этих типов процессоров, используя все доступные наборы команд и корректный двоичный интерфейс приложений; он не будет обладать обратной совместимостью с более старыми/другими типами процессоров. Если Вам не требуется исполнять код на чем либо другом, кроме системы, на которой работает Gentoo, продолжайте использовать. Вы должны рассмотреть использование  только тогда, когда Вам необходимо сгенерировать код для более старых процессоров, таких как i386 и i486. Параметр  производит более общий код, чем  ; хотя он и настроит код под определенный процессор, он не будет рассматривать доступные наборы команд и двоичный интерфейс приложений. Не используйте  на системах с x86 или x86-64, так как это не рекомендуется для этих архитектур.

Только не x86/x86-64 процессоры (такие как Sparc, Alpha, и PowerPC) могут потребовать параметры  или   вместо. На этих архитектурах, /   иногда будут вести себя как   (на x86/x86-64). . ., но с другим именем флага. Опять же, поведение  и именование флагов не является единообразным на каждой из архитектур, поэтому удостоверьтесь, что Вы проконсультировались с   manual, для того чтобы определить какой из них Вы должны использовать на своей системе.

-O
Следующая по списку - переменная. Она управляет всем уровнем оптимизации. Это приводит к тому, что компиляция кода занимает больше времени, и сможет занять гораздо больше памяти, особенно когда Вы увеличиваете уровень оптимизации.

Существует пять видов настроек для переменной :  ,   ,   ,   , и. Вы должны использовать только одну из них в.

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

Давайте исследуем каждый уровень оптимизации:


 * : Этот уровень (буква "O" и ноль за ней) отключает оптимизацию полностью и является уровнем по умолчанию, если никакого уровня с префиксом  не указано в переменных CFLAGS или CXXFLAGS. Ваш код не будет оптимизирован; обычно это нежелательно.


 * : Это наиболее простой уровень оптимизации. Компилятор попытается сгенерировать быстрый, занимающий меньше объема код, без затрачивания наибольшего времени компиляции. Он достаточно простой, но должен всегда выполнять свою работу.


 * : Шаг вперед из  . Это рекомендуемый уровень оптимизации, до тех пор пока Вам не понадобится что-то особенное.   активирует несколько дополнительных флагов вдобавок к флагам, активированным на   . С параметром  , компилятор попытается увеличить производительность кода без нарушения размера, и без затрачивания большого количества времени компиляции.


 * : Это наибольший возможный уровень оптимизации, и также самый рискованный. С этим параметром, компиляция Вашего кода займет больше времени, и, фактически, он не должен использоваться глобально с  4.x . Поведение   значительно изменилось с выходом версий 3.x. В версиях 3.x было продемонстрировано, что параметр   приводит к значительно более быстрому времени исполнения по сравнению с  , но это уже не так для   4.x. Компилирование всех Ваших пакетов с параметром   приведет к большим по объему двоичным файлам, и значительно увеличит шансы ошибки компиляции или неожиданного поведения программы (включая ошибки). Недостатки перевешивают преимущества; вспомните закон убывающей предельной полезности. Использование параметра   не рекомендовано для   4.x.


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

Как упомянуто ранее, параметр  - рекомендуемый уровень оптимизации. Если компиляция пакета выдает сообщения об ошибках, убедитесь, что Вы не используете параметр. В качестве выхода, попробуйте установить Ваши переменные CFLAGS и CXXFLAGS на наименьший уровень оптимизации, такой как, или даже   (для сообщения об ошибках и проверки возможных проблем) и перекомпилируйте пакет.

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

-fomit-frame-pointer
Это очень широкоупотребительный флаг, предназначенный для того, чтобы уменьшить размер генерируемого кода. Он включается на всех уровнях с префиксом  (исключая   ) на тех архитектурах, где это не затрудняет отладку (таких как x86-64), но Вам может быть нужно активировать его вручную для добавления к своим флагам. Хотя руководство по GNU  не указывает все архитектуры, на которых он включен с использованием параметра  , Вам будет нужно явно активировать его для x86. Однако, использование этого флага может сделать отладку со сложной до невозможной.

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

-msse, -msse2, -msse3, -mmmx, -m3dnow
Эти флаги разрешают наборы команд SSE, SSE2 , SSE3 , MMX , и 3DNow! для архитектур x86 и x86-64. Они используются в основном в мультимедиа, играх, и других вычислительных задачах с интенсивным использованием плавающей точки, хотя они также включают несколько других математических расширений. Эти наборы команд предоставляются большинством современных процессоров.

Обычно, Вы не нуждаетесь в добавлении каких-либо из этих флагов в пока Вы используете корректный параметр   (например,   подразумевает использование   ). Некоторые заметные исключения - новые процессоры VIA и AMD64, которые поддерживают инструкции, не включаемые параметром  (такие как SSE3). Для таких процессоров, Вам нужно включить дополнительные флаги, где это доступно, после проверки результата работы команды.

Но я получаю лучшую производительность с -funroll-loops -fomg-optimize!
Нет, Вам только кажется что Вы получаете лучшую производительность, потому что кто-то Вас убедил в том, что чем больше флагов, тем лучше. Агрессивные флаги только повредят Вашим приложениям при глобальном использовании. Даже  manual говорит, что использование параметров   и   увеличит объем кода и время его исполнения. Хотя, по каким-то причинам, эти два флага, вместе с флагами ,   ,   , и им подобными, продолжают пользоваться популярностью среди гонщиков, которые хотят повысить чувство собственной важности.

Истина в том, что это чрезвычайно агрессивные флаги. Посмотрите по форумам Gentoo и Bugzilla, чтобы увидеть, что эти флаги могут сделать: ничего хорошего!

Вам не нужно использовать эти флаги глобально, в переменных CFLAGS и CXXFLAGS. Они только ухудшат производительность. Может показаться, что Вы имеете высокопроизводительную систему, работающую по последнему слову техники, но они не делают ничего, кроме раздувания кода и приводят к тому, что Ваши сообщения о багах помечаются как INVALID или WONTFIX.

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

Что по поводу уровней оптимизации -O больших чем 3?
Некоторые пользователи хвалятся даже большей производительностью, достигнутой использованием ,   , и так далее, но в действительности, уровни   большие чем 3 не имеют никакого эффекта. Компилятор может принимать переменные CFLAGS, такие как , но, на самом деле, ничего с ними не делать. Он только выполняет оптимизацию до уровня , и ничего больше:

Нужно больше доказательств? Исследуйте исходный код  :

Исходный код параметра -O

Как видите, любое значение, большее тройки, рассматривается как.

А что об избыточных флагах?
Часто переменные CFLAGS и CXXFLAGS, которые включаются на разных уровнях, указаны избыточно в. Иногда, это сделано по неосведомленности, но также и для того, чтобы избежать отфильтровывание флагов или их замещение.

Фильтрация/замещение флагов используется во многих ebuild-файлах, находящихся в дереве портежей. Обычно, это делается потому что пакеты не компилируются на определенных уровнях, или когда исходный код очень чувствителен к дополнительно используемым флагам. Ebuild-файл или отфильтровывает некоторые или все переменные CFLAGS и CXXFLAGS, или может заменить  другим уровнем.

Руководство разработчика Gentoo описывает в общих чертах, где и как работает фильтрация/замещение флагов.

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

Указание избыточных CFLAGS

Однако, это не самая умная вещь, которую можно сделать. CFLAGS отфильтровываются не зря! Когда флаги фильтруются, это означает, что собирать пакет с этими флагами небезопасно. Очевидно, что небезопасно компилировать всю Вашу систему с  если некоторые из флагов, включенных на этом уровне, вызовут проблемы с определенными пакетами. Следовательно, Вы не должны пытаться обхитрить разработчиков, которые поддерживают эти пакеты. Доверяйте разработчикам. Фильтрация флагов и их замена делаются для вашей же пользы! Если ebuild-файл указывает альтернативные флаги, то не пытайтесь это обойти.

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

What about LDFLAGS?
The Gentoo developers have already set basic, safe LDFLAGS in the base profiles, so you don't need to change them.

Can I use per-package flags?
Information on how to use per-package environment variables (including CFLAGS) is described in the Gentoo Handbook, "Per-Package Environment Variables".

Resources
The following resources are of some help in further understanding optimization:


 * The GNU gcc manual


 * Chapter 5 of the Gentoo Installation Handbooks




 * Wikipedia


 * The Gentoo Forums

Acknowledgements
We would like to thank the following authors and editors for their contributions to this guide:


 * nightmorph