Скільки є рівнів оптимізації GCC?


101

Скільки є рівнів оптимізації GCC ?

Я спробував gcc -O1, gcc -O2, gcc -O3 та gcc -O4

Якщо я використовую дійсно велику кількість, це не спрацює.

Однак я спробував

gcc -O100

і складено.

Скільки є рівнів оптимізації?


13
@minitech На який FM ви дивитесь? Навіть за допомогою man gccCygwin (12000 непарних рядків) ви можете шукати -Oі знаходити всі відповіді нижче, а потім деякі.
Єнс

1
@minmaxavg, прочитавши джерело, я не згоден з вами: що-небудь більше, ніж 3таке, як 3(до тих пір, поки воно не intпереповнене). Дивіться мою відповідь .
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

1
Насправді, у GCC є багато інших прапорів для тонкої настройки оптимізації. -fomit-stack-pointer змінить створений код.
Базиль Старинкевич

Відповіді:


141

Щоб бути педантичним, є 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


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ɐɹƆ

Там також -Ogє всі варіанти оптимізації, які не заважають налагодженню
einpoklum

47

Давайте інтерпретуємо вихідний код GCC 5.1, щоб побачити, що відбувається-O100 оскільки на сторінці man не зрозуміло.

Ми зробимо висновок, що:

  • все, що вище -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 - це специфічний формат опису параметрів CLI для 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

Тепер ми вітаємо:

git grep -E '\bOPT_O\b'

що вказує нам на два файли:

Давайте спочатку простежимо opts.c

opts.c: default_options_optimization

Всі opts.cзвичаї трапляються всередині: default_options_optimization.

Ми повертаємося назад, щоб побачити, хто викликає цю функцію, і бачимо, що єдиний шлях коду:

  • 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 - внутрішній максимум

в 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, що використовує achar для економії місця. Отже, 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_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настільки цікаво, що ми жаліємось 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це зафіксовано на сторінці man. Чи є ще?

Ми 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: Оптимізуйте розмір коду. Це може фактично підвищити швидкість у деяких випадках за рахунок кращої поведінки кеш-пам'яті.

  • -Og: Оптимізуйте, але не заважайте налагоджувати. Це дає змогу виконувати незручні показники для налагоджувальних збірок і призначені для заміни -O0для налагодження.

Існують також інші параметри, які не ввімкнено жодним із них, і їх потрібно включити окремо. Можна також скористатися опцією оптимізації, але відключити конкретні прапори, включені в результаті цієї оптимізації.

Для отримання додаткової інформації див на веб-сайті GCC.


Дійсно, хоч, щоб бути справедливими до інших відповідей, ні -Ofast та -Og не існували, коли ці відповіді були написані.
janneb

То чому -O100тоді компілюється?
einpoklum

3
@einpoklum, оскільки GCC розглядає все вище -O3 як рівне -O3.
Демі

На жаль, ви все одно отримаєте тонну <оптимізовану> у відладчику з -Og. Крок все ще стрибає навмання. Це марний ІМХО.
doug65536

3

Чотири (0-3): Дивіться посібник GCC 4.4.2 . Все, що вище - лише -O3, але в якийсь момент ви переповнить обмеження розміру змінної.


Я вивчив вихідний код у своїй відповіді і погоджуюся з вами. Більш педантично, здається, що GCC покладається на atoiневизначену поведінку з наступним 255внутрішнім обмеженням.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

4
Подумайте про те, як видалити свою відповідь, оскільки вона (принаймні в ці дні) неправильна.
einpoklum
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.