Сколько существует уровней оптимизации GCC?

102

Сколько существует уровней оптимизации GCC ?

Я пробовал gcc -O1, gcc -O2, gcc -O3 и gcc -O4

Если я использую очень большое число, это не сработает.

Однако я пробовал

gcc -O100

и он скомпилирован.

Сколько существует уровней оптимизации?

нейромант
источник
13
@minitech Какой FM ты смотришь? Даже с man gccCygwin (12000 нечетных строк) вы можете искать -Oи находить все, что указано в ответах ниже, а также некоторые.
Jens
1
@minmaxavg после прочтения источника я не согласен с вами: все, что больше, 3то же самое 3(при условии, что оно не intпереполняется). Смотрите мой ответ .
Чиро Сантилли 郝海东 冠状 病 六四 事件 法轮功
1
На самом деле в GCC есть много других флагов для точной настройки оптимизации. -fomit-stack-pointer изменит сгенерированный код.
Василий Старынкевич

Ответы:

142

Чтобы быть педантичным, есть 8 различных допустимых опций -O, которые вы можете передать gcc, хотя есть некоторые, которые означают то же самое.

В исходной версии этого ответа говорилось, что существует 7 вариантов. GCC с тех пор добавил, -Ogчтобы довести общее количество до 8

На странице руководства :

  • -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

Glen
источник
23
Если вы разрабатываете на Mac OS X, есть дополнительный -Ozпараметр, который «оптимизирует размер более агрессивно, чем -Os»: developer.apple.com/mac/library/DOCUMENTATION/DeveloperTools/…
pauldoo
6
Примечание: O3 не обязательно лучше, чем O2, даже если название предполагает это. Попробуйте оба.
johan d
1
Страница @pauldoo 404, заменить на archive.org
noɥʇʎԀʎzɐɹƆ 06
Также -Ogесть все параметры оптимизации, которые не мешают отладке
einpoklum
49

Давайте интерпретируем исходный код GCC 5.1

Постараемся разобраться, что происходит дальше -O100, поскольку на странице руководства это неясно.

Сделаем вывод, что:

  • все, что выше -O3, INT_MAXто же самое -O3, но это может легко измениться в будущем, поэтому не полагайтесь на это.
  • GCC 5.1 запускает неопределенное поведение, если вы вводите целые числа больше, чем INT_MAX.
  • аргумент может состоять только из цифр, иначе он не работает корректно. В частности, это исключает отрицательные целые числа, такие как-O-1

Сосредоточьтесь на подпрограммах

Во- первых помните , что НКУ просто передний конец для 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 - это формат описания опций интерфейса командной строки для GCC, описанный во внутренней документации и переведенный на 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 */

В качестве бонуса, пока мы ищем \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 backtrack, чтобы увидеть, кто вызывает эту функцию, и видим, что единственный путь кода:

  • 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

После тщетного поиска мы замечаем, что это 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 - это внутренний максимум

in opts.c:integral_argument, atoiприменяется к входному аргументу, так INT_MAXже как и верхняя граница. И если вы поставите что-то большее, кажется, что GCC выполняет неопределенное поведение C. Ой?

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: возможно_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_PLUSin 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чтобы передать его вперед, так что все должно быть в порядке.

Чиро Сантилли 郝海东 冠状 病 六四 事件 法轮功
источник
38

Семь различных уровней:

  • -O0 (по умолчанию): без оптимизации.

  • -Oили -O1(то же самое): оптимизируйте, но не тратьте слишком много времени.

  • -O2: Более агрессивная оптимизация

  • -O3: Оптимизировать наиболее агрессивно

  • -Ofast: Эквивалентно -O3 -ffast-math. -ffast-mathзапускает оптимизацию с плавающей запятой, не соответствующую стандартам. Это позволяет компилятору делать вид, что числа с плавающей запятой бесконечно точны, и что их алгебра следует стандартным правилам алгебры действительных чисел. Он также сообщает компилятору, чтобы оборудование сбрасывало денормальные значения до нуля и обрабатывало денормальные значения как ноль, по крайней мере, на некоторых процессорах, включая x86 и x86-64. Денормальные значения запускают медленный путь для многих FPU, поэтому рассмотрение их как нуля (которое не запускает медленный путь) может быть большим выигрышем в производительности.

  • -Os: Оптимизировать под размер кода. В некоторых случаях это действительно может улучшить скорость из-за лучшего поведения I-кеша.

  • -Og: Оптимизировать, но не мешать отладке. Это обеспечивает отличную производительность отладочных сборок и предназначено для замены -O0отладочных сборок.

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

Для получения дополнительной информации см. Веб-сайт GCC.

Деми
источник
В самом деле, хотя, честно говоря, к другим ответам, ни -Ofast, ни -Og не существовало, когда эти ответы были написаны.
janneb
Так почему же -O100тогда компилируется?
einpoklum
3
@einpoklum, потому что GCC рассматривает все, что выше -O3, как -O3.
Деми
К сожалению, вы все еще получаете тонну <optimized out> в отладчике с -Og. Шагающий по-прежнему прыгает случайным образом. ИМХО это бесполезно.
doug65536
3

Четыре (0-3): см. Руководство GCC 4.4.2 . Все, что выше, просто -O3, но в какой-то момент вы превысите ограничение на размер переменной.

Том
источник
В своем ответе я исследовал исходный код и согласен с вами. Более педантично, GCC, похоже, полагается на atoiнеопределенное поведение, за которым следует 255внутреннее ограничение.
Чиро Сантилли 郝海东 冠状 病 六四 事件 法轮功
4
Удалите свой ответ, поскольку он (по крайней мере, в наши дни) неверен.
einpoklum