Сколько уровней оптимизации GCC?
Я пробовал gcc -O1, gcc -O2, gcc -O3 и gcc -O4
Если я использую действительно большое количество, это не сработает.
Однако я пробовал
gcc -O100
и он скомпилирован.
Сколько уровней оптимизации существует?
Сколько уровней оптимизации GCC?
Я пробовал gcc -O1, gcc -O2, gcc -O3 и gcc -O4
Если я использую действительно большое количество, это не сработает.
Однако я пробовал
gcc -O100
и он скомпилирован.
Сколько уровней оптимизации существует?
Чтобы быть педантичным, существует 8 различных допустимых параметров -O, которые вы можете дать gcc, хотя есть некоторые, которые означают одно и то же.
В оригинальной версии этого ответа было указано 7 вариантов. С тех пор GCC добавил -Og
, чтобы довести общее число до 8
На странице man:
-O
(То же, что -O1
)-O0
(нет оптимизации, по умолчанию, если не указан уровень оптимизации)-O1
(оптимизируйте минимально)-O2
(оптимизируйте больше)-O3
(оптимизируйте еще больше)-Ofast
(оптимизируйте очень агрессивно до уровня нарушения стандартного соответствия)-Og
(оптимизировать работу отладки. -Og включает оптимизацию, которая не мешает отладке. оптимальный уровень выбора для стандартного цикла редактирования-компиляции-отладки, предлагающий разумный уровень оптимизации сохраняя при этом быструю компиляцию и хороший отладочный опыт.)-Os
(Оптимизация для размера. -Os
позволяет использовать все -O2
оптимизации, которые обычно не увеличивают размер кода, а также обеспечивают дальнейшую оптимизацию
предназначенный для уменьшения размера кода.
-Os
отключает следующие флаги оптимизации: -falign-functions -falign-jumps -falign-loops -falign-labels -freorder-blocks -freorder-blocks-and-partition -fprefetch-loop-arrays -ftree-vect-loop-version
)Также могут быть оптимизированы платформы, так как примечания @pauldoo, OS X имеет -Oz
Семь различных уровней:
-O0
(по умолчанию): нет оптимизации.
-O
или -O1
(то же самое): оптимизируйте, но не тратите слишком много времени.
-O2
: оптимизировать более агрессивно
-O3
: оптимизировать наиболее агрессивно
-Ofast
: эквивалентно -O3 -ffast-math
. -ffast-math
запускает оптимизацию с плавающей запятой, не соответствующую стандартам. Это позволяет компилятору притворяться, что числа с плавающей запятой бесконечно точны, а эта алгебра на них соответствует стандартным правилам алгебры с вещественными числами. Он также сообщает компилятору, чтобы сообщить аппаратным средствам сбросить денормалы к нулю и обработать денормалы как ноль, по крайней мере на некоторых процессорах, включая x86 и x86-64. Денормалы запускают медленный путь на многих FPU, и поэтому обращение с ними как ноль (которое не запускает медленный путь) может быть большим выигрышем в производительности.
-Os
: оптимизируйте размер кода. В некоторых случаях это может повысить скорость, из-за лучшего поведения I-cache.
-Og
: оптимизируйте, но не мешайте отладке. Это позволяет не смущать производительность для отладочных сборников и предназначена для замены -O0
для отладочных сборников.
Существуют и другие параметры, которые не включены ни одним из них, и должны быть включены отдельно. Также возможно использовать параметр оптимизации, но отключить определенные флаги, включенные этой оптимизацией.
Для получения дополнительной информации см. веб-сайт GCC.
Пусть интерпретирует исходный код GCC 5.1, чтобы узнать, что происходит на -O100
, так как на man-странице не ясно.
Сделаем вывод, что:
-O3
до INT_MAX
совпадает с -O3
, но это может легко измениться в будущем, поэтому не полагайтесь на него.INT_MAX
.-O-1
Фокус на подпрограммах
Прежде всего помните, что GCC является только интерфейсом для cpp
, as
, cc1
, collect2
. Быстрый ./XXX --help
говорит, что только collect2
и cc1
принимают -O
, поэтому давайте сосредоточимся на них.
и
gcc -v -O100 main.c |& grep 100
дает:
COLLECT_GCC_OPTIONS='-O100' '-v' '-mtune=generic' '-march=x86-64'
/usr/local/libexec/gcc/x86_64-unknown-linux-gnu/5.1.0/cc1 [[noise]] hello_world.c -O100 -o /tmp/ccetECB5.
поэтому -O
был перенаправлен как на cc1
, так и на collect2
.
O в common.opt
common.opt - это формат описания интерфейса CLCC, определенный в внутренняя документация и переведена на C с помощью opth-gen.awk и optc-gen.awk.
Он содержит следующие интересные строки:
O
Common JoinedOrMissing Optimization
-O<number> Set optimization level to <number>
Os
Common Optimization
Optimize for space rather than speed
Ofast
Common Optimization
Optimize for speed disregarding exact standards compliance
Og
Common Optimization
Optimize for debugging experience rather than speed or size
который задает все параметры O
. Обратите внимание, что -O<n>
находится в отдельном семействе от других Os
, Ofast
и Og
.
При сборке создается файл options.h
, который содержит:
OPT_O = 139, /* -O */
OPT_Ofast = 140, /* -Ofast */
OPT_Og = 141, /* -Og */
OPT_Os = 142, /* -Os */
В качестве бонуса, в то время как мы получаем grepping для \bO\n
внутри common.opt
, мы замечаем строки:
-optimize
Common Alias(O)
который учит нас, что --optimize
(двойной тире, поскольку он начинается с тире -optimize
в файле .opt
), является недокументированным псевдонимом для -O
, который может использоваться как --optimize=3
!
Где используется OPT_O
Теперь мы grep:
git grep -E '\bOPT_O\b'
который указывает нам на два файла:
Сначала отпустите opts.c
opts.c: default_options_optimization
Все opts.c
обычаи происходят внутри: default_options_optimization
.
Мы возвращаемся к обратному выводу grep, чтобы узнать, кто вызывает эту функцию, и мы видим, что единственный путь к коду:
main.c:main
toplev.c:toplev::main
opts-global.c:decode_opts
opts.c:default_options_optimization
и main.c
- это точка входа cc1
. Хорошо!
Первая часть этой функции:
integral_argument
, который вызывает atoi
в строке, соответствующей OPT_O
, для анализа входного аргументаopts->x_optimize
, где opts
является struct gcc_opts
.struct gcc_opts
После grepping напрасно, мы замечаем, что этот struct
также генерируется в options.h
:
struct gcc_options {
int x_optimize;
[...]
}
где x_optimize
исходит из строк:
Variable
int optimize
присутствует в common.opt
и что options.c
:
struct gcc_options global_options;
поэтому мы предполагаем, что это то, что содержит глобальное состояние всей конфигурации, а int x_optimize
- значение оптимизации.
255 - внутренний максимум
в opts.c:integral_argument
, atoi
применяется к входному аргументу, поэтому INT_MAX
является верхней границей. И если вы ставите что-то большее, похоже, что GCC запускает поведение C undefined. Уч?
integral_argument
также тонко обертывает atoi
и отклоняет аргумент, если какой-либо символ не является цифрой. Таким образом, отрицательные значения изящно исчезают.
Возвращаясь к opts.c:default_options_optimization
, мы видим строку:
if ((unsigned int) opts->x_optimize > 255)
opts->x_optimize = 255;
чтобы уровень оптимизации был усечен до 255
. Читая opth-gen.awk
, я встретил:
# All of the optimization switches gathered together so they can be saved and restored.
# This will allow attribute((cold)) to turn on space optimization.
и на сгенерированном options.h
:
struct GTY(()) cl_optimization
{
unsigned char x_optimize;
который объясняет, почему усечение: параметры также должны быть перенаправлены на cl_optimization
, который использует char
для экономии места. Таким образом, 255 - это внутренний максимум.
opts.c: maybe_default_options
Возвращаясь к opts.c:default_options_optimization
, мы сталкиваемся с maybe_default_options
, что звучит интересно. Мы вводим его, а затем maybe_default_option
, где мы достигаем большого переключателя:
switch (default_opt->levels)
{
[...]
case OPT_LEVELS_1_PLUS:
enabled = (level >= 1);
break;
[...]
case OPT_LEVELS_3_PLUS:
enabled = (level >= 3);
break;
Нет проверок >= 4
, что указывает на то, что 3
является максимально возможным.
Затем мы ищем определение OPT_LEVELS_3_PLUS
в common-target.h
:
enum opt_levels
{
OPT_LEVELS_NONE, /* No levels (mark end of array). */
OPT_LEVELS_ALL, /* All levels (used by targets to disable options
enabled in target-independent code). */
OPT_LEVELS_0_ONLY, /* -O0 only. */
OPT_LEVELS_1_PLUS, /* -O1 and above, including -Os and -Og. */
OPT_LEVELS_1_PLUS_SPEED_ONLY, /* -O1 and above, but not -Os or -Og. */
OPT_LEVELS_1_PLUS_NOT_DEBUG, /* -O1 and above, but not -Og. */
OPT_LEVELS_2_PLUS, /* -O2 and above, including -Os. */
OPT_LEVELS_2_PLUS_SPEED_ONLY, /* -O2 and above, but not -Os or -Og. */
OPT_LEVELS_3_PLUS, /* -O3 and above. */
OPT_LEVELS_3_PLUS_AND_SIZE, /* -O3 and above and -Os. */
OPT_LEVELS_SIZE, /* -Os only. */
OPT_LEVELS_FAST /* -Ofast only. */
};
Ха! Это сильный индикатор, что есть только 3 уровня.
opts.c: default_options_table
opt_levels
настолько интересен, что мы grep OPT_LEVELS_3_PLUS
и сталкиваемся с opts.c:default_options_table
:
static const struct default_options default_options_table[] = {
/* -O1 optimizations. */
{ OPT_LEVELS_1_PLUS, OPT_fdefer_pop, NULL, 1 },
[...]
/* -O3 optimizations. */
{ OPT_LEVELS_3_PLUS, OPT_ftree_loop_distribute_patterns, NULL, 1 },
[...]
}
поэтому здесь кодируется -On
конкретное оптимизационное сопоставление, упомянутое в документах. Ницца!
Убедитесь, что больше не используется для x_optimize
Основное использование x_optimize
состояло в том, чтобы установить другие конкретные параметры оптимизации, такие как -fdefer_pop
, как описано на странице руководства. Есть ли еще?
Мы grep
и найдем еще несколько. Число невелико, и при ручном осмотре мы видим, что каждое использование выполняется не более чем a x_optimize >= 3
, поэтому наш вывод имеет место.
LTO-wrapper.c
Теперь мы переходим ко второму вхождению OPT_O
, которое находилось в lto-wrapper.c
.
LTO означает Оптимизацию времени связи, которая, как следует из названия, нуждается в опции -O
и будет связана с collec2
(которая в основном является компоновщиком).
Фактически, первая строка lto-wrapper.c
говорит:
/* Wrapper to call lto. Used by collect2 and the linker plugin.
В этом файле вхождения OPT_O
, по-видимому, только нормализуют значение O
, чтобы передать его вперед, поэтому мы должны быть в порядке.
Четыре (0-3): см. руководство GCC 4.4.2 . Все, что выше, равно -O3, но в какой-то момент вы превысите ограничение на размер переменной.