Již v minulém dílu jsme se dotkli voleb kompilátoru, které můžeme předat proměnné CFLAGS, případně CXXFLAGS(pro C++). Dnes se zaměříme na volby optimalizace.
Typ procesoru
Gcc nabízí volby pro kompilaci pro různé procesory. Jsou jimi:
-march=typ_cpu: Tato volba nastaví kompilaci pro cílový procesor. Kód pak funguje na daném procesoru a jeho následnících.
-mcpu=typ_cpu: Tato volba nastavuje většinu parametrů pro daný procesor, ale vlastní tabulku instrukcí nemění. Lze tak vyrobit kód, který sice běží na i386, ale optimalizován je např. pro Pentium.
Nejnovější gcc-3.2 již podporuje výstup pro následující typy procesorů IA32 (Intel Architecture 32 bit): i386, i486, i586, i686, pentium, pentium-mmx, pentiumpro, pentium2, pentium3, pentium4, k6, k6–2, k6–3, athlon, athlon-tbird, athlon-4, athlon-xp a athlon-mp.
Naproti tomu nejvyšším IA32 systémem, který dnes GNU auto nástroje detekují, je i686-pc-linux-gnu. Pokud chceme lepší optimalizaci, musíme si pomoci proměnnou CFLAGS. Tou si musíme pomáhat i tehdy, je-li kompilátor implicitně nastaven na nižší typ procesoru.
Ladění
Pro ladění existuje volba -g. Ta zapne generování tabulek symbolů pro ladění. Ty se pak objeví v knihovnách i binárních souborech (pokud je nenainstalujeme pomocí make install-strip). Mnohé balíčky mají též –enable-debug jako volbu pro configure. Ta zapne nejen generování ladicích symbolů, ale přidá do kódu množství ladicích výpisů.
K samotnému ladění pak budeme potřebovat debugger (např. gdb), zřejmě i nějakou jeho grafickou nadstavbu a nejlépe i původní zdrojový kód.
Optimalizace
Optimalizace kompilátorů gcc se skládá z množství dílčích voleb, začínajících -f nebo -fno-. Najdete je v dokumentaci gcc, zkrácenou informaci pomocí gcc -v –help. Aby bylo nastavení optimalizace jednodušší, existuje ještě číselná škála voleb -O0 až -O3, které postupně zapínají více optimalizací a kompilují déle. V nových kompilátorech je navíc k dispozici -Os, která zapne pouze takové volby, jež nezpůsobují prodloužení kódu (optimalizace na velikost) – tedy optimalizace pro stroje s minimem paměti. Pokud chcete zjistit, které volby se kdy zapínají, odkazuji vás na zdrojové kódy gcc: gcc/toplev.c.
Standardní optimalizace, kterou auto nástroje pro GNU/Linux zapínají, je -O2.
Některé speciální volby však pomocí -O? nelze zapnout. Jsou to nastavení, která jsou jistým způsobem sporná – buď mohou způsobit extrémní zvětšení kódu, nebo generují méně přesný kód.
Volba -fomit-frame-pointer způsobí, že ve výsledném kódu nebude obsažen ukazatel rámce (ukazatel do struktur nadřazených objektů na zásobníku), pokud to není nutné (např. na IA32). Takový kód může být kratší a rychlejší, prakticky jej však nelze ladit. Pokud je takto zkompilována knihovna, mohou být problémy dokonce i při ladění aplikace, která ji používá. Některé aplikace mívají problémy s funkčností, jsou-li takto kompilovány (např. některé verzeMozilly).
Volba -funroll-all-loops, jak název napovídá, rozvine všechny smyčky se známým počtem opakování. Často to vede k velkému prodloužení kódu za cenu několika málo ušetřených strojových cyklů. Přesto se používá, zvlášť u multimediálních aplikací.
Volba -ffast-math zapne rychlé matematické rutiny namísto standardních IEEE nebo ISO. To může způsobit problémy matematickým a statistickým aplikacím, ale pro multimediální a 3D aplikace to znamená zvýšení rychlosti.
Volba -malign-double může zvýšit rychlost tím, že proměnné typu double a long long se budou číst z paměti během jediného cyklu. Způsobí však také zvětšení datových struktur a jejich binární nekomaptibilitu.
U kompilátorů C++ existuje možnost zmenšit paměťové nároky, pokud nebudeme používat RTTI (identifikaci typu proměnných za běhu) ani výjimky: -fno-rtti -fno-exceptions. Některé aplikace však na těchto vlastnostech staví, a pak při jejich kompilaci narazíme na podivné chyby. Stane-li se tak, nejdříve otestujeme, jak kompilace proběhne bez těchto voleb. Pozor, tyto volby mohou generovat mírně odlišné knihovny a kompilační chyba se může projevit až v aplikaci, která je použije.
U mizerně napsaných C++ aplikací občas pomůže -fpermissive, které z některých chybových hlášení udělá pouze varování.
S postupující kvalitou kompilátorů se ukazuje jedna nepříjemná věc – ne každou aplikaci lze moderním kompilátorem optimalizovat. Setkáme se dokonce s aplikacemi, které fungují pouze s -O0 (tedy jen bez optimalizace – např. starší verze recode). V 99 % to není chyba kompilátoru, ale programátora.
U některých aplikací se lze chybě vyhnout pomocí volby -fno-strict-aliasing, případně jiných voleb, které mohou poškodit kód špatně napsaného programu.
Ale -O0 s výhodou použijeme i jindy – pokud aplikaci teprve ladíme a chceme ji rychle zkompilovat.
Existují i opačné aplikace, které nelze kompilovat bez optimalizace. Jde především o použití inline funkcí, které neexistují v jiné podobě (např. inb a outb).
Pro ty, kdož experimentují s kompilátorem icc od Intelu, přidávám ještě tip na maximální optimalizaci pro něj: -O3 -axK -ipo.
Nejdokonalejší cestou, jak zajistit optimalizaci, je profilování. Aplikace je zkompilována s volbami pro profilování, spuštěna a používána. Při tom jsou vytvářeny profily, které sledují, jak často je daný kus kódu vyvoláván a jaký bývá častější výsledek testu při větvení. Poté je aplikace znovu zkompilována a na základě těchto informací vytvoří optimalizátor co nejlepší kód.
Spouštíme configure
Pokud spouštíme configure poprvé, budeme zřejmě zahlceni informacemi o přítomnosti a nepřítomnosti knihoven a hlavičkových souborů. Později zjistíte, že se nejedná o pouhý náhodný souhrn testů, ale že je v nich určitá pravidelnost a že skript má jisté fáze (jejich oddělení není nijak patrné, pouze se změní charakter testů).
Na zkoušku si spustíme konfiguraci kdelibs (ve skutečnosti jsem si část výsledků pro studijní účely vymyslel). Z důvodů uvedených v minulém dílu si vyberu instalaci do /opt/kde. Do souboru /etc/config.site jsem si zapsal vhodné hodnoty (viz minulý díl) a volání GNU-FHS. Skriptu configure předám –enable-final (kompilace po větších blocích – viz dokumentace KDE). Do CXXFLAGS nebudu přidávat -fno-rtti -fno-exceptions, neboť u kdelibs verze 3 to dělá samotný make.
export CONFIG_SITE=/etc/config.site
export CFLAGS=„-march=athlon-4 -O3 -fomit-frame-pointer“
export CXXFLAGS=„-march=athlon-4 -O3 -fomit-frame-pointer“
./configure –prefix=/opt/kde –enable-shared –disable-static –enable-final –with-qtdir=/opt/qt3
Většina skriptů začíná obecným ověřením platformy. Testuje se, na jaké platformě pracujeme, zda fungují kompilátory a další základní programy, a jaké argumenty je možné jim předat.
configure: loading site script /etc/config.site
loading GNU-FHS site script
checking build system type… i686-pc-linux-gnu
checking host system type… i686-pc-linux-gnu
checking target system type… i686-pc-linux-gnu
checking for a BSD compatible install… /usr/bin/install -c
checking for -p flag to install… yes
checking whether build environment is sane… yes
checking for mawk… no
checking for gawk… gawk
checking whether make sets ${MAKE}… yes
checking for a BSD compatible install… /usr/bin/install -c -p
checking for style of include used by make… GNU
checking for gcc… gcc
checking for C compiler default output… a.out
checking whether the C compiler works… yes
checking whether we are cross compiling… no
checking for executable suffix…
checking for object suffix… o
checking whether we are using the GNU C compiler… yes
checking whether gcc accepts -g… no
checking dependency style of gcc… gcc3
checking how to run the C preprocessor… gcc -E
checking for g++… g++
checking whether g++ supports -fno-exceptions… yes
checking whether g++ supports -fno-check-new… yes
checking whether g++ supports -fexceptions… yes
…
Pak se testují základní systémové knihovny pro různé systémy. Zde si všimnete, že některé testy pravidelně dopadnou negativně. Jsou to testy na základní knihovny některých nelinuxových UNIXů. Poté se testuje způsob lokalizace systému, základní hlavičkové soubory a vlastnosti datových struktur (jako kolik bajtů má long, funguje-li long long apod.).
…
checking for shl_load… no
checking for shl_load in -ldld… no
checking for dlopen… no
checking for dlopen in -ldl… yes
checking whether a program can dlopen itself… yes
checking whether a statically linked program can dlopen itself… yes
checking for msgfmt… /usr/bin/msgfmt
checking for gmsgfmt… /usr/bin/msgfmt
checking for xgettext… /usr/bin/xgettext
checking for ranlib… ranlib
checking for ANSI C header files… yes
checking for sys/types.h… yes
checking for sys/stat.h… yes
checking for stdlib.h… yes
checking for string.h… yes
…
checking for res_init… no
checking for killpg in -lucb… no
checking for int… yes
checking size of int… 4
checking for long… yes
checking size of long… 4
checking for char *… yes
checking size of char *… 4
checking for char… yes
checking size of char… 1
…
Tyto testy se pravidelně opakují v mnoha balících, jsou důkladně prověřeny a většinou fungují dobře.
Grafické aplikace si poté otestují X a přecházejí se ke specializovaným testům.
checking for X… libraries /usr/X11R6/lib, headers /usr/X11R6/include
checking for IceConnectionNumber in -lICE… yes
checking for libXext… yes
Nyní je potřeba zbystřit a sledovat, co skript hledá a zda to našel. Pokud se ve svém systému zatím neorientujete, bude to trochu náročnější. Ale časem, až získáte další poznatky, se orientace stává snazší.
V této části skript testuje přítomnost (nebo aktivizace) povinných a volitelných komponent. Pokud nenalezne některou z povinných komponent systému, většinou se zastaví, vypíše, co chybí, a dále nepokračuje. Jedná-li se o volitelnou součást, vypíše pouze nepřítomnost komponenty a pokračuje. Části balíku vyžadující její podporu pak budou vypnuté.
Hned na začátku vidíme problém:
checking for Xinerama… no
Protože vím, že Xinerama (rozšíření X pro více obrazovek) je v mém systému přítomno, začnu pátrat. Řešení je tentokrát jednoduché: podpora rozšíření Xinerama je standardně vypnutá a slovo no v tomto případě znamená, že test se vůbec neprováděl. Stačí předat skriptu –enable-xinerama. Spustím jej tedy znovu:
checking for Xinerama… yes
…
checking for libz… -lz
checking for libpng… -lpng -lz -lm
checking for libjpeg6b… no
checking for libjpeg… -ljpeg
checking for Qt… libraries /opt/qt3/lib, headers /opt/qt3/include using -mt
checking if Qt compiles without flags… no
checking for moc… /opt/qt3/bin/moc
checking for uic… /opt/qt3/bin/uic
checking whether uic supports -L … yes
checking whether uic supports -nounload … yes
checking if Qt needs -ljpeg… no
…
checking whether byte ordering is bigendian… no
…
checking for mkstemps… no
…
checking for vfork.h… no
…
Zde vidíme hned několik negativních odpovědí. Aby nás neznepokojovaly, budeme pro ně hledat vysvětlení.
První nás nemusí pálit – libjpeg6b je pouze jiné jméno libjpeg, použité na některých systémech. My mámelibjpeg (jak ukázal hned následující test), takže je vše v pořádku.
Další negativní hlášení – Qt se nedá zkompilovat bez přídavných argumentů. I to je v pořádku – qt3 jsem instaloval ručně do adresáře /opt/qt3 a tam kompilátor standardně nevidí.
Dále – Qt nepotřebuje -ljpeg. Opět je to logické – jpeg obrázky totiž v Qt čte zvláštní modul (toto chování je v Qt nastavitelné).
A že pořadí bajtů není bigendian? No, to není a na IA32 nikdy nebylo.
A co funkce mkstemps? Prohledáním dokumentace ke GNU C Library snadno zjistíme, že tam o ní není ani slovo:
fgrep mkstemps /usr/share/info/libc.info*
To si ostatně můžeme potvrdit i prohledáním knihovny:
fgrep mkstemps /lib/libc.so.6
A nemáme hlavičkový soubor vfork.h? Nemáme. Prohledáním hlavičkových souborů zjistíme, že funkce vfork je definována jinde – v souboru unistd.h:
fgrep -r vfork /usr/include
/usr/include/unistd.h:extern __pid_t vfork (void) __THROW;
Takto sledujeme výstup a neshledáváme žádné skutečné problémy.
…
checking for FAMOpen in -lfam… no
…
Tak, a máme první problém. Na systému totiž FAM (File Alteration Monitor – monitor změn souboru) nebyl nainstalován. Z dokumentace zjistíme, že je to užitečná věc, bez níž musím dávat Obnovit, chci-li vidět změny v adresáři. FAM si tedy doinstaluji (včetně záplaty na jádro a spouštění démona při startu počítače) a opakuji konfiguraci:
checking for FAMOpen in -lfam (cached)… no
Hmm, výsledek je již v keši (config.cache):
ac_cv_lib_fam_FAMOpen=${ac_cv_lib_fam_FAMOpen=no}
Mohu tedy smazat tento řádek nebo celou keš a opakovat konfiguraci. A pokračovat…
checking for FAMOpen in -lfam… yes
…
checking whether OpenSSL uses rsaref… no
checking for easter eggs… none found
…
Nevím přesně, co je rsaref (předpokládám že jakási podpora šifry RSA), a nehodlám jej instalovat. Pouze si zapíšu do svého zápisníku, že budu-li někdy instalovat něco, co se takto jmenuje, budu rekompilovat i kdelibs.
Velikononí vajíčko? Nu, zde se jedná o škodolibost autorů. Je to zřejmě chyták na nezkušené, kteří začnou na disku a Internetu hledat program jménem Easter Egg.
A tak plyne výpis až k závěrečnému:
updating cache ../config.cache
configure: creating ./config.status
config.status: creating Makefile
config.status: creating config.h
config.status: config.h is unchanged
Good – your configure finished. Start make now
Vše proběhlo podle očekávání a nenarazili jsme na žádnou chybu. Ne vždy je to tak jednoduché. Ale o tom až příště…