GCC i flagi kompilacji
Opublikował/a thindil w dniu 20 Lipiec 2006
Na temat kolekcji kompilatorów GCC oraz flag kompilacji napisano już chyba parę książek – a mimo to nadal wydaje się że temat nie został do końca omówiony. Mam nadzieję że ten wpis pomoże paru osobom dobrać flagi kompilacji do własnych potrzeb.
Flagi kompilacji GCC można podzielić na kilka grup, poniżej przedstawiam te najważniejsze (moim zdaniem)
Optymalizacja kodu
Do optymalizacji kodu wykonywalnego programu służy flagi zaczynające się od znacznika -O. Należy wziąć pod uwagę iż taka optymalizacja wydłuża czas kompilacji programu, ale za to może przyspieszyć jego późniejsze działanie (oraz zmniejszyć zużycie zasobów komputera)
- -O1 – minimalna optymalizacja – można powiedzieć że kod wynikowy nie jest prawie w ogóle optymalizowany. W dzisiejszych czasach flaga ta praktycznie nie jest używana, gdyż jej użycie niewiele daje. Wspominam o niej tylko ze względów porządkowych
- -O2 – najczęściej obecnie wykorzystywana flaga – kompilator optymalizuje pod względem rozmiaru kodu wynikowego. Jest to można by rzec neutralna flaga, najlepiej przetestowana, przy jej użyciu nie powinny pojawiać się jakiekolwiek problemy w działaniu programu z dobrze napisanym kodem.
- -O3 – najsilniejsza optymalizacja (ponoć). Kod wynikowy jest optymalizowany pod kątem zużycia pamięci oraz zasobów procesora. Niestety nie jest optymalizowany pod kątem rozmiaru. Efekt jest taki że bardzo często ta flaga ma znacznie gorsze wyniki niż -O2 – właśnie ze względu na puchnięcie kodu
- -Os – coś pośredniego pomiędzy -O2 a -O3. Kod jest optymalizowany jednocześnie pod kątem rozmiaru jak również zużycia zasobów. Niestety kompilacja z użyciem tej flagi jest najwolniejsza ze wszystkich wcześniej opisanych opcji. Dodatkowo zdarza się że niektóre programy nie chcą działać z tą flagą (dotyczy to szczególnie pakietów Binutils jak i GCC
Architektura procesora
Do optymalizacji kodu wynikowego pod kątem procesora służą flagi -march= oraz -mtune=. Po znaku równości podajemy typ procesora dla jakiego chcemy optymalizować kod.
- -march= – użycie tej flagi oznacza że kod wynikowy programu będzie skompilowany tylko i wyłącznie pod dany typ procesora. Zgodność z innymi typami procesorów zostanie usunięta. Gdybyśmy np skompilowali program pod pentium4 a próbowali następnie uruchomić pod amd64 taki program niestety nie zadziała. Jednak na procesorze dla jakiego nastąpiła kompilacja dany program będzie działał maksymalnie wydajnie (będzie wykorzystywał procesor do ostatniego taktu).
- -mtune= – użycie tej flagi działa podobnie jak do poprzedniej, jednak w tym wypadku kompilator nie usunie zgodności z innymi typami procesorów. Nasz kod będzie działa (oczywiście jeżeli jest dobrze napisany) pod innymi procesorami. Są jednak dwa ale: po pierwsze – na procesorze dla którego dany program został skompilowany, jego działanie będzie minimalnie wolniejsze niż w przypadku użycia pierwszej flagi. Na dodatek kod nie będzie zoptymalizowany dla pozostałych architektur, stąd też będzie działał znacznie wolniej
Dodatkowe flagi
Oprócz wyżej wymienionych GCC posiada również wiele innych flag możliwych do włączenia podczas kompilacji. Część jest dostępna dla wszystkich procesorów, cześć tylko dla wybranych architektur. Poniżej przedstawiam kilka z nich:
- -pipe – wymusza użycie tzw rurek podczas kompilacji. Aby nie wdawać się w szczegóły, ogólnie chodzi o to, że kompilator podczas kompilacji bardziej namiętnie korzysta z pamięci komputera niż z dysku. Ta flaga przyspiesza nieco proces kompilacji. Jest to flaga bezpieczna, nie powinno być problemów z kompilacją jakiegokolwiek programu przy jej użyciu. Jedyny minus jak już wspomniałem to większe zużycie pamięci
- -fomit-frame-pointer – poprzez wykonanie pewnych operacji na kodzie flaga ta znacznie lepiej optymalizuje kod wynikowy programu. Niestety jej główną wadą jest fakt, że tak skompilowanego programu nie można potem odpalić pod debuggerem (np GDB). Jeżeli ktoś kompiluje program w celach wyłącznie użytkowych, może bez problemu użyć tej flagi podczas kompilacji. Jeżeli jednak chcemy modyfikować kod programu bądź też po prostu go diagnozować podczas działania lepiej jest nie używać tej flagi
Kilka rad
Kiedy piszesz flagi dla kompilatora GCC pamiętaj o zasadzie KISS (Keep It Simple Stupid), czyli po polsku BUZI (Bez Udziwnień Zapisuj Idioto):
- Nie wpisuj kilkunastu różnych flag – nie ma większego sensu. Nie dość że może to wydłużyć czas kompilacji programu, to jeszcze potem może okazać się, że sam program nie działa
- Nie używaj flag typu -mmx i tym podobnych. Tego typu flagi są domyślnie włączone dla procesorów które potrafią obsługiwać dane rozszerzenia. Po prostu taki zapis to masło maślane, które na dodatek nie daje jakichkolwiek bonusów
- Kiedy używasz flag -march= bądź -mtune= wpisuj dokładną nazwę swojego procesora zamiast jego grupy (czyli np -march=pentium4 a nie -march=i686). Jeżeli już optymalizować kod, to optymalizować go maksymalnie a nie tylko kawałek.
- Nie traktuj tego wpisu jak prawdy ostatecznej – to tylko moje spostrzeżenia na temat flag kompilacji, jeżeli sam masz inne doświadczenia zachęcam do dyskusji – być może w końcu uda się napisać dobry opis flag GCC zrozumiały również dla zwykłego człowieka
To tyle jeżeli chodzi o flagi GCC. Ten wpis oczywiście nie jest całkowitym wyczerpaniem tematu. Więcej na temat flag kompilacji (w tym również specjalnych flag dla danego typu procesora) znaleźć można na stronie podręcznika GCC 4.0 (po angielsku)