Подтвердить что ты не робот

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

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

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

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

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

gcc -O100

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

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

4b9b3361

Ответ 1

Чтобы быть педантичным, существует 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

Ответ 2

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

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

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

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

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

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

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

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

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

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

Ответ 3

Пусть интерпретирует исходный код GCC 5.1, чтобы узнать, что происходит на -O100, так как на man-странице не ясно.

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

  • что-либо выше -O3 до INT_MAX совпадает с -O3, но это может легко измениться в будущем, поэтому не полагайтесь на него.
  • GCC 5.1 запускает поведение undefined, если вы вводите целые числа больше 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, чтобы передать его вперед, поэтому мы должны быть в порядке.

Ответ 4

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