Forum Dyskusyjne
Zaloguj Rejestracja Szukaj Forum dyskusyjne

Forum dyskusyjne -> Software -> Linux -> opcje kompilatora
Napisz nowy temat  Odpowiedz do tematu
opcje kompilatora
PostWysłano: Sobota, 20 Grudnia 2003, 20:04 Odpowiedz bez cytowania Odpowiedz z cytatem
bbkr
Stały bywalec
<tt>Stały bywalec</tt>
 
Użytkownik #22
Posty: 103


[ Osobista Galeria ]




dzieki jakim ustawieniom da sie osiagnac maxymalna optymalizacje kompilowanych programow?? obecnie mam wpisane w .bash_profile

export CC=gcc
export CXX=g++
export CFLAGS="-march=pentium4 -O2 -mcpu=pentium4"
export CXXFLAGS=$CFLAGS

cos mozna tu jeszcze ulepszyc?? i do czego jest parametr "-O" i "-pedantic" ?? w man'ie sie nie doszukalem odpowiedzi.
  
Re: opcje kompilatora
PostWysłano: Wtorek, 30 Grudnia 2003, 23:17 Odpowiedz bez cytowania Odpowiedz z cytatem
trikster
Czytelnik
<tt>Czytelnik</tt>
 
Użytkownik #552
Posty: 3


[ Osobista Galeria ]




http://lubuska.zapto.org/~hoppke/too_much_to_learn/kompilat.html
  
Re: <trikster> opcje kompilatora
PostWysłano: Środa, 07 Stycznia 2004, 04:32 Odpowiedz bez cytowania Odpowiedz z cytatem
bbkr
Stały bywalec
<tt>Stały bywalec</tt>
 
Użytkownik #22
Posty: 103


[ Osobista Galeria ]




wielkie dzieki. super artykul.

teraz strippuje wszystko co sie da. niektore binarki strippuja sie o 75%!! np. binarke XMMS mozna zestripowac z 4 MB na 1 MB.

dopiero po przeczytaniu tego artykulu uswiadomilem sobie, jak nieprofesjonalnie postawilem system. jak wyjdzie slack 10 na kernelu 2.60 to zrobie taki systemik optymalny, ze windows 3.11 bedzie przy nim mulowaty.

jeszcze raz dzieki.
  
Re: <trikster> opcje kompilatora
PostWysłano: Środa, 07 Stycznia 2004, 10:23 Odpowiedz bez cytowania Odpowiedz z cytatem
e X t 7 3
Stały uczestnik
Stały uczestnik
 
Użytkownik #67
Posty: 854


[ Osobista Galeria ]




rzeczywiście REWELACYJNY artykuł ... dzięki i tylko prosimy o jeszcze takiej wiedzy wink.gif
  
Re: opcje kompilatora
PostWysłano: Niedziela, 11 Stycznia 2004, 12:43 Odpowiedz bez cytowania Odpowiedz z cytatem
e X t 7 3
Stały uczestnik
Stały uczestnik
 
Użytkownik #67
Posty: 854


[ Osobista Galeria ]




po stripowaniu gimpa-1.3 zaoszczędziłem 44.6 MB !!!!! zmalał bowiem z 47 MB do 2.4 MB !!!!!! wink.gif wink.gif cool.gif

użyłem składni:

strip -R .comment -R .note -R .note.ABI-tag gimp-1.3

eusa_clap.gif
  
Re: <e X t 7 3> opcje kompilatora
PostWysłano: Niedziela, 07 Marca 2004, 00:47 Odpowiedz bez cytowania Odpowiedz z cytatem
bbkr
Stały bywalec
<tt>Stały bywalec</tt>
 
Użytkownik #22
Posty: 103


[ Osobista Galeria ]




o, fajnie ze post zostal przyklejony. przy kazdym stawianiu lina musialem go szukac, zeby wygrzebac link z opisem tych opcji.
  
Re: <bbkr> opcje kompilatora
PostWysłano: Niedziela, 07 Marca 2004, 02:32 Odpowiedz bez cytowania Odpowiedz z cytatem
EDDY
Admin
 
Użytkownik #1
Posty: 1451


[ Osobista Galeria ]




Nie ma za co wink.gif
Poza tym jest opcja Ulubione, gdzie mozna trzymac wazne tematy pod reka (opcja dodania na dole po prawej w watku).
  
Re: opcje kompilatora
PostWysłano: Sobota, 07 Stycznia 2006, 14:06 Odpowiedz bez cytowania Odpowiedz z cytatem
if22
Bywalec
<tt>Bywalec</tt>
 
Użytkownik #2130
Posty: 9


[ Osobista Galeria ]




Tylko szkoda, ze już nie ma tego ortykułu.
  
Re: <if22> opcje kompilatora
PostWysłano: Sobota, 07 Stycznia 2006, 17:06 Odpowiedz bez cytowania Odpowiedz z cytatem
EDDY
Admin
 
Użytkownik #1
Posty: 1451


[ Osobista Galeria ]




Jest na nowej stronie http://dobremiasto.net/~hoppke/too_much_to_learn/kompilat.html

Wklejam całość dla potomnych na wszelki wypadek wink.gif :

Cytat:

Kompilowanie oprogramowania

Jedną z rzeczy które najlepiej robić samemu, jest kompilacja oprogramowania. Pakiety dystrybucyjne zwykle nie są specjalnie dostosowane do komputera na którym przyszło im działać - muszą być w końcu dosyć uniwersalne, jak sama dystrybucja zresztą. A tak się składa, że końcowemu użytkownikowi więcej korzyści przyniesie jednak "specjalizacja" pakietów. W końcu nie zależy mu zwykle na tym, by pakiet działał na maszynach klasy 386. Bardziej liczy się to, by program wykorzystał w pełni możliwości jego Celerona.

Ale jeszcze garść faktów:
Kod:
Własnoręczne kompilowanie może być pracochłonne i dosyć kłopotliwe...Wymaga zdobycia pewnej wiedzy zanim zacznie przynosić większe korzyści...W zamian pozwoli uczynić oprogramowanie szybszym i zużywającym mniej pamięci operacyjnej!Pozwoli także lepiej zestroić współgranie poszczególnych pakietów oraz dokroić niektóre pakiety do naszych potrzeb!No i najważniejsze - laski na to lecą ;)


No to jak? Zaczynamy?

"Rodzaje" optymalizacji:

Samodzielna kompilacja nie ma zwykle sensu jeśli nie jest połączona z jakimś świadomie wybranym modelem optymalizacji kodu. Generalizując można stwierdzić, że istnieją dwie główne linie optymalizacji - a pierwsza z nich to optymalizacja "pod procesor". GCC potrafi całkiem zręcznie wykorzystać cechy specyficzne dla poszczególnych generacji procesorów, np. obecność dodatkowych rejestrów czy nowe instrukcje. Pozbawia to kod zwykle "kompatybilności wstecznej", ale kogo to tak naprawdę obchodzi?

Drugi rodzaj optymalizacji to optymalizacja "ogólna" - zaliczają się tu techniki rozwijania pętli, instrukcji warunkowych, zmienianie układu kodu, kolejkowania instrukcji itp. Często będzie to też w jakiś sposób połączone z typem docelowego procesora, ale duża część opcji jest niezależna od generacji CPU.

Podczas gdy optymalizacja "pod procesor" jest bardzo prosta - wystarczy wybrać taki model, jakiego używamy - to już optymalizacja "ogólna" nie jest taka jednoznaczna. Często może zależeć od kodu który chcemy zoptymalizować. Np. może się okazać, że to co doskonale optymalizowało kod jakiegoś windowmanagera zupełnie nie sprawdza się w przypadku enkodera ogg. Na szczęście 99% przypadków można objąć w miarę optymalnymi i uniwersalnymi ustawieniami. Więc nie jest źle.

Skoncentruję się tutaj na kompilatorach GCC serii 3.x. Wydaje mi się, że kompilatory te już osiągnęły wystarczająco wysoki poziom stabilności by można było zapomnieć o serii 2.x. Zwłaszcza że gcc-2.x nie potrafią optymalizować kodu pod współczesne procesory, zatrzymując się w okolicach Pentium Pro. Więc opieram się o gcc3 i polecam to też innym. Zresztą gcc3 to już praktycznie sine qua non - np. najnowsze glibc już nie dadzą się skompilować niczym z gałęzi gcc-2.x. A to chyba najlepsza oznaka tego, że gcc-2.x odchodzi do lamusa.

Optymalizację kompilatora wybiera się za pomocą tzw. flag. Flagi to po prostu opcje przekazywane w linii wywołania gcc i masz ich całe mnóstwo do swojej dyspozycji. Na szczęście wystarczy znać tylko kilka-kilkanaście by wydusić z kodu i swojej maszyny 99% wydajności. To dobrze :)

Opcje można przekazywać ręcznie, ale w tak szalenie zautomatyzowanych procesach budowy jakie widzi się dzisiaj jest to zwykle niemożliwe. Dlatego używa się pewnych standardowych zmiennych środowiskowych - wystarczy je ustawić zgodnie z naszymi potrzebami, a 80% budowanych pakietów się do nich dostosuje. Zmienne te to CFLAGS, CXXFLAGS, CPPFLAGS oraz LDFLAGS. Istnieją też inne zmienne, ale te są najważniejsze. CFLAGS to flagi używane przy kompilacji kodu C. CXXFLAGS to flagi dla kodu C++. CPPFLAGS to flagi dla cpp - tzw. "preprocesora" kodu w C - i o tej zmiennej akurat można zapomnieć. A ostatni zawodnik - LDFLAGS - zawiera opcje przekazywane linkerowi, czyli temu programowi który łączy wszystkie cząstkowe produkty kompilatora (tzw. "object files", pliki z rozszerzeniem "*.o") w jeden wyjściowy obiekt - program lub bibliotekę.

Pierwsze opcje jakie warto ustawić to '-march= ' oraz '-mcpu= '. Decydują one odpowiednio o zestawie używanych instrukcji oraz o ułożeniu wykonywanych instrukcji. Ludzie obeznani z asemblerem wiedzą o co chodzi, pozostałym musi wystarczyć świadomość, że w ten sposób wykorzystuje się specyficzne cechy danego procesora. A, jeszcze coś - jeśli ustawi się opcję '-march= ', to opcja '-mcpu= ' jest ustawiana "automatycznie".

Flagi te przyjmują (czy też mogą przyjąć) w mojej wersji gcc następujące wartości:
Kod:
i386, i486, i586, i686,pentium, pentium-mmx, pentiumpro, pentium2, pentium3, pentium4,k6, k6-2, k6-3,athlon, athlon-tbird, athlon-4, athlon-xp, athlon-mp


Jak widać, wybór jest dość pokaźny i chyba wystarczający jak na obecne czasy. Można wybrać generację procesora (np. 'i686'), można też (i to jest ten zalecany sposób) wybrać konkretny model.Ja mam Celerona. Mogę wybrać 'i686', ale to ograniczy gcc to stosowania optymalizacji ogólnych dla wszystkich procesorów szóstej generacji. Jeśli wybiorę 'pentium2', to zyskam dodatkowo te opcje, które wspiera Pentium2, a których nie wspierają analogiczne procesory AMD. I vice versa. A więc - nie wybieraj ogólnej opcji 'ix86', ale wybierz od razu to, co najbliższe twojemu procesorowi.

Czyli ja, z moim Celeronem, decyduję się na '-march=pentium2'.

A, istnieją jeszcze specjalne opcje '-mmmx, -m3dnow' itp., które włączają obsługę poszczególnych rozszerzeń zestawu instrukcji procesora. Ale jeśli poprawnie określimy '-march= ', to korzystanie np. z '-mmmx' będzie zbędne. Jedynym wyjątkiem jaki jest mi teraz znany i którym być może warto sobie zawracać głowę są niuanse związane z jednostką zmiennoprzecinkową w Athlonach i instrukcjami SSE. Ale że nie mam Athlona, to i nic nie doradzę.

Teraz przechodzimy do optymalizacji ogólnej. GCC posiada multum opcji których można użyć. Ale zwykle wystarczy użyć opcji '-O'. Włącza ona optymalizację. Dodatkowo jest ona stopniowana, tzn. istnieją opcje
Kod:
'-O, -O1, -O2, -O3, -Os, -O0'


'-O' i '-O1' oznaczają to samo - bazowa optymalizacja, zarówno wielkości kodu, jak i jego szybkości. Kompromis pomiędzy osiągami kodu a czasem kompilacji (bo im silniejsza optymalizacja, tym kompilator potrzebuje więcej czasu i pamięci na wypracowanie optymalnego rozwiązania)

'-O2' to chyba najpopularniejsza opcja. Dosyć silna optymalizacja, silniejsza niż '-O1'.

'-O3' to najsilniejsza i najagresywniejsza z automatycznych optymalizacji. W odróżnieniu od '-O2' nie stara się już specjalnie utrzymać kodu w rozsądnych granicach objętościowych, co oznacza że kod może się trochę rozpuchnąć.I to bez żadnej gwarancji, że będzie działał choć odrobinkę szybciej. Ba, w bardzo wielu przypadkach flaga ta przynosi odwrotny do oczekiwanego skutek - kod puchnie i staje się wolniejszy. Zaobserwowałem już nawet przypadki, gdy kod kompilowany z '-O3' był wolniejszy o około 10% (a czasem nawet więcej) od kodu '-Os'. Dotyczy to zwykle kodu pisanego w C++, ale równie często dotyka także zwykłego C. A więc '-O3' warto używać tylko tam, gdzie jesteśmy absolutnie pewni, że da to przyspieszenie. Taką pewność można zyskać tylko przez empiryczne testy, zwłaszcza że optymalizacje '-O3' są ściśle powiązane z architekturą posiadanego procesora... idzie tutaj przede wszystkim o wielkość pamięci cache procesora. Bo można wygenerować kod bardzo szybki (licząc w cyklach procesora), taki kod jednak będzie zwykle nieco większy od kodu "normalnego". Opcja -O3 robi właśnie to - włącza wszystkie możliwe optymalizacje, nawet te bardzo wątpliwe, kosztem wielkości kodu. I nie chodzi tu nawet o rosnące zużycie pamięci operacyjnej, to nie jest problemem. Problem to wielkość pamięci cache procesora. To, co znajdzie się w cache jest "najbliższe" procesorowi i może zostać najszybciej odczytane i wykonane. Z tego powodu dobrze jest, gdy jak najwięcej danych (rozkazów) mieści się w cache. Kod produkowany z opcją -O3 będzie może i teoretycznie szybszy, ale rozrośnięty, jest więc bardziej prawdopodobne, że jego wykonanie będzie wymagało większej liczby ładowania danych do pamięci cache, a operacje takie są bardzo kosztowne. W dużej liczbie przypadków niwelują zupełnie zyski z optymalizacji wprowadzonych przez -O3, często dodatkowo spowalniając wykonywanie kodu. To trochę jak bolid F1, który ma silnik szybszy niż konkurenci, ale musi częściej zatrzymywać się i tankować - więc w ogólnym wyścigu może przegrać przez te postoje. Dlatego kod teoretycznie szybszy lecz większy (O3) często wykonuje się wolniej, niż kod nie tak optymalny, lecz mniejszy objętościowo (Os). To złożona sztuka kompromisu. Wszystko zależy od posiadanego modelu procesora i konkretnego kodu.

Z tych powodów praktycznie nigdy nie używam '-O3' - jest to zbyt kłopotliwe. Taka optymalizacja "daje coś" bardzo rzadko (tylko w przypadku kilku procent pakietów), prawie zawsze powoduje rozrośnięcie się kodu wynikowego, a nawet gdy już coś da, to są to naprawdę znikome zyski. Dlatego za '-O3' warto się brać tylko w sytuacjach wyjątkowych. Np. z wszystkich używanych przeze mnie programów tylko jeden naprawdę zyskuje na '-O3' - jest nim potrace, program do konwertowania bitmap na obrazy wektorowe.To tylko moja opinia, ale z uwagi na naturę mojego systemu mam już bardzo duży bagaż doświadczeń związany z kompilowaniem pakietów i odpowiednie "eksperymenty" mogę śmiało liczyć w setkach - więc słowa te piszę obiektywnie, jako przedstawiciel statystyki :)

'-O0' wyłącza optymalizację :) Może się przydawać przy "kompilacjach testowych", gdy chodzi tylko o jak najszybsze przejście całego procesu kompilacji, bo z tą flagą gcc pracuje o wiele, wiele szybciej niż zwykle. Z drugiej strony czasem spotyka się kod który polega na optymalizacji i z '-O0' nie uzyska się poprawnego kompilatu.

I opcja najbardziej chyba interesująca, przynajmniej mnie: '-Os'. W zasadzie jest to '-O2', tyle że z dodatkowymi "bonusikami" które mają na celu dodatkowo zredukowanie wielkość wynikowego kodu. Moim skromnym zdaniem jest to ten poziom optymalizacji którego powinno się używać w codziennej pracy. Oferuje normalną optymalizację na poziomie 'O2', ale wynikowy kod może być (i zwykle będzie) pokaźnie mniejszy. A to przelicza się na zajętość pamięci. A im więcej wolnej pamięci, tym lepiej. '-Os' nie jest specjalnie popularne, a szkoda. Częściowym wytłumaczeniem może być to, że gcc-2.x nie potrafiły zrobić z tą flagą nic ciekawego. Ale gcc-3.x potrafi dzięki niej naprawdę zaoszczędzić kilkanaście procent objętości kodu. A to wartość nie do pogardzenia - ziarnko do ziarnka i w rezultacie ma się lżejszy, szybszy system.

Więc ja polecam '-Os' zamiast popularnego '-O2'.

Jeszcze ostatnie słówko odnośnie opcji '-O' - ta opcja to tak naprawdę coś w rodzaju makra - mianowicie użycie jej włącza zawsze pewien zestaw "niskopoziomowych" opcji gcc. Te "niskopoziomowe" flagi są zawsze dostępne tak czy siak (można je sobie ręcznie włączać/wyłączać), ale zwykle nie ma potrzeby się nimi zajmować, bo opcje '-O' oferują wystarczający poziom funkcjonalności. I wygodniej jest użyć po prostu '-Os' niż podać siedem różnych, długaśnych opcji. Które zrobią to samo.

Inne opcje codziennego użytku:'-pipe' - gcc będzie używać rurek zamiast plików tymczasowych przy wielu operacjach. Polecam, bo może to przyspieszyć niektóre kompilacje, a nie niesie z sobą żadnych złych skutków ubocznych (no, proces kompilacji zużyje nieco więcej pamięci, ale to nie są wartości o które trzeba się martwić). Opcja ta nie wpływa na jakość kodu wynikowego, ale warto ją mieć włączoną.

'-fomit-frame-pointer' - za dużo technikalii by trzeba opisywać. W skrócie: gcc użyje małej modyfikacji przy wywoływaniu funkcji, która pozwoli oszczędzić nieco czasu procesora (oraz pamięci i bajtów kodu). Kod wynikowy nie będzie się zapewne nadawał do uruchomienia pod debuggerem, ale kogo to obchodzi? Polecam włączyć tę opcję, bo niesie same korzyści. Niektórzy czasem ostrzegają o skutkach ubocznych, np. autorzy glibc. Ale to jest zwykłe "ja za to nie biorę odpowiedzialności" - mój system używa tej opcji na bardzo szeroką skalę i działa wyśmienicie, więc to chyba wystarczy za gwarancję. Chociaż zdarzają się działania "nieprzewidziane" - np. nie potrafiłem zmusić glibc-2.3.1 do poprawnej pracy po kompilacji z tą opcją - dodatek "linuxthreads" nie działa potem prawidłowo (prawdę mówiąc w ogóle nie działa - komponent "linuxthreads" trzeba kompilować bez tej flagi). Podobnie jest z rpm-4.1.0, tyle że tutaj jeszcze nie wyselekcjonowałem konkretnej części sprawiającej kłopoty. Być może w przyszłości ta niedogodność zostanie usunięta i sytuacja wróci do normy (kiedyś można było bez obaw kompilować glibc/rpm z użyciem '-fomit-frame-pointer'.

Aktualizacja z końcówki marca 2004:
No i faktycznie sytuacja się unormowała. Wraz z wejściem kernela 2.6.x obsługa wątków przez linuxthreads zostaje zarzucona, teraz wątkami zajmuje się biblioteka NPTL (Native POSIX Thread Library) współpracująca blisko z nowymi kernelami. Biblioteka ta bezproblemowo współpracuje z -fomit-frame-pointer. RPM tak samo - widać problemy z nim były po prostu cieniem problemów w linuxthreads. Nowe glibc które by używały NPTL nie są jeszcze oficjalnie dostępne, ja używam wersji z CVS.

Uwaga!!!
'-fomit-frame-pointer' nie nadaje się do stosowania z kodem C++.
Z jakiegoś niezrozumiałego dla mnie powodu kod C++ kompilowany z tą flagą potrafi się czasem znacznie rozrosnąć. Dlatego zalecam ograniczenie stosowania tej flagi do kodu pisanego w C. Dodatkowo w przypadku drobnego ułamka programów pisanych w C++ flaga ta, oprócz rozdmuchania rozmiaru wynikowego kodu, może dać w rezultacie kod wadliwy, tzn. taki, który nie będzie działał poprawnie lub nawet segfaultował. Dlatego stanowczo odradzam używania tej flagi przy kompilowaniu kodu C++ (np. aplikacji KDE czy Mozilli), przynajmniej dopóki nie zostanie to naprawione w GCC (ale błąd ten występuje już od dawna, więc pewnie to ostrzeżenie się tak szybko nie zdezaktualizuje). Jeśli ktoś ma jakieś w miarę aktualne wyniki z własnych obserwacji, potwierdzające lub negujące to co napisałem, to byłbym wdzięczny za kontakt.

'-ffast-math' - opcja która nieco przyspiesza operacje 'matematyczne' za pomocą kilku półlegalnych trików. Teoretycznie może dać drobne przyspieszenie w programach używających intensywnie funkcji arytmetycznych, ale ja osobiście nigdy tego nie zaobserwowałem. Zauważyłem za to np., że perl skompilowany z tą flagą doznaje upośledzenia funkcji matematycznych (zaczynają być niedokładne), również w przypadku Gimpa widziałem różne dziwne "odchyły". Tak więc - niby może coś dać, ale nie zauważyłem tego nigdzie. Za to może popsuć, co zauważyłem. Moja rada - zostawić w spokoju. Nie warto ryzykować, zwłaszcza jeśli efekt sprowadza się do placebo.

'-fno-exceptions' oraz '-fno-rtti'. Opcje dotyczące tylko kodu C++. Wyłączają pewne mechanizmy języka C++, oszczędzając wielkie ilości kodu, pamięci oraz cykli procesora. Niestety, rozszerzenia te są wymagane przez wiele programów w C++ i jeśli użyje się tych flag to taki kod się najnormalniej w świecie nie skompiluje. Niektóre programy, jak np. Mozilla, dają się już na poziomie ./configure powiadomić specjalnymi przełącznikami o tym, że mają użyć tych flag. Jeśli masz możliwość użycia tego duetu przy kompilowaniu kodu C++, to zrób to. Oszczędności będą prawdopodobnie całkiem spore.

'-DNDEBUG -DG_DISABLE_ASSERT' - to tak naprawdę nie są flagi kompilatora, a definicje. Które można potem odczytywać w kompilowanym kodzie. Ot, taki sposób na powiedzenie '#define foo' w linii wywołania kompilatora. Te dwie wartości nie mają z kompilatorem nic wspólnego, ale są związane z tzw. programowaniem asertywnym. Istnieje sobie taka funkcja assert() której może użyć programista. Jest to funkcja "awaryjna" która może programiście w łatwy i czytelny sposób powiedzieć, w której części programu wystąpił jakiś błąd. Użytkownikowi jednak nie jest do niczego potrzebna, no bo co mnie obchodzi w której linijce kodu źródłowego był błąd który spowodował segfault Midnight Commandera czy gpm? Przecież ja mam zamiar tylko tego używać, a nie poprawiać.

I teraz clou programu: Funkcje assert() służą tylko ludziom związanym z rozwojem programu. A więc jego autorom. W innych zastosowaniach tylko rozdmuchują wielkość programu i zwiększają jego pamięciożerność (no i redukują prędkość działania, bo procesor marnuje czas na sprawdzanie każdej funkcji assert()). Dobry, przygotowany dla końcowego użytkownika kod powinien być tak skonstruowany, by przy kompilacji omijał fragmenty z assert() jeśli użytkownik sobie tego nie życzy, np. przy './configure --without-debug'. Albo jeszcze ogólniej - kod dystrybucyjny, a więc przeznaczony dla użytkowników końcowych powinien mieć pousuwane/wyłączone funkcje assert() przed ostateczną kompilacją i testami, żeby mieć pewność że i bez nich działa poprawnie. Niestety, często developer zapomina o takich "drobnostkach" i wypuszcza program naszpikowany kawałeczkami "developerskimi". A to jest złe. O, np. tak było z niedawną wersją "make", która przez źle wywoływaną funkcję "assert()" zawodziła w niektórych przypadkach - po wyłączeniu assert() zaczynała normalnie działać. Ironia losu - mechanizm, który miał służyć podnoszeniu jakości kodu tylko ją pogorszył. Wyłączenie funkcji assert() powinno być całkowicie bezpieczne. Niestety, programiści czasem używają tej funkcji w niewłaściwy sposób i po jej wyłączeniu kod zaczyna działać błędnie, tak że może się zdarzyć że z tymi opcjami nie da się skompilować poprawnie działającego kodu.

Te dwie definicje przeciwdziałają funkcjom assert() zawartym w bibliotekach glibc oraz glib (tym od gtk+ ;). Jeśli te wartości są zdefiniowane, to definicje assert() są rozwijane do kodu o długości 0 bajtów - a więc w efekcie usuwane. Jest to sposób na poprawienie niechlujstwa czy zapominalstwa programistów, zmniejszenie kodu itp., bez żadnej utraty funkcjonalności. Dlatego warto jest je mieć włączone "na stałe". A, oczywiście jeśli kompilowany kod nie używa assert(), to wpływ tych definicji jest zerowy. Dosłownie. Czyli można tylko zyskać. Jak fajnie :)

'-s' - stripowanie. Generowany kod będzie od razu stripowany, pozbawiany "debug info". W rezultacie binarki będą mniejsze, kompilacje będą wymagały mniej megabajtów dysku. W przypadku dużych kompilacji, jak np. mozilla, glibc czy xfree, oszczędności "dyskowe" przy kompilacji sięgają setek megabajtów. To pośrednio zwiększa też prędkość kompilacji - bo dużo mniej danych wynikowych trzeba zapisywać na dysku.

Stripowanie:

Ale tutaj trzeba wyjaśnić kilka pojęć. Najpierw - "debugging". Czym jest debugging? To proces debuggowania debuggerem. Taaak, sepulki wiecznie żywe ;)

Po ludzku: debugger to program, który zwykle nadzoruje proces uruchamiania innego programu. Potrafi np. powiedzieć jaka funkcja aktualnie się wykonuje, jakie wartości mają w danym punkcie określone zmienne itp. Czyli przydatne w diagnostyce i usuwaniu błędów, ale nie w normalnym użytkowaniu programu. Aby program dał się "ładnie" debuggować, musi zawierać tzw. "debug info", czyli np. czytelne dla człowieka nazwy funkcji i zmiennych oraz inne takie głupotki. Te informacje potrafią znacznie rozepchać wielkość pliku binarnego. Na szczęście mają tylko nieznaczny wpływ na czas uruchamiania, oraz zerowy wpływ na zajętość pamięci (przy normalnym uruchamianiu programu całe "debug info" jest pomijane i nie ładuje się do pamięci - tyle, że zajmuje miejsce na dysku)

Dlatego warto jest postarać się o to, by biblioteki i programy na dysku nie zawierały "debug info" - da to naprawdę duże oszczędności w "objętości" danych, kosztem utraty możliwości debuggowania. Ale to nie jest coś, co interesuje zwykłego juzera, więc... stripujmy. Stripowanie to właśnie usuwanie "debug info" z plików binarnych. Myślę, że to dobry moment by opisać stripowanie nieco dokładniej.

Generalnie stripować można wszystko. Częściowo można sobie to "załatwić" dzięki fladze '-s' przy kompilacji, ale zawsze warto "poprawić" po kompilacji, no i warto też przejrzeć już zainstalowane binaria, może uda się na nich jeszcze nieco zaoszczędzić. Pliki stripuje się poleceniem, niespodzianka, 'strip'. Stripowaniu podlegają pliki *.so (biblioteki dynamiczne), pliki *.a (biblioteki statyczne), oraz zwykłe binarki. "Kandydata" do stripowania można znaleźć przy pomocy programu 'file' - jeśli uruchomi się 'file' na binarce, to powie on, czy binarka/biblioteka jest już zestripowana, czy też jeszcze nie. Oczywiście uruchomienie 'strip' na już ogołoconej bibliotece nie wyrządzi jej żadnej krzywdy. Jeśli uruchamia się 'strip' na jakimś programie, to powie on albo że program jest 'stripped' - wtedy niewiele można zrobić, albo że jest on 'not stripped' - wtedy można go trochę zmniejszyć. To samo dotyczy bibliotek *.so i plików *.a.

Zasadniczo wszystkie te trzy rodzaje można stripować, z tym że diabeł tkwi w szczegółach i przy stripowaniu plików *.a należy wykazać się największą ostrożnością. Hmm, to chyba dobry punkt by znowu wykonać małe "odbicie" od tematu i wyjaśnić różnicę między plikami *.so i *.a.

Obydwa te typy to biblioteki, oferują zwykle te same funkcje. Z tym że biblioteki *.so to biblioteki dynamiczne - program łączy się z nimi w momencie uruchamiania i w swoim ciele nie zawiera ani jednej funkcji z biblioteki - tylko "namiary" na nią. Pliki *.a to biblioteki statyczne. Przy kompilacji (a właściwie przy konsolidacji) linker wyjmuje z pliku *.a odpowiednie funkcje i wbetonowuje je w binarkę programu. Binarka jest przez to większa, ale nie potrzebuje potem już obecności biblioteki by się uruchomić i działać. Linkowanie statyczne w ciągu ostatnich lat zostało silnie zdegradowane, bo nie jest to specjalnie ekonomiczny model. Np. każdy (no, prawie każdy) linuksowy program używa glibc. Gdyby był z tym kolosem statycznie zlinkowany, to musiałby zawierać w sobie spory kawałek kodu glibc. Każdy jeden program. W sumie dałoby to, na działającym systemie, kilkadziesiąt replik kodu glibc w pamięci operacyjnej, każda replika identyczna z pozostałymi, ale każda też niezależna, bo wlepiona na stałe w inny program. Marnotrawstwo zasobów, bo skoro i mutt, i slrn używają glibc, to po kiego diabła miałyby go wlec z sobą? Lepiej jest, gdy w pamięci znajdzie się jedna kopia biblioteki, a potrzebujące jej programy się podzielą dostępem do jej funkcji. Jest to odrobinę wolniejsze, ale oszczędności miejsca na dysku i wolnego ramu wynagradzają to z nawiązką. Dlatego też biblioteki dynamiczne (i linkowanie dynamiczne) są najlepszym rozwiązaniem. Ale mimo wszystko istnieją biblioteki statyczne. Mają one swoją racje bytu, np. jeśli jakaś biblioteka jest używana przez jeden tylko program, który na dodatek zwykle nie pojawia się w systemie w większej ilości (tzn. nie jest uruchamiany wielokrotnie i równolegle), jeśli takie są warunki działania programu, to lepiej jest go zlinkować z biblioteką statyczną. Albo sytuacje awaryjne - np. niektóre bardzo ważne narzędzia systemowe mogą być linkowane ze statyczną wersją glibc, aby uniezależnić się od ew. awarii dynamicznej wersji glibc. Ale to bardzo sporadyczne przypadki, które można by wymienić na palcach obu rąk.

Dlaczego tak się nad tym rozwodzę? Z prostego powodu: Wiele wersji bibliotek statycznych nie jest, nie będzie nigdy potrzebne. Np. statyczne wersje biblioteki ncurses. Albo Alsa. Albo inne, różniaste biblioteki, takie jak np. libpng, libssl, libImlib czy jeszcze inne. Skoro nie są potrzebne, to można je usunąć. Jeśli nie ma potrzeby linkowania z biblioteką statyczną, a mamy w systemie zarówno wersję dynamiczną *.so, jaki i statyczną *.a, to można śmiało usunąć tą statyczną. Oszczędności policzy się w megabajtach.

Uruchomienie prostego jednolinijkowca:
Kod:
for i in *.a;do [ -f ${i%a}.so ]&&echo $i;done


w katalogu zawierającym biblioteki statyczne (np. /usr/lib) wyświetli te spośród nich, które mają swoje odpowiedniki dynamiczne. I które tym samym stają się kandydatami "do odstrzału". Zalecam jednak pozostawić statyczną wersję glibc, oraz być może libstdc++. Chyba, że już teraz wiesz, że ci się nie przydadzą.

Możliwe, że biblioteki statyczne będą dodatkowo istniały w specjalnych "podwersjach" oznaczonych przyrostkami, np. *_g.a lub *_p.aZwykle idzie wtedy o biblioteki z rozszerzonym "debug info", lub, co gorsza, biblioteki "profilowane" które również nie przydadzą się zwykłemu człowiekowi. Do śmieci z nimi! :)

A, w katalogach z bibliotekami znajdą się również czasem pliki *.la. To tekstowe pliki dla narzędzia "libtool", zawierające informacje o dostępności (lub nie) odpowiednich wersji statycznych/dynamicznych. Jeśli usunęliśmy np. libpng.a i istnieje plik libpng.la, to warto go zmodyfikować (usuwając z linijki 'old_library' nazwę biblioteki statycznej). Nie jest to zwykle konieczne, ale można to zrobić. Wtedy będzie "bardzo porządnie". Mi by się pewnie nie chciało :) A, lepiej nie usuwać plików *.la. Libtool na nich polega, a różne skrypty aplikacje przy kompilacji używają libtoola.

No dobrze, wróćmy do stripowania. Pliki wykonywalne i biblioteki *.so (oraz różne pluginy w formie *.so) stripuje się po prostu poleceniem 'strip plik1 plik2 plik3', gdzie 'plik*' to pliki do stripowania.

Uwaga!!!

nie stripuje się plików aktualnie wykorzystywanych w systemie, jak np. glibc - to się może skończyć nieodwracalnym uszkodzeniem pliku, a w przypadku glibc jest to dodatkowo praktycznie równoznaczne z zawieszeniem systemu (i poważnym utrudnieniem w przywróceniu systemu to stanu używalności!). Trzeba najpierw skopiować gdzieś glibc, zestripować, a następnie jakimś statycznie zlinkowanym narzędziem (np. shellem 'sash') skopiować na stare miejsce. To bardzo ważna uwaga! Nie uruchamiaj pochopnie "strip /lib/*.so*"!

To by załatwiło podstawowe stripowanie. Można potem jeszcze 'poprawić', używając bardziej wyrafinowanej składni polecenia 'strip', a mianowicie:
Kod:
strip -R .comment -R .note -R .note.ABI-tag plik1 plik2 plik3


Dochodzą tu nowe opcje -R, wypruwające 3 konkretne sekcje z plików, które nie są normalnie usuwane przez 'strip', a na których można jeszcze trochę zaoszczędzić. Używając polecenia 'objdump -h' możesz uzyskać listę sekcji w jakiejś binarce. Sekcji będzie wiele, ale zwykle będą one programowi potrzebne, więc ogranicz się do normalnego stripowania, plus usunięcie tych trzech wymienionych przeze mnie.

Zupełnie inaczej wygląda stripowanie bibliotek statycznych. "Normalne" zestripowanie by je nieodwracalnie uszkodziło. Dlatego pliki *.a należy stripować poleceniem 'strip -gXx'. Staną się mniejsze, a nadal będzie można ich używać przy kompilacjach. Oczywiście dotyczy to tylko tych bibliotek statycznych, których jeszcze nie usunęliśmy :)

Słowo końcowe: Stripowanie może dać oszczędności miejsca. Stripuje się programy i biblioteki. Warto zwrócić uwagę, że dużo programów używa pluginów/modułów w formie bibliotek dynamicznych. Tak jest np. z pluginami xmms, rozszerzeniami Zsh, albo modułami XFree86. Je też można zwykle stripować, choć przy eksperymentowaniu dobrze jest zrobić najpierw kopię oryginalnych plików. Na wszelki wypadek. Nie stripuje się plików *.o, ani *.jpeg, ani *.mp3. Ogranicz się do *.so, *.a i programów, a wszystko powinno być dobrze. Programu 'file' używaj, by sprawdzić czy coś już było stripowane. To by było na tyle co do stripowania.

Kompilacja:

Po pierwsze, będziemy potrzebowali ustawionych flag kompilatora. Dobrze jest ustawić je globalnie, np. w /etc/profile. Ja na swoim Celeronie używam zestawu:
Kod:
export CFLAGS="-march=pentium2 -Os -fomit-frame-pointer -s -pipe -DNDEBUG -DG_DISABLE_ASSERT"export CXXFLAGS="-march=pentium2 -Os -s -pipe -DNDEBUG -DG_DISABLE_ASSERT"export LDFLAGS="-s -z combreloc"


Jak widać flagi dla C++ różnią się od C tylko brakiem '-fomit-frame-pointer'. Widać też, że nie używam standardowo '-fno-rtti' i '-fno-exceptions', ale to dlatego, że stosunkowo niewiele kodu w C++ da się z nimi skonfigurować i w razie potrzeby po prostu dodaję te flagi przy kompilacji "królika doświadczalnego". Za to wszędzie optymalizuję pod mój procesor i wybieram najrozsądniejszy i najwydajniejszy IMO model '-Os'.

Występuje tutaj też linijka dla linkera: '-s -z combreloc'. '-s' to stripowanie obiektów w ostatniej fazie - czyli przy konsolidacji. Tak na wszelki wypadek :) No i opcja '-z combreloc' - linker będzie próbował użyć jeszcze trochę magii i jeszcze o odrobinkę przyspieszyć kod przez odpowiednie układanie fragmentów układanki. Generalnie te opcje są dobre w 80-90% zastosowań, wystarczy dostosować sobie opcje '-march= ' i ewentualnie '-mcpu= ' do swojej maszyny.

No dobrze, mamy już te zmienne poustawiane. Teraz możemy spotkać się z dwoma rodzajami pakietów z kodem: tych opartych na mechanizmach autoconf, i tych nie opartych ;)

Autoconf:

Cóż, to bardzo proste. Uruchamiamy skrypt ./configure, używając odpowiednich w danej sytuacji przełączników '--prefix=, --without-debug=, --disable-foo' itp. Jeśli kompilujemy bibliotekę, to warto z góry poprosić o wersję dynamiczną, bez statycznej (zwykle '--disable-static --enable-dynamic'), zwykle warto też powyłączać wszelkiego rodzaju "debug", o ile można. Programy kompilować należy zwykle tak, by miały to, czego potrzebujemy, ale ani grama funkcjonalności więcej. Czyli w mplayerze wkompilowuje się tylko te urządzenia, których faktycznie będzie się używać, itd. To chyba zrozumiałe. A gdzie wchodzi do gry "normalna" optymalizacja? Cóż, ona wchodzi automagicznie. Skrypt ./configure popatrzy sobie na zmienne środowiskowe CFLAGS, CXXFLAGS, LDFLAGS i ustawi flagi kompilatora stosownie do tych zmiennych. Pozostaje tylko wpisać 'make' i patrzeć, jak się nam ładnie kod optymalizuje. A potem zainstalować. Praktycznie każdy system używa jakiegoś mechanizmu pakietowania i jeśli kompiluje się coś samodzielnie, to warto zadbać o to, by "nasze" programy również były zarejestrowane w bazie oprogramowania. Najprościej robić to za pomocą programu 'checkinstall', najładniej robić to samemu, robiąc firmowy pakiet "tymi ręcami". Ale to wymaga więcej wiedzy. Robienie pakietów to jednak opowieść na inny artykuł. Ja rozwijam sobie od niedawna własny system pakietowania, który może okazać się dobrym wyborem dla wielu osób używających systemów bez żadnego natywnego systemu pakietów.

Wracając do autoconfa: Warto się zatroszczyć o to, by mieć jak najnowsze wersje pakietów autoconf i automake. Czasem dobrze jest też wiedzieć jak one działają. Nie żeby to było konieczne, ale przy kompilacji kodu pochodzącego z CVS jest to bardzo przydatne.

Nie-autoconf:

Głęboka woda. Wszystko zależy od programisty zestawiającego pakiet ze źródłami. Zwykle będzie to wszystko bazowało na jakimś systemie Makefile, i flagi kompilacji trzeba będzie samodzielnie poustawiać. Nie będzie żadnego './configure'. Czyli trzeba będzie przejrzeć jakieś README czy INSTALL. Na szczęście takich pakietów nie ma już wiele na świecie, a te które istnieją mają zwykle jasne instrukcje. Nie napiszę tu nic konkretnego, bo nie ma tutaj już żadnych standardów i nie ma żadnych uniwersalnych wzorców zachowań. Obowiązują oczywiście te same zasady "obcinania" kodu jak w poprzednim przypadku: wyłączyć "debug" i profilowanie, wyłączyć niepotrzebne zbytki. W przypadku XFree86 warto np. ograniczyć listę obsługiwanych kart do tych, które posiadamy, oraz skompilować XSerwer jako monolit (oszczędza się całe megabajty ramu).

Instalacja:

Instalacja powinna być połączona ze stworzeniem pakietu. Ale to zbyt obszerny temat, by go tutaj teraz opisywać. Bo stworzenie pakietu spada na administratora, to oczywiste.

Posłowie:

Jakie pakiety opłaca się przerobić samodzielnie? Wszystkie. Ale niektóre bardziej. Przede wszystkim, te programy, które zużywają dużo czasu procesora. Oraz te, które są często używane. Czyli na pewno glibc, XFree86, biblioteki graficzne, duże środowiska typu KDE czy GNOME, Mozilla. Jeśli to cię jeszcze nie zniechęci, to przerób całą pozostałą drobnicę.

I garść haseł na drogę: Dbaj o aktualność narzędzi. gcc, binutils - zwykle im nowsze, tym lepsze. Wiedz z góry, co ci potrzebne. Czytaj pliki README i INSTALL, uważnie przeglądaj listę opcji './configure --help'. Jeśli możesz tego uniknąć, to nie instaluj programów "na dzika" za pomocą 'make install' - buduj pakiety, w ostateczności zwal czarną robotę na 'checkinstall'. Stripuj co się da. Bądź ostrożny przy modyfikowaniu plików wybitnie systemowych - rób kopię zapasową. Jeśli nawet po Twoim majsterkowaniu ciągle działa, możesz kopię zapasową usunąć. Ale zawsze miej coś w odwodzie, choćby dla polepszenia samopoczucia. Nie pij przy kompilowaniu ;) Ograniczaj funkcjonalność programów do tego, co Ci jest potrzebne. Bądź realistą, nie myśl "a jeśli za pół roku zmienię kartę graficzną?" - bo jeśli zmienisz kartę, to najwyżej jeszcze raz przekompilujesz XFree86/svgalib/mplayera. Jeśli coś nie jest Ci teraz potrzebne, to możesz z tego zrezygnować. Przygotuj się na porażki - będą Cię często, jakże często spotykać. Pocieszaj się tym, że to zwykle nie twoja wina, tylko koderzy coś znowu spieprzyli.

No i ostatnie, najważniejsze: Baw się dobrze :)

Uwaga!!! ;)

Stosując się do uwag zawartych w tym artykule i rozpoczynając własnoręczne kompilacje ryzykujesz odrzuceniem dystrybucji jako takiej, możesz skończyć z systemem który od A do Z jest twoją produkcją. You have been warned.

Varia

Poniżej będę umieszczał luźne uwagi jakoś powiązane z kompilowaniem oprogramowania. Drobne rzeczy o które ludzie czasem pytają, rzeczy niezbyt oczywiste lub które można poznać tylko empirycznie. Małe, małe rzeczy którym nie mam zamiaru zakładać osobnych artykułów.

gcc

Obecną plagą kompilatorów gcc jest język C++. Odkąd rozpoczęła się gałąź 3.x, każde nowe wydanie coraz ściślej trzyma się standardów. A to oznacza, że niezbyt poprawny kod C++ który poprzednio dawał się skompilować bez problemów nagle może okazać się nie do ugryzienia nowym kompilatorem. W 99% przypadków winowajcą nie jest gcc, ale właśnie brudny, brzydko napisany kod. Po prostu dopiero teraz zaczęło się to ujawniać. Dotyczy to też języka C, ale w dużo mniejszym stopniu. Ja używam aktualnie wersji gcc-3.3.3, którą kompilowałem praktycznie wszystkie części systemu, te drobne (gtk+, vim, elinks itp.) jak i te duże (kernel, XFree86, glibc) i działają poprawnie. Wersja 3.x wydaje się być całkiem udaną, każda kolejna podwersja generuje coraz to mniejszy (a tak samo szybki) kod.

Innym problemem jest to, że kompilatory gcc zawsze działają w ścisłej współpracy z pakietem binutils. Często jest tak, że współpraca ta nie układa się zbyt dobrze, dla niektórych kombinacji gcc/binutils kompilator produkuje czasem wadliwy kod, jest to o tyle męczące, że błędy mogą objawić się dopiero po miesiącach. Trudno wykryć takie "mikrouszkodzenia" - uzyskałem kiedyś w taki sposób felerne glibc, które niby działało dobrze, ale dwie posiadane przeze mnie aplikacjie segfaultowały. Tylko dwie na cały system. Myślałem, że to one są winne, ale okazało się, że to jednak wina glibc, które było kompilowane na kłopotliwej kombinacji gcc/binutils. Ja używam teraz binutils-2.14.90.0.8, które w połączeniu z gcc-3.3.3 zdają się produkować stabilny, godny zaufania kod. Przynajmniej na platformie x86.
Aktualizacja z końcówki marca 2004: wygląda na to, że od paru ładnych miesięcy jest spokój. Po prostu wystarczy się trzymać najnowszych "eksperymentalnych" binutils, a wszystko będzie dobrze.

binutils

Binutils stoją w cieniu, ale to pakiet równie ważny co gcc. Mogą nadpisać jeden z plików gcc, mianowicie libiberty.a. Czasem będzie tak, że nowsze wersje binutils będą naprawiały (maskowały) jakiś błąd w gcc, czasem z kolei nowsze wersje binutils będą powodowały kłopoty. Rozwój binutils idzie dwoma ścieżkami - jest sobie wersja "stabilna" jak i "rozwojowa". "Stabilna" przez ostatnie miesiące nie nadawała się w ogóle do użytku z kompilatorami gcc3, mimo iż pojawiały się w tej gałęzi aktualizacje po wydaniu gcc3! Było to bardzo "interesujące" zjawisko, gdy nawet w Debianie gościła wersja binutils z gałęzi niestabilnej - po prostu tylko ona działała. Na szczęście ostatnio wydana wersja stabilna (2.14) zdaje się zsynchronizowała się z niestabilnymi i znowu działa. Ale w przyszłości, gdy pojawią się nowe wersje gcc, może znowu zajść potrzeba używania wersji rozwojowych (co czasem przypomina ruletkę).
Aktualizacja z końcówki marca 2004: na szczęście ostatnio jest pod tym względem całkiem nieźle i większość zastrzeżeń w tym paragrafie zdaje się być nieaktualna. Zostawiam je jednak na wszelki wypadek.

glibc

Glibc lubi dostarczać problemów. Nie jest projektem specjalnie czystym, kod miejscami jest po prostu niechlujnie napisany. Aktualna wersja (2.3.2) nie daje się tak łatwo skompilować za pomocą gcc-3.3.1, potrzebny jest drobny patch (znajdziesz go w moim kadłubku RPM dla glibc), ale po jego nałożeniu wszystko działa bardzo stabilnie. Inny kłopot to flaga -fomit-frame-pointer: glibc w 95% przypadków kompiluje się razem z dodatkiem linuxthreads. Ale dodatek ten nie działa prawidłowo skompilowany z tą flagą. Nie wiem czy to wina gcc czy linuxthreads, ale przypuszczam że to raczej linuxthreads jest tak "ślicznie" napisany. W każdym razie skutek jest taki, że po skompilowaniu glibc z flagą -fomit-frame-pointer uzyskuje się półsprawny kompilat, część programów działa a część segfaultuje od razu (segfaultują te używające libpthread - bo to właśnie jest dodatek linuxthreads). Rozwiązania są dwa - albo nie używać -fomit-frame-pointer przy kompilacji glibc, albo nałożyć drobny patch który wyfiltruje tę flagę przy kompilacji samego linuxthreads (resztę glibc spokojnie można kompilować z -fomit-frame-pointer). Patch ten znajduje się w moim kadłubku RPM, identyczne rozwiązanie znajduje się też w zasobach PLD.Aktualizacja z końcówki marca 2004: to o -fomit-frame-pointer i kłopotach z gcc jest już nieaktualne, a przynajmniej będzie gdy wyjdzie kolejna oficjalnie wersja glibc z NPTL zamiast linuxthreads. W CVS już jest, można pobrać, skompilować i używać - ja tak robię. Ale ostrzeżenia pozostawiam, bo zapewne nie wszyscy zdecydują się na wersję z CVS, no i nie wiadomo kiedy będzie następne "release" glibc.

I jeszcze jedna uwaga - nie ma sensu w glibc używać specjalnej opcji ./configure która ma niby włączyć '-fomit-frame-pointer'. Opcja ta i owszem, dodaje tę flagę, ale przy okazji włącza też optymalizację na poziomie -O99 - koszmar jeśli chcesz użyć -Os. Lepiej jest kompilować normalnie, wystarczy ustawić sobie $CFLAGS i tam wstawić -fomit-frame-pointer.

kernel

Kernel oferuje tylko kilka głównych architektur do wyboru i wszystko kompiluje ze standardowym -O2, ignorując przy tym $CFLAGS. Ma to swoje powody, chodzi głównie o to, że inne kombinacje nie są testowane więc się ich nie zaleca. Co nie znaczy jednak, że nie można tego robić... flagi należy zmienić przez odpowiednią edycję niektórych plików Makefile, ja np. od jakiegoś już czasu swoje jądro kompiluję pod pentium2 (zamiast pod ogólne i686), a optymalizację zmieniłem na -Os (oszczędzając chyba prawie 100KB na wynikowym bzImage). I działa tak samo stabilnie, nie zanotowałem żadnych efektów ubocznych (a na jądro dodatkowo nakładam sobie zawsze łatki xfs-lowlat-preempt). Nie zalecam jednak kombinowania z -fomit-frame-pointer ani z flagami zmieniającymi np. wykorzystanie rejestrów przy wywoływaniu funkcji, to już mocno ryzykowne.
Aktualizacja z końcówki marca 2004: Jak wiadomo, mamy teraz już kernele 2.6, w chwili pisania tych słów jest to 2.6.4. Z ciekawszych pozycji można wymienić "General setup -> Remove kernel features". Warto wyłączyć "load all symbols for debugging/kksymoops", to zmniejszy trochę wynikowe jądro. Można też włączyć "Optimize for size", co spowoduje używanie -Os zamiast -O2. Ha! Teraz można to zrobić całkowicie oficjalnie, bez edytowania plików Makefile.

XFree86

Ten pakiet, choć kobylasty i operujący blisko sprzętu, jest jednak dosyć elastyczny w przyjmowania flag kompilatora, trudno nimi coś popsuć. Cała konfiguracja odbywa się za pomocą specjalnego pliku host.def, który zawiera opcje dla kompilatora jak i całego procesu budowania. Ale wygląda na to, że autorzy nie testują go wcale przed wypuszczeniem! A przynajmniej nie testują czegokolwiek poza "default options", bo bardzo łatwo jest ustawić sobie opcje, teoretycznie całkowicie poprawne, przy których XFree86 nie da się skompilować. Albo będzie próbował kompilować się korzystając z plików istniejącej już instalacji XFree86 zamiast tych, które przychodzą w jego źródłach. Myślę, że winny jest tutaj system nadzorujący proces kompilacji oparty o mechanizmy imake, który zdaje się zawodzić przy tak wielkich projektach. Albo to koderzy nie umieją się nim posługiwać. W każdym bądź razie po kompilacji i zainstalowaniu XFree86 raczej nie będzie sprawiać problemów. Kłopoty mogą być z samym zmuszeniem drania do skompilowania.

Wspomniany plik host.def zawiera całą konfigurację. Wybrane sterowniki, flagi kompilatora, zaawansowane opcje. Za jego pomocą można sobie skroić naprawdę dopasowany XSerwer - zyskać można sporo na ramie jak i czasie uruchamiania, największy zysk przynosi rezygnacja z modelu modularnego i skompilowanie serwera jako monolit. Nie jest to jednak raczej możliwe jeśli musimy używać zamkniętych, zewnętrznych, binarnych sterowników karty graficznej (no, nVidia i spółka). A szkoda. Zawsze jednak można powyłączać mnóstwo innych, niepotrzebnych rzeczy. Jednak wystruganie "minimalnej dającej się skompilować" konfiguracji może zabrać kilka cykli kompilacyjnych, a walczenie z błędami koderów XFree86 też do przyjemnych nie należy. A, każda instalacja XFree86 standardowo instaluje się razem ze swoim plikiem host.def (/usr/X11R6/lib/X11/config/host.def), więc można go używać jako podstawki do własnej dłubaniny, choć wiele opcji może nie być wcale w nim wymienionych. W każdym razie koniecznie trzeba przeczytać załączoną w źródłach dokumentację.
  
Re: opcje kompilatora
PostWysłano: Środa, 20 Września 2006, 11:03 Odpowiedz bez cytowania Odpowiedz z cytatem
hardtmuth
Pro uczestnik
<tt>Pro uczestnik</tt>
 
Użytkownik #2
Posty: 1301


[ Osobista Galeria ]




o super trzeba bedzie sie temu przyjerzec blizej, ale po sesji poprawkowej:)
  
opcje kompilatora
Forum dyskusyjne -> Software -> Linux

Strona 1 z 1  
  
  
 Napisz nowy temat  Odpowiedz do tematu  
noclegi ciechocinek
Kopiowanie i rozpowszechnianie materiałów w całości lub części jest niedozwolone. Wszelkie informacje zawarte w tym miejscu są chronione prawem autorskim.



Forum dyskusyjne Heh.pl © 2002-2010