GCC optimization/ru

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

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

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

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

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

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

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

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

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

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

Основы
The goal behind CFLAGS and CXXFLAGS is to create code tailor-made to your system; it should function perfectly while being lean and fast, if possible. Sometimes these conditions are mutually exclusive, so this guide will stick to combinations known to work well. Ideally, they are the best available for any CPU architecture. For informational purposes, aggressive flag use will be covered later. Not every option listed on the GCC manual (there are hundreds) will be discussed, but basic, most common flags will be reviewed.

-march
The first and most important option is. This tells the compiler what code it should produce for the system's processor architecture (or arch); it tells GCC that it should produce code for a certain kind of CPU. Different CPUs have different capabilities, support different instruction sets, and have different ways of executing code. The  flag will instruct the compiler to produce specific code for the system's CPU, with all its capabilities, features, instruction sets, quirks, and so on.

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

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

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

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

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

If the type of CPU is undetermined, or if the user does not know what setting to choose, it is possible use the  setting. When this flag is used, GCC will attempt to detect the processor and automatically set appropriate flags for it. However, this should not be used when intending to compile packages for different CPUs!

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

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

On x86 and x86-64 CPUs,  will generate code specifically for that CPU using its available instruction sets and the correct ABI; it will have no backwards compatibility for older/different CPUs. Consider using  when generating code for older CPUs such as i386 and i486. produces more generic code than ; though it will tune code for a certain CPU, it does not take into account available instruction sets and ABI. Do not use  on x86 or x86-64 systems, as it is deprecated for those arches.

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

-O
Next up is the  variable. This variable controls the overall level of optimization. Changing this value will make the code compilation take more time and will use much more memory, especially as the level of optimization is increased.

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

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

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


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


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


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


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


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


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


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

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

-pipe
A common flag is. This flag has no effect on the generated code, but it makes the compilation process faster. It tells the compiler to use pipes instead of temporary files during the different stages of compilation, which uses more memory. On systems with low memory, GCC might get killed. In those cases do not use this flag.

-fomit-frame-pointer
This is a very common flag designed to reduce generated code size. It is turned on at all levels of  (except  ) on architectures where doing so does not interfere with debugging (such as x86-64), but it may need to activated. In that case add it to the flags. Though the GCC manual does not specify all architectures, it is turned on by using the  option. It's still necessary to explicitly enable the  option, to activate it on x86-32 with GCC up to version 4.6, or when using   on x86-32 with any version of GCC. However, using  will make debugging hard or impossible.

In particular, it makes troubleshooting applications written in Java much harder, though Java is not the only code affected by using this flag. So while the flag can help, it also makes debugging harder; backtraces in particular will be useless. When not doing software debugging and no other debugging-related CFLAGS such as  have been used, then try using.

-msse, -msse2, -msse3, -mmmx, -m3dnow
These flags enable the Streaming SIMD Extentions (SSE), SSE2, SSE3, MMX, and 3DNow! instruction sets for x86 and x86-64 architectures. These are useful primarily in multimedia, gaming, and other floating point-intensive computing tasks, though they also contain several other mathematical enhancements. These instruction sets are found in more modern CPUs.

Normally none of these flags need to be added to, as long as the system is using the correct  (for example,   implies  ). Some notable exceptions are newer VIA and AMD64 CPUs that support instructions not implied by  (such as SSE3). For CPUs like these additional flags will need to be enabled where appropriate after checking.

Но я получаю лучшую производительность с -funroll-loops -fomg-optimize!
No, you only think you do because someone has convinced you that more flags are better. Aggressive flags will only hurt applications when used system-wide. Even the GCC manual says that using  and   will make code larger and run more slowly. Yet for some reason, these two flags, along with,  ,  , and similar flags, continue to be very popular among ricers who want the biggest bragging rights.

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

You do not need to use those flags globally in CFLAGS or CXXFLAGS. They will only hurt performance. They may make you sound like you have a high-performance system running on the bleeding edge, but they don't do anything but bloat the code and get your bugs marked INVALID or WONTFIX.

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

Что по поводу уровней оптимизации -O больших чем 3?
Some users boast about even better performance obtained by using,  , and so on, but the reality is that   levels higher than 3 have no effect. The compiler may accept CFLAGS like, but it actually doesn't do anything with them. It only performs the optimizations for, nothing more.

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

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

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

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

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

А что об избыточных флагах?
Oftentimes CFLAGS and CXXFLAGS that are turned on at various  levels are specified redundantly in. Sometimes this is done out of ignorance, but it is also done to avoid flag filtering or flag replacing.

Flag filtering/replacing is done in many of the ebuilds in the Portage tree. It is usually done because packages fail to compile at certain  levels, or when the source code is too sensitive for any additional flags to be used. The ebuild will either filter out some or all CFLAGS and CXXFLAGS, or it may replace  with a different level.

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

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

However, this is not a smart thing to do. CFLAGS are filtered for a reason! When flags are filtered, it means that it is unsafe to build a package with those flags. Clearly, it is not safe to compile your whole system with  if some of the flags turned on by that level will cause problems with certain packages. Therefore, you shouldn't try to "outsmart" the developers who maintain those packages. Trust the developers. Flag filtering and replacing is done for your benefit! If an ebuild specifies alternative flags, then don't try to get around it.

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

Что по поводу LDFLAGS?
The Gentoo developers have already set basic, safe LDFLAGS in the base profiles, so they do not need to be changed.

Могу ли я использовать флаги для отдельных пакетов?
Information on how to use per-package environment variables (including CFLAGS ) is described in the Gentoo Handbook, "Per-Package Environment Variables".

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


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


 * Глава 5 настольной книги по установке Gentoo


 * man make.conf


 * Wikipedia


 * форумы Gentoo