Ile jest poziomów optymalizacji GCC?

101

Ile jest poziomów optymalizacji GCC ?

Próbowałem gcc -O1, gcc -O2, gcc -O3 i gcc -O4

Jeśli użyję naprawdę dużej liczby, to nie zadziała.

Jednak próbowałem

gcc -O100

i to skompilowane.

Ile jest poziomów optymalizacji?

neuromancer
źródło
13
@minitech Na który FM patrzysz? Nawet man gccna Cygwin (12000 nieparzystych wierszy) możesz wyszukać -Oi znaleźć wszystko, co podają poniższe odpowiedzi, a następnie niektóre.
Jens,
1
@minmaxavg po przeczytaniu źródła, nie zgadzam się z tobą: wszystko większe niż 3jest takie samo jak 3(o ile się nie intprzepełnia). Zobacz moją odpowiedź .
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
1
W rzeczywistości GCC ma wiele innych flag umożliwiających dostrojenie optymalizacji. -fomit-stack-pointer zmieni wygenerowany kod.
Basile Starynkevitch

Odpowiedzi:

141

Aby być pedantycznym, istnieje 8 różnych poprawnych opcji -O, które możesz dać gcc, chociaż jest kilka, które oznaczają to samo.

W pierwotnej wersji tej odpowiedzi podano 7 opcji. Od tego czasu GCC dodał, -Ogaby uzyskać łączną liczbę 8

Ze strony podręcznika :

  • -O (Tak samo jak -O1)
  • -O0 (nie optymalizuj, wartość domyślna, jeśli nie określono poziomu optymalizacji)
  • -O1 (optymalizuj minimalnie)
  • -O2 (optymalizuj więcej)
  • -O3 (optymalizuj jeszcze bardziej)
  • -Ofast (optymalizacja bardzo agresywna do momentu złamania zgodności ze standardami)
  • -Og (Optymalizuj działanie debugowania. -Og włącza optymalizacje, które nie kolidują z debugowaniem. Powinien to być poziom optymalizacji wybrany dla standardowego cyklu edycja-kompilacja-debugowanie, oferujący rozsądny poziom optymalizacji przy jednoczesnym zachowaniu szybkiej kompilacji i dobrych wrażeń z debugowania. )
  • -Os(. Optymalizacja dla rozmiaru -Oswłącza wszystkie -O2optymalizacje, które zazwyczaj nie zwiększają rozmiar kodu wykonuje również dalsze optymalizacje zaprojektowane w celu zmniejszenia rozmiaru kodu.. -OsWyłącza następujące flagi optymalizacji: -falign-functions -falign-jumps -falign-loops -falign-labels -freorder-blocks -freorder-blocks-and-partition -fprefetch-loop-arrays -ftree-vect-loop-version)

Mogą również istnieć optymalizacje specyficzne dla platformy, jak zauważa @pauldoo, OS X. -Oz

Dolina górska
źródło
23
Jeśli tworzysz w systemie Mac OS X, istnieje dodatkowe -Ozustawienie, które jest „optymalizuj pod kątem rozmiaru bardziej agresywnie niż -Os”: developer.apple.com/mac/library/DOCUMENTATION/DeveloperTools/ ...
pauldoo
6
Uwaga: O3 niekoniecznie jest lepsze niż O2, nawet jeśli nazwa to sugeruje. Spróbuj obu.
johan d
1
@pauldoo 404 strona, zamień na archive.org
noɥʇʎԀʎzɐɹƆ
Jest też -Og, czyli wszystkie opcje optymalizacji, które nie kolidują z debugowaniem
einpoklum
47

Spójrzmy prawdzie w interpretacji kodu źródłowego GCC 5.1 , aby zobaczyć, co dzieje się -O100, ponieważ nie jest jasne, na stronie man.

Wnioskujemy, że:

  • wszystko powyżej -O3do INT_MAXjest takie samo jak -O3, ale może się to łatwo zmienić w przyszłości, więc nie polegaj na tym.
  • GCC 5.1 uruchamia niezdefiniowane zachowanie, jeśli wprowadzisz liczby całkowite większe niż INT_MAX.
  • argument może mieć tylko cyfry lub zawodzi. W szczególności wyklucza to ujemne liczby całkowite, takie jak-O-1

Skoncentruj się na podprogramach

Po pierwsze należy pamiętać, że GCC jest tylko front-end dla cpp, as, cc1, collect2. Krótko ./XXX --helpmówiąc, tylko collect2i cc1bierz -O, więc skupmy się na nich.

I:

gcc -v -O100 main.c |& grep 100

daje:

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.

więc -Ozostał przekazany do obu cc1i collect2.

O in common.opt

common.opt to specyficzny dla GCC format opisu opcji CLI opisany w wewnętrznej dokumentacji i przetłumaczony na C przez opth-gen.awk i optc-gen.awk .

Zawiera następujące interesujące wiersze:

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

które określają wszystkie Oopcje. Zwróć uwagę, jak -O<n>jest w osobnej rodziny z drugiej strony Os, Ofasti Og.

Kiedy budujemy, generuje options.hplik, który zawiera:

OPT_O = 139,                               /* -O */
OPT_Ofast = 140,                           /* -Ofast */
OPT_Og = 141,                              /* -Og */
OPT_Os = 142,                              /* -Os */

Jako bonus, gdy grepujemy do \bO\nśrodka common.opt, zauważamy linie:

-optimize
Common Alias(O)

co nas uczy, że --optimize(podwójny myślnik, ponieważ zaczyna się od myślnika -optimizew .optpliku) jest nieudokumentowanym aliasem, -Októrego można użyć jako --optimize=3!

Gdzie OPT_O jest używany

Teraz grep:

git grep -E '\bOPT_O\b'

co wskazuje nam na dwa pliki:

Najpierw wytropmy opts.c

opts.c: default_options_optimization

Wszystkie opts.czwyczaje zdarzyć wewnątrz: default_options_optimization.

Wykonujemy grep backtrack, aby zobaczyć, kto wywołuje tę funkcję i widzimy, że jedyna ścieżka do kodu to:

  • main.c:main
  • toplev.c:toplev::main
  • opts-global.c:decode_opts
  • opts.c:default_options_optimization

i main.cjest punktem wejścia cc1. Dobry!

Pierwsza część tej funkcji:

  • robi, integral_argumentktóra wywołuje atoiciąg odpowiadający do, OPT_Oaby przeanalizować argument wejściowy
  • przechowuje wartość wewnątrz, opts->x_optimizegdzie optsjest struct gcc_opts.

struct gcc_opts

Po grepowaniu na próżno zauważamy, że structjest to również generowane pod adresem options.h:

struct gcc_options {
    int x_optimize;
    [...]
}

skąd x_optimizepochodzi z linii:

Variable
int optimize

obecny w common.opti że options.c:

struct gcc_options global_options;

więc domyślamy się, że to właśnie zawiera cały stan globalny konfiguracji i int x_optimizejest wartością optymalizacji.

255 to wewnętrzne maksimum

in opts.c:integral_argument, atoijest stosowany do argumentu wejściowego, więc INT_MAXjest to górna granica. A jeśli umieścisz coś większego, wydaje się, że GCC uruchamia niezdefiniowane zachowanie C. Auć?

integral_argumentrównież cienko zawija atoii odrzuca argument, jeśli którykolwiek znak nie jest cyfrą. Zatem wartości ujemne zawodzą wdzięcznie.

Wracając do opts.c:default_options_optimization, widzimy wiersz:

if ((unsigned int) opts->x_optimize > 255)
  opts->x_optimize = 255;

tak aby poziom optymalizacji został obcięty do 255. Podczas czytania opth-gen.awknatknąłem się na:

# All of the optimization switches gathered together so they can be saved and restored.
# This will allow attribute((cold)) to turn on space optimization.

i na wygenerowanych options.h:

struct GTY(()) cl_optimization
{
  unsigned char x_optimize;

co wyjaśnia, dlaczego obcięcie: opcje muszą być również przekazane do cl_optimization, który używa a, charaby zaoszczędzić miejsce. Tak więc 255 jest właściwie wewnętrznym maksimum.

opts.c: może_default_options

Wracając do opts.c:default_options_optimization, natrafiliśmy na to, maybe_default_optionsco brzmi interesująco. Wchodzimy do niego, a potem maybe_default_optiondocieramy do dużego przełącznika:

switch (default_opt->levels)
  {

  [...]

  case OPT_LEVELS_1_PLUS:
    enabled = (level >= 1);
    break;

  [...]

  case OPT_LEVELS_3_PLUS:
    enabled = (level >= 3);
    break;

Nie ma >= 4kontroli, co oznacza, że 3jest to możliwie największe.

Następnie szukamy definicji OPT_LEVELS_3_PLUSw 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.  */
};

Ha! To mocny wskaźnik, że istnieją tylko 3 poziomy.

opts.c: default_options_table

opt_levelsjest tak interesujący, że grepujemy OPT_LEVELS_3_PLUSi natrafiamy na 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 },
    [...]
}

więc w tym miejscu -Onjest kodowane mapowanie optymalizacyjne do określonego, o którym mowa w dokumentacji. Miły!

Upewnij się, że x_optimize nie ma już zastosowań

Głównym zastosowaniem x_optimizebyło ustawienie innych specyficznych opcji optymalizacji, takich -fdefer_popjak udokumentowane na stronie podręcznika. Czy jest ich więcej?

My grepi znajdujemy kilka innych. Liczba jest niewielka i po ręcznym sprawdzeniu widzimy, że każde użycie powoduje co najwyżej a x_optimize >= 3, więc nasz wniosek jest ważny.

lto-wrapper.c

Teraz przejdźmy do drugiego wystąpienia OPT_O, które było w lto-wrapper.c.

LTO oznacza optymalizację czasu łącza, która, jak nazwa sugeruje, będzie potrzebować -Oopcji i będzie połączona z collec2(co jest w zasadzie konsolidatorem).

W rzeczywistości pierwsza linijka lto-wrapper.cmówi:

/* Wrapper to call lto.  Used by collect2 and the linker plugin.

W tym pliku OPT_Owystąpienia wydają się tylko normalizować wartość, Oaby przekazać ją dalej, więc powinno być dobrze.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
źródło
38

Siedem różnych poziomów:

  • -O0 (domyślnie): brak optymalizacji.

  • -Olub -O1(to samo): Optymalizuj, ale nie trać zbyt wiele czasu.

  • -O2: Bardziej agresywna optymalizacja

  • -O3: Optymalizuj najbardziej agresywnie

  • -Ofast: Odpowiednik -O3 -ffast-math. -ffast-mathwyzwala niezgodne ze standardami optymalizacje zmiennoprzecinkowe. Pozwala to kompilatorowi udawać, że liczby zmiennoprzecinkowe są nieskończenie dokładne, a ich algebra jest zgodna ze standardowymi zasadami algebry liczb rzeczywistych. Mówi również kompilatorowi, aby nakazał sprzętowi opróżnienie denormali do zera i traktowanie denormali jako zero, przynajmniej na niektórych procesorach, w tym x86 i x86-64. Denormale wyzwalają powolną ścieżkę na wielu FPU, więc traktowanie ich jako zero (co nie wyzwala wolnej ścieżki) może być dużą wygraną w wydajności.

  • -Os: Optymalizacja pod kątem rozmiaru kodu. W niektórych przypadkach może to faktycznie zwiększyć szybkość dzięki lepszemu zachowaniu pamięci podręcznej I.

  • -Og: Optymalizuj, ale nie koliduj z debugowaniem. Umożliwia to niezakłócającą wydajność kompilacji debugowania i ma zastąpić -O0kompilacje debugowania.

Istnieją również inne opcje, które nie są włączane przez żadną z nich i muszą być włączone oddzielnie. Możliwe jest również użycie opcji optymalizacji, ale wyłącz określone flagi włączone przez tę optymalizację.

Więcej informacji można znaleźć na stronie internetowej GCC.

Demi
źródło
Rzeczywiście, chociaż aby być uczciwym w stosunku do innych odpowiedzi, ani -Ofast, ani -Og nie istniały, gdy te odpowiedzi były pisane.
janneb
Dlaczego więc -O100kompiluje się?
einpoklum
3
@einpoklum, ponieważ GCC traktuje wszystko powyżej -O3 jako równe -O3.
Demi
Niestety, nadal masz mnóstwo <zoptymalizowanych wyjść> w debugerze z -Og. Stepping nadal przeskakuje losowo. To bezużyteczne IMHO.
doug65536
3

Cztery (0-3): Patrz GCC 4.4.2 instrukcji . Cokolwiek wyższe to tylko -O3, ale w pewnym momencie przekroczysz limit rozmiaru zmiennej.

Tomek
źródło
W mojej odpowiedzi zbadałem kod źródłowy i zgadzam się z tobą. Bardziej pedantycznie, GCC wydaje się polegać na atoiniezdefiniowanym zachowaniu, po którym następuje 255wewnętrzny limit.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
4
Rozważ usunięcie swojej odpowiedzi, ponieważ jest ona (przynajmniej obecnie) nieprawidłowa.
einpoklum