Logo GNU
Kodovani P°edchozφ Nßsledujφcφ Obsah

4. P°ekladaΦ

Nejd∙le╛it∞j╣φ GNU program je nesporn∞ p°ekladaΦ. Ten umo╛nil, aby vzniklo v╣echno ostatnφ. Jednß se o jeden z nejkvalitn∞j╣φch p°ekladaΦ∙ v∙bec - je snadno p°enositeln² na novou architekturu (jednß se pouze o napsanφ n∞kolika soubor∙ s popisem architektury a pro generovßnφ assembleru), umφ p°eklßdat mnoho r∙zn²ch jazyk∙ (nenφ tedy t°eba jeden p°ekladaΦ pro Pascal a jin² pro C) a mß v²born² optimizer.

Klasick² p°ekladaΦ nap°ed naΦte vstup, rozlo╛φ ho na tokeny a porozumφ mu. Kdy╛ u╛ vφ, co programßtor napsal, p°id∞lφ pam∞╗ovß mφsta prom∞nn²m (modern∞j╣φ pou╛φvajφ i registry) a potom pomocφ jak²chsi prefabrikßt∙ na ka╛d² p°φkaz jazyka slo╛φ v²sledn² k≤d. P°φpadn∞ se je╣t∞ pokusφ p°edvyhodnotit v²razy, nebo se rozhlΘdne po vzniklΘm assembleru a vym∞nφ pßr instrukcφ, kterΘ se Φasto ╣patn∞ generujφ. Vzniklou sklßdaΦku potom po╣le dßl.

P°ekladaΦ od GNU mß jinΘ °e╣enφ. Nejprve porozumφ programu. Toto je jedinß Φßst zßvislß na jazyce a naz²vß se parsing. Potom se vygeneruje mezijazyk zvan² Register transfer language (RTL). Od tohoto okam╛iku u╛ je p°ekladaΦi jedno, jestli kompiluje C, nebo Pascal. Mezijazyk je podobn² lispu. Tedy ideßlnφ pro strojovΘ zpracovßnφ, proto╛e v╣echny p°φkazy majφ stejnou syntax (li╣φ se pouze akcφ) a obsahujφ dodateΦn∞ ╣ikovnΘ informace, jako t°eba co pot°ebuje za vstup a v²stup a co modifikuje. V╣echny prom∞nnΘ a pam∞╗ jsou unifikovßny do registr∙ (t°eba pole je takΘ registr). Jazyk nenφ reprezentovßn textov∞, ale ve specißlnφ struktu°e tak, aby se s nφm dob°e pracovalo. Je mo╛nΘ ulo╛it jej do textovΘ podoby.

Je pravda, ╛e existujφ i jinΘ p°ekladaΦe, kterΘ majφ mezik≤d, ale je to spφ╣e vyjφmka.

Nßsledujφcφ p°iklad:

      main()
      {
        return(0);
      }
Mß mezik≤d

Potom se dostanou ke slovu optimalizßtory. Valnß v∞t╣ina je nezßvislß na architektu°e (procesoru), proto╛e ani RTL se zatφm moc asembleru nepodobß (snad a╛ na jmΘna registr∙). Optimalizßtor∙ je hodn∞. Tak╛e jenom krßtk² popis:

Optimalizace skok∙

zru╣enφ skok∙ na skoky

Scanovßnφ registr∙

tady se najde, kdy kter² registr (krycφ nßzev pro prom∞nnou) byl pou╛it poprvΘ a naposled

Optimalizace skok∙ na stejn² nebo opaΦn² test

ty mohou b²t vynechßny

Propagace konstant, eliminace v²raz∙

propagace konstant se sna╛φ dosadit konstanty za registry. Odstra≥uje v²razy bez v²stup∙ a vedlej╣φch efekt∙ a vyhodnocuje v²razy bez vstup∙. To v²razy zkracuje, vyhodφ nepot°ebnΘ Φßsti programu atd.

Optimalizace smyΦek

Sna╛φ se v╣echno, co jenom jde, dostat mimo smyΦku. Znova prob∞hne eliminace expresion.

Anal²za toku dat

Rozd∞lφ program do blok∙, vyhazuje nedosa╛itelnΘ smyΦky, a zji╣╗uje, kde kter² registr je ╛iv² - to znamenß, ╛e je t°eba uchovßvat jeho hodnotu - najde v²poΦty, kde v²sledek nenφ pou╛it.

Kombinace instrukcφ

(ne assemblerov²ch, ale t∞ch RTL) Spojuje instrukce, kterΘ jdou sjednotit do jednΘ, pou╛φvß algebru na optimalizaci matematick²ch v²raz∙ a p°id∞lφ RTL k≤du opravdovΘ instrukce, kterΘ odpovφdajφ popisu procesoru. (ano, tato Φßst je ΦßsteΦn∞ zßvislß na architektu°e).

Scheduling instrukcφ

posklßdß instrukce pro superskalßrnφ riscovΘ procesory tak, aby bylo mo╛nΘ jich provßd∞t vφce narßz. Verze 2.8.0 (kterß je╣t∞ nenφ) to bude d∞lat i pro Pentia a Pentia pro.

T°φdy registr∙

hardwarovΘ registry majφ n∞kolik t°φd. Tak t°eba pam∞╗ (zßsobnφk), registry procesoru, registry koprocesoru. Tato Φßst rozhodne, kterΘ t°φdy registr∙ lze pou╛φt pro ten dan² pseudoregistr.

Alokace registr∙

tady se koneΦn∞ rozhodne, kterΘ prom∞nnΘ budou ve v²slednΘm programu ulo╛eny a kdy.

Globßlnφ alokace registr∙

to samΘ, ale pro globßlnφ prom∞nnΘ.

Reloading

tady se dosadφ hardwarovΘ registry registr∙m a zbytek se ulo╛φ do zßsobnφku.

Dal╣φ scheduling

aby se instrukce nehßdaly o pam∞╗, potom se opakuje optimalizace skok∙ a je╣t∞ jednou scheduling

optimalizace zßsobnφkov²ch registr∙

to je specißln∞ pro koprocesor i386

P°evod do assembleru

a dφlo je hotovo.

Nß╣ p°φklad po optimalizaci

Vyrobit assembler je u╛ snadnΘ: (Je v  AT&T syntaxi . Ale snad to je jasnΘ)

      main:
             xorl %eax,%eax
             ret

A je╣t∞ jeden pon∞kud slo╛it∞j╣φ p°φklad:

      main()
      {int i,a=1,b=0;
          for(i=0;i<9999;i++)
          a=a+b,b=2*a;
          printf("%i\n",a);
      }

V²sledek z  GCCBorland C.

4.1 Srovnßnφ s ostatnφmi

K test∙m jsem se rozhodl pou╛φt v²poΦet Mandelbrotovy mno╛iny. Je to netrivißlnφ, koprocesor vyu╛φvajφcφ smyΦka z reßlnΘho ╛ivota. Tak se p°ekladaΦ mß co Φinit.

Zdrojov² k≤d v  C, a v  Pascalu

Pro testy jsem pou╛il Pentium/100 na 120, 512KB burst cache pod DOSem s himemem. Sna╛il jsem se v╣echny p°ekladaΦe donutit k co nejlep╣φm v²sledk∙m. Program jsem se sna╛il napsat co nejrychlej╣φ.

Kompilßtor p°epφnaΦe SmyΦek za sekundu
pgcc2.7.2p -O6 -ffast-math -mpentium -frisc -fomit-frame-pointer -funroll-loops-fopt-reg-use -frisc 5 464 480
egcs-1.0 -O3 -ffast-math -mpentium -fomit-frame-pointer -funroll-loops 5 405 405
egcs-1.0+HAIFA -O3 -ffast-math -mpentium -fomit-frame-pointer -funroll-loops 5 005 005
egcs-1.0 -O3 -ffast-math -m486 -fomit-frame-pointer -funroll-loops 4 807 692
gcc2.6.3 -O3 -ffast-math -m486 -fomit-frame-pointer -funroll-loops 4 233 641
gcc2.7.2.1 -O3 -ffast-math -m486 -fomit-frame-pointer -funroll-loops 419 377
gpc2.0 -O3 -ffast-math -m486 -fomit-frame-pointer -funroll-loops 3 433 962
vc5.0 (optimalizace na max) 3 060 908
wc10.0 -7 -5 -ol -ol+ -om -op -or -ot -fp5 -fpi87 2 222 222
wc10.0 -5 -7 2 217 249
delphi max. speed 1 807 288
plan9 default 1 623 376
vc1.0 (v IDE-586,speed+size optim.) 1 531 393
bc4.5 (v IDE-486,fastest executable) 1 455 604
bc3.1 (v IDE-386,fastest executable) 1 433 070
gcc2.7.2.1 (bez optimalizacφ) 1 281 348
gpc2.0 (bez optimalizacφ) 1 109 756
bp7.0 max. speed 901 713
tc2.0 -1 -f87 -O -r -Z -G -a -mt 846 511
bc4.0 (v IDE-486,speed optimizations) 755 857
bc2.0 (bez optimalizacφ) 716 535
bc2.0 -G -O -2 -Z -r 716 535
tc2.0 max. speed 546 546
bc4.0 (v IDE-486,speed+size optim.) -3.6676456...
Srovnßnφ rychlostφ na Pentiu ve floating point verzi
Vc znamenß Visual C++ 1.0/5.0. GCC 2.6.3 bylo pou╛ito v DJGPP 1.0. Wc znamenß Watcom C 10.0. Plan9 je kompilßtor ve stejnojmennΘm OS (kter² je mimochodem takΘ velice zajφmav²). Gpc je GNU Pascal compiler. Egcs je od╣t∞penß v∞tev od v²voje GCC 2.8.0, mß ale navφc n∞kterΘ optimalizace a nov² HAIFA scheduler od IBM. Mß za ∙kol vytvo°it GCC 3.0.0, kterΘ by m∞lo mφt v²razn∞ lep╣φ podporu superskalßrnφch procesor∙, zejmΘna Pentia, PentiPro, Pentia II, UltraSparcu, Alphy a PowerPC. Ma takΘ n∞kolik nov²ch optimalizaΦnφch pr∙chod∙ (nap°iklad globßnφ eliminaci v²raz∙). PGCC je GCC pro Pentium, ktere p∙vodn∞ zaΦal vyvφjet Intel. Potom co dosßhl 30% urychlenφ ve smφ╣en²ch benchmarkßch, nechal v²voje a uvolnil zdrojßky. V²voje se ujala skupina jmΘnem Pentium Support Group, kterß odstranila velkou Φßst chyb, kterΘ Intel zavedl, p°idala podporu PentiaPro, k5, k6 a Cyrixe. Pou╛itelnß Φßst zm∞n v PGCC bude brzo p°idßna do EGCS. ProblΘm PGCC je na GNU pom∞ry vysokß nestabilita a to, ╛e v n∞kter²ch p°φpadech oproti starΘmu GCC zpomaluje, proto p°idßvßnφ zm∞n do EGCS pravd∞podobn∞ bude chvφli trvat.

Jak je vid∞t, Borland C je 5.77krßt pomalej╣φ. Pascal je na tom o n∞co lΘpe, tedy 4.58 krßt. Assemblerov² zdrojßk se mi poda°ilo zφskat jenom z  Borland CGCC . NapoΦφtal jsem 20 instrukcφ v internφ smyΦce u GNU a 35 u Borland C. Nov∞j╣φ Borland C se celkem slu╣n∞ vylep╣il. Ve verzi 4.5 je u╛ ,,pouze'' 2.6krßt pomalej╣φ. Naprosto m∞ ale p°ekvapil test ve verzi 4.0. Tam se zpomalil a po zapnutφ optimalizacφ na velikost se program natolik zrychlil, ╛e zφskal zßporn² Φas. Nevφm, jak n∞kdo m∙╛e kompilßtor s tolika chybami pustit do sv∞ta. Dal╣φ zajφmavß v∞c ohledn∞ Borland C je to, ╛e ve verzi 3.1 byl .exe dlouh² 14KB, ve verzi 4.0 u╛ 48KB a v 4.5 cel²ch 61KB. V poslednφch Borland C jsem napoΦφtal 34 instrukcφ v internφ smyΦce. FractalZoom, kter² mß smyΦku v assembleru se py╣nφ 22 instrukcemi a jeho test °φkß 3.9 milion∙ smyΦek za sekundu. ZajφmavΘ je, ╛e starΘ GCC vy╣lo z testu o 2.2% lΘpe. Je to tφm, ╛e jako novΘ DJGPP jako jedinΘ poΦφtß 18.2 tik∙ do sekundy mφsto 18. Watcom C p°esto, ╛e na rozdφl od GCC podporuje Pentium, schedulding instrukcφ pro Pentium a fp optimalizace pro Pentia, bylo tΘm∞° dvakrßt pomalej╣φ. Je╣t∞ v∞t╣φho zrychlenφ dosßhla v²vojovß verze GCC - egcs pomocφ unroolovßnφ smyΦky, proto╛e Pentiov² koprocesor opravdu ╣patn∞ snß╣φ skoky. P°i zapnutφ optimalizacφ pro 480 se smyΦka takΘ unrooluje, ale proto, aby bylo mΘn∞ fxch intrukcφ, kterΘ na 486 zabφrajφ Φas(na pentiu u╛ ne). Navφc koneΦnß podmφnka je zkombinvanß s poΦßteΦnφ dal╣φho testu, tak╛e se u╣et°φ jeden skok.

Provedl jsem je╣t∞ testy na SUNech a Siliconech GCC versus p°ekladaΦ dodßvan² s OS. V²sledky jsou v tabulce 2 a 3

Kompilßtor p°epφnaΦe SmyΦek za sekundu
gcc2.7.0 -O3 -ffast-math -fomit-frame-pointer -funroll-loops 925 925
cc -O  840 336
Srovnßnφ rychlostφ na SUNovi
Kompilßtor p°epφnaΦe SmyΦek za sekundu
gcc2.7.0 -O3 -ffast-math -fomit-frame-pointer -funroll-loops 2 688 172
cc -O  2 096 436
Srovnßnφ rychlostφ na SGI

Musφm °φct, ╛e v²sledky m∞ opravdu p°ekvapily, proto╛e jsou mnohem vyrovnan∞j╣φ, ne╛ u DOSov²ch p°ekladaΦ∙. Napadly m∞ nßsledujφcφ vysv∞tlenφ:

Proto jsem ud∞lal dal╣φ test. Nahradil jsem long double za int. Te∩ je u╛ celß matematika jednoduchß i na Intelu. V²sledky jsou v tabulce 4.

Kompilßtor p°epφnaΦe SmyΦek za sekundu
pgcc2.7.2p -O6 -ffast-math -mpentium -frisc -fomit-frame-pointer -funroll-loops-fopt-reg-use -frisc 3 464 480
egcs-1.0+HAIFA -O3 -ffast-math -mpentium -fomit-frame-pointer -funroll-loops 3 331 258
egcs-1.0 -O3 -ffast-math -mpentium -fomit-frame-pointer -funroll-loops 3 311 258
gcc2.7.2.1 -O3 -ffast-math -m486 -fomit-frame pointer -funroll-loops 3 250 000
wc10.0 -fpi87 -fp5 -5 -7 -ol -ol+ -om -on -or -ot 3246 753
wc10.0 -5 -7 3 194 888
plan9 default 2 973 176
gpc2.0 -O3 -ffast-math -fstrength reduce -fomit-frame pointer -funroll-loops 2 888 888
gcc2.7.2.1 (bez optimalizace) 2 394 736
gpc2.0 (bez optimalizace) 2 219 512
bc2.0 -G -O -2 -Z -r 2 166 666
bp7.0 max. speed 1 956 947
tc2.0 -1 -O -r -G -Z -a -mt 892 156
tc2.0 max. speed 846 511
Srovnßnφ rychlostφ integerovΘ verze
V²sledek m∞ op∞t p°ekvapil. Zpomalenφ u GCC jsem neΦekal. Po prozkoumßnφ assembleru jsem zjistil, ╛e tam nenφ nic, co bych byl schopn² n∞jak radikßln∞ zoptimalizovat - v╣echno je v registrech a smyΦka se zkrßtila na 14 instrukcφ. Pentium mß mul pomalej╣φ ne╛ fmul a tak se v²sledek asi dal oΦekßvat. Samoz°ejm∞ to neplatφ o 486. Experimentßlnφ pgcc sice smyΦku trochu p°erovnß, ale pom∙╛e to jen o cca 1%.

Po°ßd je ale Borland C o hodn∞ pomalej╣φ. To, ╛e GCC by generovalo na mipsech hor╣φ k≤d se mi zdß nepravd∞podobnΘ, proto╛e optimalizery jsou z velkΘ Φßsti nezßvislΘ na architektu°e. A tak si myslφm, ╛e to bude tak p∙l na p∙l mezi lep╣φ architekturou a tφm, ╛e UNIXovΘ kompilßtory b²vajφ kvalitnφ.

Myslφm, ╛e testy mluvφ samy za sebe a ukazujφ, ╛e tady GNU odvedlo opravdu dobrou prßci. Psanφ rutin v assembleru, aby byly rychlej╣φ u╛ skoro nemß smysl. Je to d∙vod, proΦ programy v Linuxu b²vajφ rychlej╣φ (klasick² p°φklad je Doom). Mß to bohu╛el i nev²hodu. P°e╛havenΘ stroje Φasto programy kompilovanΘ pomocφ GCC nevydr╛φ (moje 486DX2 normßln∞ pracujφcφ v pokojovΘ teplot∞ se p°i kompilaci jßdra zah°eje tak, ╛e na nφ neudr╛φm ruku). LidΘ si Φasto myslφ, ╛e je to chyba programu. TakΘ proces kompilace je docela slo╛it² a tak to kompilßtoru chvφli trvß.

Poslednφ dost d∙le╛itß v∞c je to, ╛e oproti Borland C je GCC tΘm∞° bez chyb. B∞hem ╣estiletΘho pou╛φvßnφ jsem narazil jen asi na t°i problΘmy. Jinak v╛dy v╣echno fungovalo.


P°edchozφ Nßsledujφcφ Obsah

Dotazy a p°ipomφnky ohledn∞ strßnky posφlejte na hubicka@paru.cas.cz