Оптимизации GCC
Это руководство предлагает введение в оптимизацию компилируемого кода используя безопасные, разумные флаги CFLAGS и CXXFLAGS. Оно также описывает теорию оптимизации в общих чертах.
В Gentoo значения по умолчания для CFLAGS могут быть заданы в make.conf. CFLAGS также может быть переопределен для каждого пакета индивидуально.
За более подробной информацией обращайтесь к статьям CFLAGS и CXXFLAGS в Руководстве Gentoo и Безопасные CFLAGS. См. также FAQ.
Введение
Что такое CFLAGS и CXXFLAGS?
Переменные CFLAGS и CXXFLAGS входят в число переменных окружения, традиционно используемых для указания системе сборки параметров компилятора при сборке кода C и C++. Хотя эти переменные не стандартизованы, они используются повсеместно и любой грамотно написанный сборочный файл должен их понимать для того чтобы иметь возможность передавать дополнительные или индивидуальные параметры компилятору при его запуске. Info-страница GNU make содержит перечень некоторых наиболее широко используемых переменных из этой категории.
Поскольку большое количество пакетов, составляющих большинство систем Gentoo, написано на C и C++, администраторам следует правильно установить эти две переменные, так как они оказывают большое влияние на сборку системы.
Они могут быть использованы для уменьшения количества отладочных сообщений программы, увеличения уровня сообщений об ошибках, и, конечно же, оптимизации производимого кода. GCC manual поддерживает полный список доступных параметров и их предназначений.
Как они используются?
Как правило, переменные CFLAGS и CXXFLAGS устанавливаются в окружении при вызове скрипта configure или с помощью файла make-файлов, созданных программой automake. В системах на базе Gentoo эти переменные устанавливаются в файле /etc/portage/make.conf. Переменные, установленные в этом файле, экспортируются в окружение программ, запускаемых portage — таким образом, все пакеты собираются, используя эти параметры в качестве основы.
CFLAGS="-march=skylake -O2 -pipe"
CXXFLAGS="${CFLAGS}"
Хоть и можно записать USE-флаги в несколько строк, запись в несколько строк переменной CFLAGS может привести к ошибкам в некоторых программах, например, в cmake. Убедитесь, что CFLAGS записаны в одну строку и с короткими пробелам, во избежании лишних проблем. Посмотрите bug #500034 например.
Как видно в примере выше, переменная CXXFLAGS установлена таким образом, что она использует все параметры, установленные в переменной CFLAGS. Почти все системы следует настраивать таким образом. Дополнительные параметры для переменной CXXFLAGS менее распространены и, как правило, используются недостаточно широко для того, чтобы имело смысл устанавливать их глобально.
Статья Safe CFLAGS поможет новичкам начать оптимизацию их систем.
Заблуждения
Хотя оптимизации компилятора, включенные с помощью различных параметров переменной CFLAGS, могут являться эффективным средством для создания меньших по размеру и/или более быстрых исполняемых файлов, они также могут ухудшить функциональность кода, увеличить его размер, замедлить его выполнение или привести к ошибке сборки. Неправильное использование переменной CFLAGS может довольно быстро привести к ухудшению производительности. Не устанавливайте эти параметры произвольно.
Параметры глобальной переменной CFLAGS, установленной в файле /etc/portage/make.conf, применяются ко всем пакетам в системе, поэтому обычно администраторы устанавливают в этой переменной только наиболее общие, широко используемые параметры. Отдельные пакеты могут изменять эти параметры либо в файле ebuild либо в самой системе сборки, создавая окончательный набор флагов, подаваемых компилятору.
Готовы?
Теперь, зная о некоторых рисках, давайте посмотрим на некоторые из разумных, безопасных оптимизаций. Они окажут большую пользу и расположат разработчиков в следующий раз, когда будете сообщать о проблеме на Bugzilla. (Разработчики обычно просят пользователей перекомпилировать пакет с минимальным количеством переменных CFLAGS для того, чтобы определить, продолжает ли проблема существовать. Запомните: агрессивные флаги могут разрушить код!)
Оптимизация
Основы
Целью использования CFLAGS и CXXFLAGS является создание кода, приспособленного под систему; он должен отлично функционировать, будучи легковесным и быстрым, если это возможно. Иногда это взаимоисключающие условия, поэтому это руководство будет придерживаться комбинаций, о которых известно, что они работают хорошо. В идеале, они являются легко доступными на любой архитектуре CPU. Для ознакомления агрессивное использование флага будет рассмотрено позднее. Не будет описываться каждый параметр из руководства GCC (их очень много), но опишем основные, наиболее часто используемые флаги.
Если не уверены, что делает флаг, просмотрите соответствующую главу GCC руководства. Если после просмотра руководства вы все ещё не уверены, то попробуйте поискать в интернете, или в списках рассылок GCC.
-march
Самым первым и наиболее важным параметром является -march
. Он сообщает компилятору, какой код генерировать для архитектуры процессора; он сообщает GCC, что тот должен генерировать код для определенного типа CPU. Разные типы CPU имеют разные возможности, поддерживают различные наборы команд и обладают разными способами исполнения кода. Флаг -march
проинструктирует компилятор генерировать специфичный код для архитектуры CPU, со всеми доступными возможностями, особенностями, наборами команд, интересными функциями и так далее, при условии, что исходный код их поддерживает. К примеру, для оптимизации с помощью инструкций AVX, исходный код должен быть адаптирован для их поддержки.
-march=
— параметр выбора архитектуры набора команд (ISA); он сообщает компилятору, что тот может использовать инструкции из ISA. На платформе Intel/AMD64 с -march=native -O2
или более низким уровнем оптимизации код будет (вероятнее всего) использовать инструкции AVX, но с более короткими регистрами SSE XMM. Чтобы полностью использовать преимущества регистров AVX YMM, следует использовать -ftree-vectorize
, -O3
или -Ofast
[1].
-ftree-vectorize
— параметр оптимизации (по умолчанию при -O3
и -Ofast
), который по возможности пытается векторизовать циклы с помощью выбранной ISA. Причина, по которой она ранее не включалась при -O2
— она не всегда улучшает код, может также сделать его медленнее, и обычно делает код больше; зависит от конкретного цикла и т. д. Начиная с GCC 12, эта оптимизация включена в самой незатратной модели (-fvect-cost-model=very-cheap
), дабы достичь баланса между размером кода и скоростью выполнения. Затратная модель может быть указана с помощью -fvect-cost-model
.
Хотя переменная CHOST в /etc/portage/make.conf и указывает основную используемую архитектуру, параметр -march
все еще должен использоваться, так чтобы программы были оптимизированы для конкретного процессора. Процессоры x86 и x86-64 (в числе других) должны использовать флаг -march
.
Какой вид CPU имеется в системе? Чтобы это узнать, введите следующую команду:
user $
cat /proc/cpuinfo
или установите app-portage/cpuid2cpuflags и добавьте специфичные для вашего CPU опции в файл /etc/portage/package.use/00cpuflags, что программа делает через, к примеру, переменную CPU_FLAGS_*:
user $
cpuid2cpuflags
CPU_FLAGS_X86: aes avx avx2 f16c fma3 mmx mmxext pclmul popcnt sha sse sse2 sse3 sse4_1 sse4_2 sse4a ssse3
root #
echo "*/* $(cpuid2cpuflags)" > /etc/portage/package.use/00cpu-flags
Чтобы получить более детальную информацию, включая значения march
и mtune
можно использовать две команды.
- Первая команда говорит компилятору не производить линковку (
-c
), и вместо того, чтобы интерпретировать опцию--help
для уточнения параметров командной строки, показывает какие опции включены или отключены (-Q
). В этом случае показаны те опции, которые были включены для выбранной цели:user $
gcc -c -Q -march=native --help=target
- Вторая команда покажет директивы компилятора для построения заголовочного файла, но без фактического выполнения, а результат работы выведет на экран (
-###
). Вывод содержит все параметры оптимизации, а также выбранную архитектуру:user $
gcc -### -march=native /usr/include/stdlib.h
The
l2-cache-size
option represents processor's last level cache (L2 or higher if present).[2]- Возможности glibc-hwcaps (>=sys-libs/glibc-2.33) можно использовать для определения
-march
для более универсальной архитектуры процессора (для >=sys-devel/gcc-11):
user $
/lib64/ld-linux-x86-64.so.2 --help
... Subdirectories of glibc-hwcaps directories, in priority order: x86-64-v4 x86-64-v3 (supported, searched) x86-64-v2 (supported, searched) x86_64 (supported, searched)
В этом примере процессор поддерживает x86-64-v3 psABI x86_64, который можно использовать для -march=x86-64-v3
Давайте теперь рассмотрим -march
в действии. Этот пример приведен для более старого чипа AMD Athlon 64:
CFLAGS="-march=athlon64"
CXXFLAGS="${CFLAGS}"
А это другой пример для обычного процессора Intel:
CFLAGS="-march=skylake"
CXXFLAGS="${CFLAGS}"
Если тип процессора все еще нее определен, либо не знаете какую настройку выбрать, то можно воспользоваться параметром -march=native
. Когда используется этот флаг, GCC попытается распознать процессор и автоматически установит для него подходящие флаги. Однако, не нужно его использовать, если собираетесь компилировать пакеты для разных CPU!
При сборке пакетов на одном компьютере с целью их запуска на другом (например, при сборке на более быстром компьютере с целью запуска на более старом и медленном) не используйте параметр -march=native
. "Native" означает, что полученный код может запускаться только на этом типе CPU. Приложения, собранные с параметром -march=native
на процессоре Intel Core, не будут работать на старом процессоре Intel Atom.
Также, доступны флаги -mtune
и -mcpu
. Эти флаги обычно используются только тогда, когда нет доступного параметра -march
; определенные архитектуры процессоров могут требовать -mtune
или даже -mcpu
. К сожалению, поведение GCC не совсем предсказуемо для того, как эти флаги ведут себя при переходе от одной архитектуры к другой.
На процессорах x86 и x86-64, параметр -march
будет генерировать код, предназначенный специально для этих типов процессоров, используя все доступные наборы команд и корректный двоичный интерфейс приложений; он не будет обладать обратной совместимостью с более старыми/другими типами процессоров. Рассмотрите возможность использования -mtune
, когда необходимо сгенерировать код для более старых процессоров, таких как i386 и i486. Параметр -mtune
производит более общий код, чем -march
; хотя он и настроит код под определенный процессор, он не будет рассматривать доступные наборы команд и двоичный интерфейс приложений. Не используйте -mcpu
на системах с x86 или x86-64, так как это не рекомендуется для этих архитектур.
Только не x86/x86-64 процессоры (такие как ARM, SPARC, Alpha, и PowerPC) могут потребовать параметры -mtune
или -mcpu
вместо -march
. На этих архитектурах, -mtune
/-mcpu
иногда будут вести себя как -march
(на x86/x86-64), но с другим именем флага. Опять же, поведение GCC и именование флагов не является единообразным на каждой из архитектур, поэтому удостоверьтесь, что проконсультировались с GCC manual, для того чтобы определить какой из них должен быть использован.
Для большего количества предполагаемых настроек
-march
/-mtune
/-mcpu
, пожалуйста, прочитайте главу 5 подходящей Руководства пользователя Gentoo для архитектуры. Также, прочтите список руководств по архитектурно-зависимым параметрам GCC, наряду с более подробным объяснением различий между -march
, -mcpu
, и -mtune
.Не используйте
-march=native
или -mtune=native
в переменных CFLAGS или CXXFLAGS файла make.conf при компиляции с помощью distcc.-O
Использование
-O3
или -Ofast
может привести либо к ошибке компиляции, либо неправильному поведению во время выполнения программы.Для отображения всех пакетов, которые были скомпилированы с определенным CFLAGS/CXXFLAGS, можно использовать следующую команду:
grep Ofast /var/db/pkg/*/*/CFLAGS
Следующая по списку - переменная -O
. Она управляет всем уровнем оптимизации. Изменение этой переменной приводит к тому, что компиляция кода занимает больше времени, и сможет занять гораздо больше памяти, особенно когда уровень оптимизации увеличен.
Существует восемь видов настроек переменной -O
: -O0
, -O1
, -O2
, -O3
, -Os
, -Oz
, -Og
и -Ofast
. Используйте только одну из них в /etc/portage/make.conf.
За исключением -O0
, каждая из настроек с префиксом -O
активирует несколько дополнительных флагов, поэтому удостоверьтесь, что Вы прочитали главу руководства GCC по параметрам оптимизации для изучения того, какие флаги активируются на каждом уровне с приставкой -O
, также как и некоторые из объяснений того, что они делают.
Давайте исследуем каждый уровень оптимизации:
-O0
: Этот уровень (буква «O» и ноль за ней) отключает оптимизацию полностью и является уровнем по умолчанию, если никакого уровня с префиксом-O
не указано в переменных CFLAGS или CXXFLAGS. Это сокращает время компиляции и может улучшить данные для отладки, но некоторые приложения не будут работать должным образом без оптимизации. Эта опция не рекомендуется, за исключением использования в целях отладки.
-O1
: Это наиболее простой уровень оптимизации. Компилятор попытается сгенерировать быстрый, занимающий меньше объема код, не тратя много времени на компиляцию. Он достаточно простой, но должен всегда выполнять свою работу.
-O2
: Шаг вперед от-O1
. Рекомендуемый уровень оптимизации, до тех пор пока не понадобится что-то особенное.-O2
активирует несколько дополнительных флагов вдобавок к флагам, активированных-O1
. С параметром-O2
, компилятор попытается увеличить производительность кода без нарушения размера, и не тратя много времени на компиляцию. На этом уровне могут быть использованы SSE и AVX, но YMM-регистры не будут использоваться без отдельного включения параметра-ftree-vectorize
.
-O3
: включает-O2
и оптимизации, являющиеся дорогостоящими с точки зрения времени компиляции и потребления памяти. Компиляция с-O3
, скорее всего, улучшит производительность (хотя это не гарантируется). Хотя в прошлом использование-O3
могло вызвать ошибки компиляции, на данный момент проблемы с-O3
практически всегда являются примерами неопределенного поведения и ошибок в коде, который необходимо исправлять. Некоторые пакеты до сих пор не могут использоваться после компиляции-O3
. Использование-O3
не рекомендуется (если только пакеты после этого не подвергаются тестированию). Однако, этот параметр также включает-ftree-vectorize
, при котором применяется векторизация циклов и используются регистры AVX YMM.
-Ofast
: Новое в GCC 4.7, состоит из-O3
plus-ffast-math
,-fno-protect-parens
,-fallow-store-data-races
,-fstack-arrays
и-fno-semantic-interposition
. Этот параметр нарушает строгое соответствие стандарту, и не рекомендуется для использования. Никогда не используйте этот флаг для всей системы, только для конкретного пакета и только при условии, что программа была протестирована для его использования.
-Os
: На этом уровне код будет оптимизирован по объему. Он активирует все параметры-O2
, которые не приводят к увеличению размера генерируемого кода. Он может быть полезным на компьютерах, которые обладают чрезвычайно ограниченным пространством жесткого диска и/или процессоры с небольшим размером кэша.
-Oz
: Этот уровень впервые появился в GCC 12.1 и является более агрессивной оптимизацией по размеру, чем-Os
. Заметьте, что это может привести к значительному ухудшению производительности по сравнению-O2
, так как для сокращения размера файла будут использоваться больше инструкций.
-Og
: В GCC 4.8 был введен новый общий уровень оптимизации -Og. Он удовлетворяет потребность в быстрой компиляции и имеет превосходные возможности для отладки, обеспечивая при этом приемлемый уровень производительности во время выполнения. Общий опыт разработки должен быть лучше, чем с уровнем оптимизации по умолчанию -O0. Обратите внимание, что -Og не означает -g, он просто отключает оптимизацию кода, которая может помешать отладке.
Как упомянуто ранее, параметр -O2
- рекомендуемый уровень оптимизации. Если компиляция пакета выдает сообщение об ошибке и не используется параметр -O2
, то попробуйте перекопилировать с этой опцией. В качестве выхода, попробуйте установить переменные CFLAGS и CXXFLAGS на наименьший уровень оптимизации, такой как -O1
, или даже -O0 -g2 -ggdb
(для сообщения об ошибках и проверки возможных проблем).
-pipe
Общеупотребительный флаг - -pipe
. Этот флаг не влияет на генерируемый код, но ускоряет процесс компиляции. Он сообщает компилятору, чтобы тот использовал конвейер (pipe) вместо временных файлов в течение разных стадий компиляции, которые используют большее количество памяти. На системах с небольшим количеством памяти, GCC может завершить свою работу. В этих случаях, не используйте этот флаг.
-fomit-frame-pointer
Это очень часто используемый флаг, предназначенный для того, чтобы уменьшить размер генерируемого кода. Он включается на всех уровнях с префиксом -O
(исключая -O0
) на тех архитектурах, где это не затрудняет отладку (таких как x86-64), но его, возможно, необходимо активировать. В этом случае добавьте его к флагам. Хотя руководство по GCC не указывает все архитектуры, но он включается с использованием параметра -O
. Также нужно явно активировать -fomit-frame-pointer
для x86-32, если GCC версии ниже 4.6, либо при использовании -Os
на x86 -32 с любой версией GCC. Однако, использование -fomit-frame-pointer
может сделать отладку сложной, или даже невозможной.
В частности, это делает устранение неполадок в приложениях, написанных на Java и скомпилированных в gcj, намного сложнее, хотя код, написанный на Java - не единственный, который затронут использованием этого флага. Поэтому, в то время как использование этого флага может помочь, оно также затрудняет отладку; трассировка стека, в частности, будет бесполезна. Если не планируется отлаживать программы и нет других переменные CFLAGS, связанные с отладкой, такие как -ggdb
, то попробуйте использовать -fomit-frame-pointer
.
Не комбинируйте
-fomit-frame-pointer
с подобным флагом -momit-leaf-frame-pointer
. Использование последнего флага не рекомендуется, так как -fomit-frame-pointer
уже выполняет всю работу. Кроме того, показано, что -momit-leaf-frame-pointer
негативно влияет на производительность кода.-msse, -msse2, -msse3, -mmmx, -m3dnow
Эти флаги разрешают наборы команд Streaming SIMD Extensions (SSE), SSE2, SSE3, MMX и 3DNow! для архитектур x86 и x86-64. Они полезны в основном в мультимедиа, играх и других интенсивных вычислениях с использованием чисел с плавающей точкой, хотя они также включают несколько других математических расширений. Эти наборы команд можно найти в большинстве современных процессоров.
Убедитесь, что проверили поддерживает ли процессор эти наборы команд, введя команду cat /proc/cpuinfo. Результат будет включать любые дополнительные наборы команд, которые поддерживаются. Заметьте, что pni — это просто другое имя для SSE3.
Обычно, нет необходимости добавлять какие-либо из этих флагов в /etc/portage/make.conf пока в системе используется корректный параметр -march
(например, -march=nocona
подразумевает использование -msse3
). Некоторые заметные исключения — новые процессоры VIA и AMD64, которые поддерживают инструкции, не включаемые параметром -march
(такие как SSE3). Для таких процессоров, нужно включить дополнительные флаги, где это необходимо, после проверки /proc/cpuinfo.
Сверьтесь с списком флагов, характерных для архитектур x86 и x86-64, чтобы увидеть, какие из этих инструкций активированы соответствующим флагом типа процессора. Если инструкция перечислена, то не нужно ее указывать отдельно; она будет включена с помощью подходящей настройки
-march
.Оптимизации безопасности
While it is possible to use a hardened profile, it certainly isn't necessary for adding some hardening flags to /etc/portage/make.conf on a different profile. Especially on a desktop system, the use of position independent code (PIC) and position independent executables (PIE) on a system-wide level may cause compilation failures.
Hardening an otherwise unhardened system, like when using a desktop profile, can be considered a GCC optimization as well, especially in the light of security vulnerabilities such as Meltdown and Spectre.
Some packages feature an individual hardened
USE flag, enabling tested security enhancements (like CFLAGS/CXXFLAGS). It may be a good idea to set this system-wide in /etc/portage/make.conf.
Reading section Do I need to pass any flags to LDFLAGS/CFLAGS in order to turn on hardened building? in the Hardened/FAQ is recommended for retrieving some basic hardened CFLAGS/CXXFLAGS.
Изменение CFLAGS/CXXFLAGS может вызвать проблемы и в некоторых случаях даже вывести систему из строя. Пересборка всей системы с помощью emerge -e @world может помочь решить проблему.
Защита от переполнения
Оптимизация CFLAGS/CXXFLAGS для защиты от переполнения может быть хорошей идеей, если соображения безопасности для вас важнее оптимизации скорости. Это может быть актуально для повседневной настольной системы, в то время как, например, на оптимизированном игровом ПК это будет выглядеть контрпродуктивно.
For GCC version 12, package sys-devel/gcc, the USE flags default-stack-clash-protection
and default-znow
will automatically enable additional overflow protection.
A lot of these flags are now applied internally through the GCC toolchain automatically under the hardened profile, and some even under the regular profile. See table at Hardened/Toolchain.
CFLAGS/CXXFLAGS | LDFLAGS | функция |
---|---|---|
-D_FORTIFY_SOURCE=2
|
детектирование переполнения буфера во время выполнения | |
-D_GLIBCXX_ASSERTIONS
|
проверка границ для строк и контейнеров C++ во время выполнения | |
-fstack-protector-strong
|
защита от разрушения стека | |
-fstack-clash-protection
|
усиление возможности определения переполнения стека | |
-fcf-protection
|
control flow integrity protection | |
-Wl,-z,defs
|
detect and reject underlinking | |
-Wl,-z,now
|
disable lazy binding | |
-Wl,-z,relro
|
отметка сегментов после релокации в режим «только для чтения» |
ASLR
Рандомизация размещения адресного пространства (Address Space Layout Randomization, ASLR) — это способ повышения безопасности за счёт случайного размещения в памяти каждой функции и буфера. Это значительно затрудняет успешное выполнения многих векторов атаки.
PIE is enabled by default when it is safe to do so on all 17.0 profiles[3]. PIC may also be enabled by default on executables for architectures that require it (like AMD64).
There is no need to set PIE or PIC manually in CFLAGS.
CFLAGS/CXXFLAGS | LDFLAGS | функция |
---|---|---|
-fpie
|
-Wl,-pie
|
полная ASLR для исполняемых файлов |
-fpic -shared
|
запрет текстовых перемещений для динамических библиотек |
FAQ по оптимизации
Higher version of GCC should mean better optimizations?
Not always because of software regression, where an optimization with an earlier version of GCC no longer optimizes. A full list of regressions can be found at this link. Should this happen, please file a bug to Gentoo's bugzilla and/or GCC's bugzilla.
Is there a perfect optimizer?
No, because it would solve the halting problem, where it can tell if any program stops or runs forever [4].
Что насчёт оптимизации самого GCC?
У gcc есть USE-флаги pgo
и lto
, включающие Profile Guided Optimization и Link Time Optimization. Для включения этих флагов при сборке gcc укажите:
sys-devel/gcc pgo lto
In Gentoo, a 3-stage bootstrap of gcc is done, meaning it compiles itself three times [5]. In stage1, gcc is complied using an older gcc. In stage2, gcc is compiled using stage1 gcc. In stage3, gcc is compiled using stage2 gcc and is used to verify that stage2 gcc and stage3 gcc are the same. This is done because it is tested more completely and has better performance. The lto
use flag adds -flto to BOOT_CFLAGS. The pgo
use flag adds -fprofile-generate
to stage2 gcc and adds -fprofile-use -fprofile-reproducible=parallel-runs
to stage4 gcc.
С PGO производительность gcc может повыситься, однако время сборки увеличится вдвое.
Но я получаю лучшую производительность с -funroll-loops -fomg-optimize!
Нет, людям только кажется что они получают лучшую производительность, потому что кто-то их убедил в том, что чем больше флагов, тем лучше. Агрессивные флаги только повредят приложениям при глобальном использовании. Даже GCC manual говорит, что использование параметров -funroll-loops
и -funroll-all-loops
может увеличить объем кода и время его исполнения. Хотя, по каким-то причинам, эти два флага, вместе с флагами -ffast-math
, -fforce-mem
, -fforce-addr
, и им подобными, продолжают пользоваться популярностью среди гонщиков, которые хотят повысить чувство собственной важности.
Истина в том, что это чрезвычайно агрессивные флаги. Посмотрите по форумам Gentoo и Bugzilla, чтобы увидеть, что эти флаги могут сделать: ничего хорошего!
Не нужно использовать эти флаги глобально, в переменных CFLAGS и CXXFLAGS. Они только ухудшат производительность. Может показаться, что они сделают систему более высокопроизводительной, работающей по последнему слову техники, но они ничего не делают, кроме раздувания кода и приведут к тому, что ваши сообщения о багах пометят как INVALID или WONTFIX.
Такие опасные флаги, как эти не нужны. Не используйте их. Придерживайтесь основ: -march
, -O
, и -pipe
.
Что по поводу уровней оптимизации -O больших чем 3?
Некоторые пользователи хвалятся даже большей производительностью, достигнутой использованием -O4
, -O9
, и так далее, но в действительности, уровни -O
большие чем 3 не имеют никакого эффекта. Компилятор может принимать переменные CFLAGS, такие как -O4
, но, на самом деле, ничего с ними не делает. Он только выполняет оптимизацию до уровня -O3
, и ничего больше:
Нужно больше доказательств? Исследуйте исходный код:
case OPT_LEVELS_3_PLUS:
enabled = (level >= 3);
break;
case OPT_LEVELS_3_PLUS_AND_SIZE:
enabled = (level >= 3 || size);
break;
Как можно увидеть, любое значение больше тройки рассматривается как -O3
.
Что насчет компиляции не на целевой машине?
Некоторые читатели могут спросить, не приведет ли компиляция не на целевой машине, сильно отличающейся архитектурой процессора или структурой GCC, к плохому качеству оптимизации (по сравнению с нативной компиляцией). Ответ прост: Нет. Независимо от используемого оборудования, на котором проводится компиляция, и от значения переменной CHOST, с использованием которой был собран GCC, если используются те же аргументы (кроме -march=native
) и та же версия GCC (хотя небольшие оптимизации могут отличаться), результирующий уровень оптимизации останется строго тем же.
Например, если Gentoo установлен на компьютере, на котором переменная CHOST для GCC равна i686-pc-linux-gnu, а сервер Distcc настроен на другом компьютере, на котором CHOST для GCC равна i486-linux-gnu, то не нужно бояться, что результаты будут менее оптимальны из-за отличающейся архитектуры удаленного компилятора и/или оборудования. Результат будет оптимизирован в той же степени, как и при сборке на целевом компьютере, если, конечно, обоим компиляторам передаются одинаковые параметры (и параметр -march
не включает значение native
). В данном конкретном случае целевую архитектуру нужно явно определять, как указано в статье Distcc.
Единственная разница в поведении между двумя версиями GCC, построенными с использованием разных архитектур в значении параметра -march
по умолчанию. Он берется из переменной CHOST для GCC, если он не указан явно в командной строке.
А что об избыточных флагах?
Часто переменные CFLAGS и CXXFLAGS, которые включаются на разных уровнях -O
, указаны избыточно в /etc/portage/make.conf. Иногда, это сделано по неосведомленности, но также и для того, чтобы избежать отфильтровывание флагов или их замещение.
Фильтрация/замещение флагов используется во многих ebuild-файлах, находящихся в дереве Portage. Обычно, это делается потому что пакеты не компилируются на определенных уровнях -O
, или когда исходный код очень чувствителен к дополнительно используемым флагам. Ebuild-файл или отфильтровывает некоторые или все переменные CFLAGS и CXXFLAGS, или может заменить -O
другим уровнем.
Руководство разработчика Gentoo описывает в общих чертах, где и как работает фильтрация/замещение флагов.
Возможно обойти фильтрацию уровней -O
, избыточно перечисляя флаги для определенного уровня, например -O3
, делая такие вещи как:
CFLAGS="-O3 -finline-functions -funswitch-loops"
Однако, это не самая умная вещь, которую можно сделать. CFLAGS отфильтровываются не зря! Когда флаги фильтруются, это означает, что собирать пакет с этими флагами небезопасно. Очевидно, что небезопасно компилировать всю систему с -O3
если некоторые из флагов, включенных на этом уровне, вызовут проблемы с определенными пакетами. Следовательно, не пытайтесь обхитрить разработчиков, которые поддерживают эти пакеты. Доверяйте разработчикам. Фильтрация флагов и их замена делаются для обеспечения стабильности системы и приложения! Если ebuild-файл указывает альтернативные флаги, то не пытайтесь это обойти.
Сборка пакетов с недопустимыми флагами скорее всего вызовет проблемы. При создании отчета об ошибке на Bugzilla флаги, установленные в файле /etc/portage/make.conf, отчетливо видны, и разработчики все равно попросят пересобрать пакет без этих флагов. Чтобы избежать необходимости повторной сборки, не используйте эти флаги изначально. Не стоит автоматически полагать себя более сведущим, чем разработчики.
Что по поводу LDFLAGS?
Разработчики Gentoo уже установили простые, безопасные LDFLAGS в базовых профилях, поэтому не нужно их изменять.
Могу ли я использовать флаги для отдельных пакетов?
Использование флагов для отдельных пакетов затрудняет отладку и поддержку. Убедитесь, что упомянули об использовании этой возможности и о сделанных изменениях.
Информация об использовании переменных среды для каждого пакета по отдельности (включая CFLAGS) описана в Руководстве пользователя Gentoo, раздел «Переменное окружение для отдельных пакетов».
Профильная оптимизация (PGO)
Profile guided optimization (PGO) consists of compiling and profiling a program to assess hot paths in the code. Optimizations are then applied based on this analysis. Specifically, the code is compiled with -fprofile-generate
, which instrument the code. Second, the code is run with applications to collect profile information. Finally, using the profiled data, the code is compiled with -fprofile-use
. To manually enable PGO for packages, see this link.
Firefox также поддерживает PGO, хотя иногда это может нарушить процесс сборки.
Оптимизация во время связывания (LTO)
LTO значительно увеличивает время компиляции; если хотя бы один объектный файл был изменен во время компиляции, LTO начнет пересборку всего кода целиком. Существует действующий проект GSoC, нацеленный на то, чтобы LTO перекомпилировал только то, что посчитает нужным.
LTO все ещё является экспериментальным функционалом. Возможно, понадобится отключить эту оптимизацию перед заполнением отчета об ошибке, так как она может являться источником проблем. Используется флаг -flto
совместно с оптимальным значением auto
(определять, сколько задач использовать) или целочисленным значением (количество задач для параллельного выполнения).
See the LTO article for more information on LTO on Gentoo.
См. также
- Настройка параметров компиляции (Руководство пользователя AMD64)
- CPU_FLAGS_* — a USE_EXPAND-переменная, содержащая набор инструкций и другие связанные с процессором возможности.
- Safe CFLAGS — a summary of "safe" settings for CFLAGS on Gentoo Linux.
- RUSTFLAGS
Внешние ресурсы
Следующие источники могут быть полезными в дальнейшем изучении оптимизации:
Ссылки
- ↑ GNU GCC Bugzilla, AVX/AVX2 no ymm registers used in a trivial reduction. Retrieved on 2017/07/18.
- ↑ GNU GCC Bugzilla, 'gcc -marc=native' sets L2 cache size equal to L3 cache size on i7 and i5 CPU. Retrieved on 2022/08/14.
- ↑ New 17.0 profiles in the Gentoo repository
- ↑ https://en.wikipedia.org/wiki/Full-employment_theorem
- ↑ https://gcc.gnu.org/install/build.html
This page is based on a document formerly found on our main website gentoo.org.
The following people contributed to the original document:
They are listed here because wiki history does not allow for any external attribution. If you edit the wiki article, please do not add yourself here; your contributions are recorded on each article's associated history page.