GCC optimization/ru

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

Что такое CFLAGS и CXXFLAGS?
Переменные CFLAGS и CXXFLAGS входят в число переменных среды, традиционно используемых для указания системе сборки параметров компилятора при сборке кода C и C++. Хотя эти переменные не стандартизованы, они используются повсеместно и любой грамотно написанный сборочный файл должен их понимать для того, чтобы иметь возможность передавать дополнительные или индивидуальные параметры компилятору при его запуске. Info-страница GNU make содержит перечень некоторых наиболее широко используемых переменных из этой категории.

Because such a large proportion of the packages that make up most Gentoo systems are written in C and C++, these are two variables administrators will definitely want to set correctly as they will greatly influence the way much of the system is built.

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

Как их использовать?
Normally, CFLAGS and CXXFLAGS would be set in the environment when invoking a configure script or with makefiles generated by the program. In Gentoo-based systems, set the CFLAGS and CXXFLAGS variables in. Variables set in this file will be exported to the environment of programs invoked by portage such that all packages will be compiled using these options as a base.

As seen in the example above the CXXFLAGS variable is set to use all the options present in CFLAGS. Most every system should be configured in this manner. Additional options for CXXFLAGS are less common and don't usually apply generally enough to deserve setting them globally.

Заблуждения
While compiler optimizations enabled by various CFLAGS can be an effective means of producing smaller and/or faster binaries, they can also impair the function of the code, bloat its size, slow down its execution time, or simply cause a build failure. The point of diminishing performance returns is reached rather quickly when dealing with CFLAGS. Don't set them arbitrarily.

Remember, the global CFLAGS configured in will be applied to every package on the system so administrators typically only set general, widely-applicable options. Individual packages further modify these options either in the ebuild or the build system itself to generate the final set of flags used when invoking the compiler.

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

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

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

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

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

Чтобы получить более детальную информацию, включая значения  и   можно использовать две команды.


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


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

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

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

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

If compiling packages on one computer in order to run them on a different computer (such as when using a fast computer to build for an older, slower machine), then do not use. "Native" means that the code produced will run only on that type of CPU. The applications built with  on an AMD Athlon 64 CPU will not be able to run on an old VIA C3 CPU.

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

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

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

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

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

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

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


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


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


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


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


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


 * : В GCC 4.8 был введен новый общий уровень оптимизации -Og. Он удовлетворяет потребность в быстрой компиляции и имеет превосходные возможности для отладки, обеспечивая при этом приемлемый уровень производительности во время выполнения. Общий опыт разработки должен быть лучше, чем с уровнем оптимизации по умолчанию -O0. Обратите внимание, что -Og не означает -g, он просто отключает оптимизацию кода, которая может помешать отладке.


 * : Новое в GCC 4.7, состоит из  плюс ,  , и  . Этот параметр нарушает строгое соответствие стандарту, и не рекомендуется для использования.

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

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

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

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

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

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

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

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

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

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

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

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

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

Что насчет компиляции не на целевой машине?
Некоторые читатели могут спросить, не приведет ли компиляция не на целевой машине, сильно отличающейся архитектурой процессора или структурой GCC, к плохому качеству оптимизации (по сравнению с нативной компиляцией). Ответ прост: Нет. Независимо от используемого оборудования, на котором проводится компиляция, и от значения переменной CHOST, с использованием которой был собран GCC, если используются те же аргументы (кроме ) и та же версия GCC (хотя небольшие оптимизации могут отличаться), результирующий уровень оптимизации останется строго тем же.

Например, если Gentoo установлен на компьютере, на котором переменная CHOST для GCC равна i686-pc-linux-gnu, а сервер Distcc настроен на другом компьютере, на котором CHOST для GCC равна i486-linux-gnu, то не нужно бояться, что результаты будут менее оптимальны из-за отличающейся архитектуры удаленного компилятора и/или оборудования. Результат будет оптимизирован в той же степени, как и при сборке на целевом компьютере, если, конечно, обоим компиляторам передаются одинаковые параметры (и параметр  не включает значение  ). В данном конкретном случае целевую архитектуру нужно явно определять, как указано в статье Distcc и -march=native.

Единственная разница в поведении между двумя версиями GCC, построенными с использованием разных архитектур в значении параметра  по умолчанию. Он берется из переменной CHOST для GCC, если он не указан явно в командной строке.

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

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

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

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

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

Building packages with unacceptable flags will most likely lead to problems. When reporting problems on Bugzilla, the flags that are used in will be readily visible and developers will ask to recompile without those flags. Save the trouble of recompiling by not using redundant flags in the first place! Don't just automatically assume to be more knowledgeable than the developers.

Что по поводу LDFLAGS?
Разработчики Gentoo уже установили простые, безопасные LDFLAGS в базовых профилях, поэтому не нужно их изменять.

Могу ли я использовать флаги для отдельных пакетов?
Информация об использовании переменных среды для каждого пакета по отдельности (включая CFLAGS ) описана в настольной книге Gentoo, "Переменное окружение для отдельных пакетов".

Источники
Следующие источники могут быть полезными в дальнейшем изучении оптимизации:


 * Онлайн-документация GCC


 * Gentoo Handbook - настройка параметров компиляции


 * man make.conf


 * Wikipedia


 * форумы Gentoo