G++ optimization beyond-O3/ - Ofast

Problem

Mamy średniej wielkości program do zadania symulacyjnego, który musimy zoptymalizować. Zrobiliśmy już wszystko, co w naszej mocy, optymalizując źródło do granic naszych umiejętności programistycznych, w tym profilowanie za pomocą Gprof i Valgrind .

Kiedy wreszcie skończymy, chcemy uruchomić program na kilku systemach prawdopodobnie przez kilka miesięcy. Dlatego jesteśmy naprawdę zainteresowani, aby popchnąć optymalizację do granic możliwości.

Wszystkie systemy będą działać Debian / Linux na stosunkowo nowym sprzęcie (Intel i5 lub i7).

Pytanie

jakie są możliwe opcje optymalizacji przy użyciu najnowszej wersji g++, które wykraczają poza -O3 / - Ofast?

Jesteśmy również zainteresowani kosztowną, drobną optymalizacją, która będzie wypłacana w dłuższej perspektywie.

Czego teraz używamy

W tej chwili używamy następujących opcji optymalizacji g++:

  • -Ofast: najwyższy" standardowy " poziom optymalizacji. Na Zawarte -ffast-math nie sprawiały żadnych problemów w naszych obliczeniach, więc zdecydowaliśmy się na to, pomimo niestandardowej zgodności.
  • -march=native: umożliwia korzystanie ze wszystkich instrukcji specyficznych dla procesora.
  • -flto aby umożliwić optymalizację czasu łącza, w różnych jednostkach kompilacji.
Author: Haatschii, 2013-01-24

8 answers

Większość odpowiedzi sugeruje alternatywne rozwiązania, takie jak różne Kompilatory lub zewnętrzne biblioteki, które najprawdopodobniej przyniosłyby wiele pracy nad przepisaniem lub integracją. Postaram się trzymać tego, co zadaje pytanie, i skupić się na tym, co można zrobić z samym GCC, aktywując flagi kompilatora lub wykonując minimalne zmiany w kodzie, zgodnie z życzeniem OP. to nie jest odpowiedź "musisz to zrobić", ale bardziej zbiór poprawek GCC, które dobrze się dla mnie sprawdziły i które można wykonać. spróbuj, jeśli są one istotne w konkretnym kontekście.


Ostrzeżenia dotyczące pytania pierwotnego

Zanim przejdziemy do szczegółów, kilka ostrzeżeń dotyczących pytania, zazwyczaj dla osób, które przyjdą, przeczytają pytanie i powiedzą: "OP optymalizuje się poza O3, powinienem użyć tych samych FLAG niż on!".

  • -march=native umożliwia użycie instrukcji specyficznych dla danej architektury procesora , A niekoniecznie dostępne na innej architekturze. Program może nie działać w ogóle, jeśli działa na systemie z innym procesorem lub jest znacznie wolniejszy (ponieważ umożliwia to również mtune=native), więc pamiętaj o tym, jeśli zdecydujesz się go użyć. Więcej informacji tutaj .
  • -Ofast, Jak stwierdziłeś, umożliwia pewne niestandardowe optymalizacje zgodne z , więc powinien być również używany z ostrożnością. Więcej informacji tutaj .

Inne flagi GCC do wypróbowania

The szczegóły dla różnych flag są wymienione tutaj .

  • -Ofast włącza -ffast-math, co z kolei umożliwia -fno-math-errno, -funsafe-math-optimizations, -ffinite-math-only, -fno-rounding-math, -fno-signaling-nans i -fcx-limited-range. Możesz pójść jeszcze dalej na optymalizacje obliczeń zmiennoprzecinkowych poprzez selektywne dodanie niektórych dodatkowych znaczników, takich jak -fno-signed-zeros, -fno-trapping-math i inni Nie są one zawarte w -Ofast i mogą dać dodatkowe zwiększenie wydajności w obliczeniach, ale musisz sprawdzić, czy rzeczywiście korzystają z Ciebie i nie przerywaj żadnych obliczeń.
  • GCC posiada również dużą ilość innych znaczników optymalizacji , które nie są włączone przez żadne Opcje "-O". Są one wymienione jako "opcje eksperymentalne, które mogą wytwarzać uszkodzony kod", więc ponownie, powinny być używane z ostrożnością, a ich efekty sprawdzane zarówno przez testowanie poprawności, jak i benchmarking. Niemniej jednak często używam -frename-registers, opcja ta nigdy nie przyniosła niechcianych rezultatów dla mnie i ma tendencję do zauważalnego wzrostu wydajności (tj. można zmierzyć przy benchmarkingu). Jest to typ flagi, która jest bardzo zależna od procesora. -funroll-loops czasami daje dobre wyniki( a także implikuje -frename-registers), ale zależy to od rzeczywistego kodu.

PGO

GCC ma optymalizacje profilowe funkcje. Nie ma na ten temat zbyt dużej dokładnej dokumentacji GCC, ale mimo to uruchomienie go jest dość proste.

  • najpierw skompiluj swój program z -fprofile-generate.
  • niech program uruchomi się (czas wykonania będzie znacznie wolniejszy, ponieważ kod generuje również informacje o Profilu .pliki gcda).
  • przekompiluj program za pomocą -fprofile-use. Jeśli Twoja aplikacja jest wielowątkowa, Dodaj również flagę -fprofile-correction.

PGO z GCC może dać niesamowite rezultaty i naprawdę znacznie zwiększyć wydajność(widziałem 15-20% wzrost prędkości na jednym z projektów, nad którymi ostatnio pracowałem). Oczywiście chodzi o to, żeby mieć jakieś dane, które są wystarczająco reprezentatywne dla wykonania aplikacji, które nie zawsze są dostępne lub łatwe do uzyskania.

GCC ' s Parallel Mode

GCC posiada tryb równoległy , który został po raz pierwszy wydany w czasie, gdy kompilator gcc 4.2 był wyłączony.

Zasadniczo dostarcza równoległych implementacji wielu algorytmów w bibliotece standardowej C++ . Aby włączyć je globalnie, wystarczy dodać -fopenmp i -D_GLIBCXX_PARALLEL flagi do kompilatora. W razie potrzeby można również selektywnie włączyć każdy algorytm, ale będzie to wymagało drobnych zmian w kodzie.

Wszystkie informacje o tym trybie równoległym można znaleźć tutaj .

Jeśli często używasz tych algorytmów na dużych strukturach danych i masz wiele kontekstów wątków sprzętowych, te równoległe implementacje mogą znacznie zwiększyć wydajność. Korzystałem tylko z równoległej implementacji sort do tej pory, ale aby dać przybliżony pomysł, udało mi się skrócić czas sortowania z 14 do 4 sekund w jednej z moich aplikacji (środowisko testowe: wektor 100 milionów obiektów z niestandardową funkcją komparatora i maszyna 8 rdzeniowa).

Dodatkowe triki

W przeciwieństwie do poprzednich sekcji punktów, ta część wymaga niewielkich zmian w kodzie . Są również specyficzne dla GCC( niektóre z nich działają również na Clang), więc makra czasu kompilacji powinny być używane, aby zachować kod Przenośny na innych kompilatorach. Ta sekcja zawiera kilka bardziej zaawansowanych technik i nie powinna być używana, jeśli nie masz pewnego poziomu zrozumienia tego, co się dzieje. Zauważ również, że procesory i kompilatory są obecnie dość inteligentne, więc może to być trudne, aby uzyskać zauważalne korzyści z funkcji opisanych tutaj.

  • GCC builtins, które są wymienione tutaj . Konstrukty takie jak __builtin_expect mogą pomóc kompilatorowi w lepszej optymalizacji, dostarczając mu przewidywanie gałęzi Informacje. Inne konstrukcje, takie jak __builtin_prefetch, przenoszą dane do pamięci podręcznej przed jej dostępem i mogą pomóc zmniejszyć liczbę błędów pamięci podręcznej .
  • atrybuty funkcji, które są wymienione tutaj . W szczególności powinieneś przyjrzeć się atrybutom hot i cold; pierwszy z nich wskaże kompilatorowi, że funkcja jest hotspot programu i zoptymalizować funkcję bardziej agresywnie i umieścić ją w specjalnym podrozdziale sekcja tekstowa, dla lepszej lokalizacji; im później zoptymalizuje funkcję pod względem rozmiaru i umieści ją w innej specjalnej sekcji sekcji tekstowej.

Mam nadzieję, że ta odpowiedź okaże się przydatna dla niektórych programistów i z przyjemnością rozważę wszelkie zmiany lub sugestie.

 38
Author: Pyves,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-08 22:19:20

Stosunkowo nowy sprzęt (Intel i5 lub i7)

Dlaczego nie zainwestować w kopię kompilatora Intela i wysokowydajnych bibliotek? Może przewyższyć GCC w zakresie optymalizacji o znaczną marżę, zazwyczaj od 10% do 30% lub nawet więcej, a nawet więcej w przypadku ciężkich programów do łamania numerów. Intel oferuje również szereg rozszerzeń i bibliotek dla wysokowydajnych aplikacji do przetwarzania liczb (równoległych), jeśli możesz sobie na to pozwolić Twój kod. Może się opłacić, jeśli skończy się to oszczędnością miesięcy czasu pracy.

Zrobiliśmy już wszystko, co w naszej mocy, optymalizując źródło do granic naszych umiejętności programistycznych

Z mojego doświadczenia wynika, że mikro - i nano-optymalizacje, które zazwyczaj wykonuje się z pomocą profilera, mają słaby zwrot w czasie-inwestycje w porównaniu z makro-optymalizacjami (usprawniającymi strukturę kodu) i, co najważniejsze i często pomijane, dostęp do pamięci optymalizacje (np. lokalizacja odniesienia, przejście w kolejności, minimalizacja indrection, dzierżenie pamięci podręcznej, itp.). Ta ostatnia zwykle polega na zaprojektowaniu struktur pamięci, aby lepiej odzwierciedlały sposób wykorzystania pamięci (przemierzania). Czasami może to być tak proste, jak przełączenie typu kontenera i uzyskanie ogromnego zwiększenia wydajności. Często w przypadku profilerów gubisz się w szczegółach optymalizacji instrukcji po instrukcji, a problemy z układem pamięci nie pojawiają się i są zwykle brakuje, gdy zapomina spojrzeć na szerszą perspektywę. Jest to znacznie lepszy sposób na zainwestowanie swojego czasu, a wypłaty mogą być ogromne(np. wiele algorytmów o(logN) kończy się wykonywaniem prawie tak powolnym jak O (N) tylko z powodu słabych układów pamięci (np. użycie listy połączonej lub drzewa połączonego jest typowym winowajcą ogromnych problemów z wydajnością w porównaniu do ciągłej strategii przechowywania)).

 19
Author: Mikael Persson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-24 02:16:40

Jeśli cię na to stać, spróbuj VTune . Dostarcza o wiele więcej informacji niż proste próbkowanie (dostarczone przez gprof, o ile wiem). Możesz spróbować Code Analyst. Ten ostatni jest przyzwoitym, wolnym oprogramowaniem, ale może nie działać poprawnie (lub w ogóle) z procesorami Intela.

Będąc wyposażonym w takie narzędzie, pozwala sprawdzić różne miary, takie jak wykorzystanie pamięci podręcznej (a w zasadzie układ pamięci), które - jeśli są używane w pełni - zapewnia ogromny impuls do wydajność.

Gdy masz pewność, że algorytmy i struktury są optymalne, zdecydowanie powinieneś użyć wielu rdzeni na i5 i i7. Innymi słowy, pobaw się różnymi algorytmami/wzorcami programowania równoległego i sprawdź, czy możesz przyspieszyć.

Jeśli masz naprawdę równoległe dane (struktury podobne do tablic, na których wykonujesz podobne / te same operacje), powinieneś wypróbować instrukcje OpenCL i SIMD(łatwiejsze do skonfigurowania).

 6
Author: Red XIII,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-24 10:17:28

Huh, ostatnia rzecz, którą możesz spróbować: acovea Projekt: Analiza optymalizacji kompilatora za pomocą algorytmu ewolucyjnego - jak widać z opisu, próbuje algorytmu genetycznego, aby wybrać najlepsze opcje kompilatora dla Twojego projektu (robi kompilację maaany razy i sprawdzić czas, dając informację zwrotną do algorytmu :) -- ale wyniki mogą być imponujące! :)

 4
Author: zaufi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-28 11:33:18

Kilka uwag na temat aktualnie wybranej odpowiedzi (nie mam jeszcze wystarczającej ilości punktów reputacji, aby dodać to jako komentarz):

Odpowiedź mówi:

-fassociative-math, -freciprocal-math, -fno-signed-zeros, i -fno-trapping-math. Nie są one zawarte w -Ofast i mogą dać dodatkowe zwiększenie wydajności w obliczeniach

Być może było to prawdą, gdy odpowiedź została opublikowana, ale dokumentacja GCC mówi, że wszystkie z nich są włączone przez -funsafe-math-optimizations, co jest włączone przez -ffast-math, co jest enabled by -Ofast. Można to sprawdzić za pomocą polecenia gcc -c -Q -Ofast --help=optimizer, które pokazuje, które optymalizacje są włączone przez -Ofast i potwierdza, że wszystkie z nich są włączone.

Odpowiedź mówi również:

Inne znaczniki optymalizacji, które nie są włączone przez żadne Opcje "-O"... -frename-registers

Ponownie, powyższe polecenie pokazuje, że przynajmniej w moim GCC 5.4.0, {[10] } jest domyślnie włączone przez -Ofast.

 2
Author: user3708067,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-29 10:48:25

Trudno odpowiedzieć bez dalszych szczegółów:

  • Jaki rodzaj liczby?
  • jakich bibliotek używasz?
  • Jaki stopień paralelizacji?

Czy możesz zapisać część kodu, która trwa najdłużej? (Typowo ciasna pętla)

Jeśli jesteś związany CPU odpowiedź będzie inna niż Jeśli jesteś związany IO.

Ponownie, proszę podać dalsze szczegóły.

 1
Author: Escualo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-24 03:25:06

Polecam przyjrzeć się rodzajowi operacji, które kosztują ciężkie podnoszenie, i poszukać zoptymalizowanej biblioteki. Istnieje sporo szybkich, zoptymalizowanych do montażu, wektoryzowanych bibliotek SIMD dla typowych problemów (głównie matematycznych). Odkrywanie koła na nowo jest często kuszące, ale zwykle nie jest warte wysiłku, jeśli istniejące soltuion może pokryć twoje potrzeby.Ponieważ nie podałeś, jaki to rodzaj symulacji mogę podać tylko niektóre przykłady.

Http://www.yeppp.info/

Http://eigen.tuxfamily.org/index.php?title=Main_Page

Https://github.com/xianyi/OpenBLAS

 0
Author: uLoop,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-16 22:01:45

Z GCC Intel turn of / implementation-fno-gcse (działa dobrze na gfortran) i-fno-guess-branch-prbability (domyślnie w gfortran)

 -2
Author: xTrameshmen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-11 10:07:42