You are on page 1of 29

FAKULTET ELEKTROTEHNIKE I RAUNARSTVA ZAVOD ZA ELEKTRONIKU, MIKROELEKTRONIKU, RAUNALNE I INTELIGENTNE SUSTAVE

SEMINARSKI RAD:

OPTIMIZACIJA DE CASTELJAUOVOG ALGORITMA VEKTORIZACIJOM

Predmet: Raunalna grafika Nastavnik: prof. dr. sc. eljka Mihajlovi Student: Miroslav tampar, dipl. ing. MB: R-10/2008 Datum: 03.10.2010.

Sadraj
1. Uvod...................................................................................................................................1 2. Teorijski dio........................................................................................................................2 2.1.1. Optimizacija.........................................................................................................2 2.1.2. Vektorizacija........................................................................................................3 2.1.3. Bzierove krivulje................................................................................................5 2.1.4. De Casteljauov algoritam....................................................................................6 2.1.5. SSE vektorski skup instrukcija............................................................................8 3. Praktini dio......................................................................................................................12 4. Rezultati i rasprava..........................................................................................................17 5. Zakljuak..........................................................................................................................19 6. Literatura..........................................................................................................................20 Dodatak A: Koriteni programski kod..................................................................................22 A.1. Test.cpp....................................................................................................................22 A.2. Makefile....................................................................................................................26

1.Uvod

1. Uvod
List Forbes je devedesetih godina prolog stoljea meu deset zakona modernog doba [1] svrstao tzv. Andyjev i Billov zakon koji glasi: to Andy daje, Bill uzima k sebi. U to doba svaki put kad bi Intel na elu s Andy Groveom predstavio novi procesor na tritu Microsoft bi na elu s Bill Gatesom odmah nadogradio svoj softver i opteretio novi procesor u potpunosti. Ipak, iza te reenice skriva se jedna velika istina. Mooreov zakon do sada je konstantno omoguavao razvoj novog softvera i otvaranje novih mogunosti. No, premda se do sada taj zakon (o udvostruenju procesorskih performansi svakih 18 mjeseci) odravao sirovim podizanjem frekvencije, od 2004. godine dolo je do naglih promjena. Intel je, naime, tada izdao i trenutno najbri procesor (Pentium 4 na 3.8GHz [2]), i od tada slijedi prekid rasta u tom pogledu. Radi toga su se proizvoai procesora morali okrenuti drugim mogunostima kako bi Mooreov zakon barem prividno odrali na ivotu. Jedan od smjerova na ijem se razvoju u posljednje vrijeme uurbano radi jest vektorizacija. U ovom e se uratku pokuati predstaviti osvrt te kroz optimizaciju De Casteljauovog algoritma predstaviti mogunosti ove vrste optimizacije.

2.Teorijski dio

2. Teorijski dio
2.1.1. Optimizacija
U raunalnoj znanosti programska optimizacija naziv je za proces u kojem se sustavnom promjenom obavlja promjena odreenih dijelova programa u svrhu poveanja efikasnosti odreenih aspekata izvoenja i/ili smanjenja koritenih resursa [3] . Openito govorei, raunalni program moe se optimirati kako bi se izvavao bre, kako bi se smanjila uporaba memorijskog prostora i/ili drugih resursa ili kako bi se smanjila uporaba elektrine energije. Postoji vie razina optimizacije poredanih silazno po stupnju apstrakcije: dizajnerski, kodni, prevodilaki, asemblerski i izvodilaki. Vanost pojedine razine, kao i (preporuena) potrebna koliina utroenog vremena za njihovu primjenu, najee istovjetno prati navedeni poredak. Na najvioj se razini (dizajnerskoj) programska optimizacija izvodi prvenstveno dobrim odabirom algoritama i arhitekturom programskog sustava. Na kodnoj razini izuzetno je bitna kvaliteta implementacije odabranog algoritma te u dananje doba sve snaniji trend paralelizacije uvjetovan mogunostima novijeg sklopovlja (engl. hardware). Prevodilaka razina sastoji se od odabira i testiranja optimizacijskih mogunosti koritenog programskog prevoditelja, pri emu valja imati na umu da su noviji prevoditelji na ovom podruju sve kvalitetniji. Asemblerska razina optimizacije bila je vrlo popularna u prolom desetljeu, no u pravilu, u doba dananjih prevoditelja koji optimizaciju na ovoj razini izvode znatno bolje od veine programera, postaje sve slabije koritena. Krajnja razina, izvodilaka, odnosi se samo na interpretirane jezike poput Python-a i Java-e, odnosno na svojstva automatske optimizacije koritenog interpretera. Ukoliko je voeno rauna o kvalitetnom odabiru algoritma i arhitekturi programskog sustava na dizajnerskoj razini, od razina na kojem moe napraviti znaajnije zahvate programeru ostaje samo kodna. Na toj razini proces optimizacije obino zapoinje identifikacijom tzv. uskog grla (engl. bottleneck) u programu, kritinog dijela koji postaje glavni potroa potrebnih resursa. Kod ove vrste optimizacije moemo primijeniti tzv. Paretov princip koji kae da se 80% resursa obino koristi od strane 20% koda. To znai, umjesto da krenemo s optimizacijom cijelog programa i samim time vjerojatno smanjimo itljivost programskog koda, trebamo identificirati tih 20% sporog koda i usredotoimo se samo na njega. Raunalna paralelizacija naziv je za formu raunanja u kojoj se vie kalkulacija izvodi istovremeno vodei se principom da se veliki problemi mogu podijeliti na vie manjih, koji se zatim mogu izvesti konkurentno (paralelno). Paralelizam se koristi ve dulji niz godina veinom kod visoko-performansnog raunanja (engl. HighPerformance Computing), no veliki je interes zavladao tek zadnjih nekoliko godina zbog posljedica fizikih ogranienja daljnjeg frekvencijskog skaliranja. Tradicionalno se raunalni programi piu serijski. Takvi programi namijenjeni su izvoenju na jednom sredinjem procesoru (engl. Central Processing Unit). Zadani
2

2.Teorijski dio

problem u tom se sluaju dijeli na diskretne serije instrukcija koje se izvode jedna za drugom te se u jednom trenutku moe izvesti samo jedna instrukcija. U svom osnovnom obliku paralelizacija je naziv za simultano koritenje veeg broja raunalnih resursa kako bi se u to kraem vremenu rijeio zadani problem. Kako bi se mogao izvriti paralelno koritenjem vie procesorskih jedinica problem se dijeli na vie konkurentnih diskretnih dijelova. Svaki takav dio se nadalje dijeli na vie nizova instrukcija te se same instrukcije izvravaju simultano na razliitim procesorskim (ili vektorskim) jedinicama.

2.1.2. Vektorizacija
Jedan od najrairenijih i najee koritenih tipova paralelizacije je vektorizacija, poznata i kao SIMD (engl. krat. za Single Instruction Multiple Data) nain raunalnog rada po Flynnovoj taksonomiji [4]. U procesu vektorizacije dolazi do pretvaranja skalarnog jednodretvenog programa (SISD - engl. krat. za Single Instruction Single Data) u vektorski oblik u kojem se koritenjem posebnih instrukcija u jednom trenutku obrauje vie podataka. Prednost ove vrste optimizacije i njena snaga lei u injenici da veina dananjih procesora namijenjenih irokom tritu imaju podran barem jedan oblik ove vrste paralelizacije.

Slika 1: Grafiki prikaz razlike izmeu SISD i SIMD naina rada

Po nainu primjene raspoznajemo runu (engl. manual) i automatsku prevodilaku (engl. auto-compiler) vektorizaciju. Runoj vektorizaciji najee pristupamo koritenjem posebnih intrinzinih (svojstvenih) prevodilakih naredbi prilagoenih koritenju u jezicima ope namjene (npr. C/C++). Prevodilakoj vektorizaciji pristupamo koritenjem posebnih parametara koritenog prevoditelja. U tom sluaju programski prevoditelj sam automatski prepoznaje dijelove prevoenog programskog koda koji su prikladni za pretvaranje u vektorski oblik. Problem kod ovog oblika vektorizacije lei u injenici da dananji programski prevoditelji, iako su dosta napredovali u zadnjih desetak godina, prepoznaju vrlo mali broj programskih struktura pogodnih za ovu vrstu obrade. Sklopovlje s podrkom za izvoenje vektorskih instrukcija svoju popularnost meu irokom masom najvie duguje pojavi Intel-ovog MMX (engl. krat. za Multimedia Extensions) vektorskog skupa instrukcija na x86 arhitekturi. Iako su se vektorske
3

2.Teorijski dio

instrukcije mogle pronai i ranije u osobnim raunalima (npr. Altivec za IBM PowerPC i VIS za Sun SPARC), dolaskom Pentium MMX procesora 1996. godine te zahvaljujui prvenstveno agresivnom marketingu i novim mogunostima usmjerenim prvenstveno poboljanju performansi tadanjih video igara, zapoela je prava mala revolucija ijim posljedicama svjedoimo i danas. Vektorski skupovi instrukcija mogu se pronai vie manje na svim modernim procesorima, ukljuujui IBM-ov AltiVec i SPE za PowerPC, HP-ov PA-RISC MAX, Intel-ov MMX, iwMMXt, SSE, SSE2, SSE3 i SSSE3, AMD-ov 3DNow!, ARC-ov ARC Video, SPARC-ov VIS i VIS2, Sun-ov MAJC, ARM-ov NEON, MIPS-ov MDMX (MaDMaX) i MIPS-3D, IBM/Sony/Toshiba Cell (Playstation 3), itd. Valja napomenuti da su podrane veliine obraivanih vektora dananjih procesora mahom 128 i 256 bitova. Uz poveanje broja osnovnih vektorskih instrukcija te uvoenje specijaliziranih tzv. brzih instrukcija, Intel-ov novi skup AVX (engl. krat. za Advanced Vector Extensions) koristit e se za obraivanje vektora veliine 256 bitova, dok e Intel-ova arhitektura Larrabee omoguiti veliinu vektora od ak 512 bitova. to se tie trendova, Apple-ovim prebacivanjem na x86 arhitekturu 2006. godine, te jakim marketingom iz Intelovog tabora, najpoznatiji i najee koriteni vektorski skup instrukcija (u osobnim raunalima) definitivno postaje SSE (engl. krat. za Streaming SIMD Extensions) - direktan nasljednik skupa MMX. Trenutna verzija (SSE5) takoer podrava i sve inaice nastale nakon objave originalne verzije iz 1999. godine. Zanimljivo je da se u poecima ire primjene vektorizacije jo vodila kako takva bitka izmeu tabora Intel-a, AMD-a i IBM-a, no dvije crtice iz prolosti bile su presudne. AMD je u poetku imao jak odgovor na Intel-ov MMX dodatni skup instrukcija u obliku skupa 3DNow!, no u svrhu postizanja maksimalne kompatibilnosti sa svojim glavnim rivalom Intelom, elni ljudi iz AMD-a su 2000. godine odluili da e podrati obje tehnologije. Uz to to sam Intel dri velik udio trita prevoditelja, kasnije e se uvidjeti da je ova odluka samo doprinijela polakom izumiranju AMDovih vektorskih ekstenzija (3DNow!, XOP, FMA4, CVT16). Drugi bitan dogaaj definitivno je prelaenje Apple-ovog MacOS-a s dosadanje IBM-ove PowerPC arhitekture iskljuivo na Intel-ovu x86 arhitekturu s obaveznom podrkom za SSE2 instrukcijski skup. O razlozima prelaska na ovu arhitekturu i dan se danas nagaa, no razlog obavezne podrke za vektorskim SSE2 instrukcijama lei u injenici da se prethodno napisani raunalni programi, koji koriste (sad ve zastarjele) AltiVec instrukcije, mogu prebaciti na novu arhitekturu koritenjem jednostavnog skupa pravila bez znaajnijih gubitaka u funkcionalnosti [5]. Koritenjem vektorizacije u svrhu znaajnijih poboljanja performansi teoretski moemo dobiti ubrzanja jednaka broju komponenata u podranim vektorskim veliinama. Tako na primjer, ukoliko su osnovne komponente (operandi) veliine 32 bita, a vektora 128 bita, teoretsko ubrzanje iznosi 4. Najvea teoretska ubrzanja mogu se tako oekivati kod vektorizacije dosadanje skalarne obrade teksta, ifriranja podataka te filtera za obradu slike, najee zbog njihove male veliine operanada.
4

2.Teorijski dio

U stvarnosti, teorija i praksa nisu isto. Za postizanje ubrzanja to blieg teoretskom, potrebno je zadovoljiti dosta uvjeta. Izmeu ostalih ovdje spadaju: modifikacija programa/algoritma u vektorski oblik, pretvorba ulaznih i izlaznih podataka u prikladnu formu, izbjegavanje mijeanja vektorskih i skalarnih instrukcija zbog esto negativnih posljedica po performanse, minimalizacija broja instrukcija s memorijskim pristupom, optimalno rasporeivanje instrukcija ovisno o meuovisnosti koritenih operanada, optimalno koritenje vektorskih registara, izbjegavanje pristupa neporavnatim (engl. unaligned) memorijskim lokacijama, zadovoljenje opih pravila za optimiranje koda niske razine [6]... Radi velikog broja uvjeta koji moraju biti zadovoljeni kako bi se dobili zamjetni rezultati, ova se vrsta paralelizacije koristi samo u rijetkom broju sluajeva i to za ubrzanje za to pogodnih algoritama. Jedan od takvih algoritama je De Casteljauov.

2.1.3. Bzierove krivulje


Bzierove krivulje su forme sveprisutne u raunalnoj grafici. U teoretsku matematiku implicitno su ih uveli puno prije raunala dvojica matematiara: Francuz Charles Hermite te Rus Sergei Bernstein. No, za njihovu dananju popularnost i jednostavnost primjene prvenstveno su zasluni Pierre Bzier ( Renault) te Paul De Casteljau (Citroen) zaposlenici u auto industriji 60-ih godina prolog stoljea. Bzierove se krivulje esto koriste u raunalnoj grafici za modeliranje tzv. glatkih (engl. smooth) krivulja. Poto je takva vrsta krivulje u cijelosti sadrana u konveksnoj ljusci svojih kontrolnih toaka, toke se mogu grafiki prikazati i iskoristiti za intuitivno upravljanje krivuljom. Takoer, afine se transformacije, kao to su translacija i rotacija, mogu primijeniti na krivulju primjenom odgovarajuih transformacija nad pripadnim kontrolnim tokama. Kvadratne i kubne Bzierove krivulje su najee; krivulje vieg stupnja rjee se koriste jer su zahtjevnije za procjenu. Kada su potrebni sloeniji oblici, koriste se Bzierove krivulje nieg stupnja spojene zajedno u tzv. stazu (engl. path). Kako bi se objasnio princip rada De Casteljauovog algoritma, najprije je potrebno upoznati osnove Bzierovih krivulja za iju se kalkulaciju koristi izmeu ostalih i ovaj algoritam. Bzierova krivulja stupnja n moe se generalizirati na sljedei nain. S obzirom na tzv. kontrolne toke P 0, P 1,... , P n , Bzierova krivulja zadana je sljedeom jednadbom: B t = = = 1t t P n i 1 t P n 1t t P 1 n 1t t P t P n 1

i=0

n i i

n 1

n 1

n 1

Tako na primjer za

n =3 dobivamo kubnu Bzierovu krivulju:

B t =1 t 3 P0 3 1 t 2 t P 13 1t t 2 P 2t 3 P 3
5

2.Teorijski dio

Ova se (generalizirana) formula moe prikazati rekurzivno na sljedei nain. Neka B P P ... P t predstavlja Bzierovu krivulja zadanu tokama P 0 , P 1 , ... , P n . Tada vrijedi rekurzivna formula:
0 1 n

B t = B P

P1 ... P n

t = 1t B P

P 1 ... Pn 1

t t B P

P2 ... P n

t .

Drugim rijeima, Bzierova krivulja n-tog stupnja linearna je interpolacija izmeu dviju Bzierovih krivulja stupnja n-1.

Slika 2: Kubna Bzierova krivulja

U terminologiji se moe pronai i sljedei prikaz: B t = bi , n t Pi , t [ 0,1 ]


i=0 n i n i gdje su polinomi b i , n t = t 1t , i = 0,... , n poznati i kao Bernsteineovi bazni i polinomi stupnja n, pri emu je zadano t 0=1 i 1t 0=1 . n

2.1.4. De Casteljauov algoritam


U matematikom polju numerike analize De Casteljauov algoritam, nazvan po svom pronalazau Paul De Casteljau, naziv je za rekurzivnu metodu procjene polinoma u Bernsteinovoj formi i/ili Bzierovim krivuljama De Casteljauov algoritam definiramo na sljedei nain. Ako se polinom B moe prikazati u Bernsteinovoj formi stupnja n na sljedei nain: B t = i bi , n t
i=0 n

pri emu je s bi,n oznaen Bernsteinov bazni polinom, tada se njegova vrijednost u toki t0 moe izraunati koritenjem rekurzivne relacije: i : =i , i =0,. .. , n j1 i j : =i j1 1 t 0 i 1 t 0 , i = 0,. .. , n j , j = 1,... , n
0

2.Teorijski dio

Raunanje vrijednosti B u toki t0 moe se izraunati koritenjem algoritma u n koraka. Rezultat B(t0) je zadan kao:
B t 0 =0
n

Na primjer, recimo da elimo izraunati Bernsteinov polinom stupnja 2 s Bernsteinovim koeficijentima u toki t0: 00 =0 10 =1 0 2 =2 Zapoinjemo rekurziju na sljedei nain: 0 =0 1t 0 1 t 0=0 1t 0 1 t 0 11 =10 1t 0 20 t 0=1 1 t 02 t 0 te u sljedeoj iteraciji rekurzija zavrava s rezultatom: 02 = 01 1 t 011 t 0 = 0 1 t 0 1t 0 1 t 0 1 t 01 1 t 0 t 0 2 t 0 t 0 = 0 1 t 021 2 t 0 1 t 0 2 t 2 0 to je oekivani Bernsteinov polinom stupnja n. Moda je najbolji nain za objasniti rad De Casteljuovog algoritma grafiki kroz primjer. Kubna Bzierova krivulja ima etiri kontrolne toke. Na poetku algoritma poveemo te etiri toke s tri linije (prikazano crvenom bojom na slici 3). Nakon toga oznaimo sredine tih linija s tri nove toke (zelene boje).
1 0 0

Slika 3: Rezultat nakon prvog koraka De Casteljauovog algoritma

Nadalje, te tri nove toke poveemo s dvije nove linije (prikazano zelenom bojom na slici 4). Ponovno oznaimo sredinje toke (plave boje) tih linija.

2.Teorijski dio

Slika 4: Rezultat nakon drugog koraka De Casteljauovog algoritma

Na kraju poveemo zadnje dvije toke (plave boje) s linijom (prikazano plavom bojom na slici 5) i oznaimo sredinu te linije s tokom. Ta zadnja toka predstavlja jednu od toki Bzierove krivulje.

Slika 5: Rezultat nakon treeg (zavrnog) koraka De Casteljauovog algoritma

U ovom trenutku znamo izraunati koordinate jedne toke na Bzierovoj krivulji. Ukoliko elimo to isto uraditi za bilo koju toku na krivulji moramo prilagodit nain izrauna. To emo uraditi na nain da umjesto opisanog koritenja sredinjih toaka koristimo jednostavnu linearnu interpolaciju (jednadba pravca kroz dvije toke) na opisanim pomonim linijama. Na taj nain moemo pronai bilo koju toku na Bzierovoj krivulju, to odgovara zakljuku iz 2.1.3. da je Bzierova krivulja n-tog stupnja linearna interpolacija izmeu dviju Bzierovih krivulja stupnja n-1.

2.1.5. SSE vektorski skup instrukcija


U veljai 1999. godine Intel je predstavio Pentium III procesor u kojem je jedna od novina bila i nadogradnja postojeeg MMX (engl. krat. za MultiMedia Extensions) s novim SSE (engl. krat. za Streaming SIMD Extensions) vektorskim skupom instrukcija. Te instrukcije takoer su bile poznate i pod kraticom KNI (engl. krat. za

2.Teorijski dio

Katmai New Instructions) do konanog predstavljanja jer je Katmai bio kodni naziv za Pentium III procesor. SSE sadrava 70 novih instrukcija za grafiku i zvunu obradu kao novina naspram onoga to je dotad omoguavao MMX. SSE je vrlo slian skup MMX-u. Zapravo, prije nego to je objavljen, neki su ga jo i zvali MMX-2. Uz nove instrukcije sline dosadanjim iz skupa MMX, SSE omoguuje raunanje nad brojevima s pominim zarezom (engl. floating point), a za njihovo se izvravanje sada koristi poseban dio procesora umjesto dosadanje (MMX) metode dijeljenja standardne jedinice za brojeve s pominim zarezom (engl. krat. FPU - floating point unit). SSE se sastoji od 70 novih instrukcija, ukljuujui vektorske za rad s brojevima s pominim zarezom, dodatne vektorske za rad s cijelim brojevima (engl. integer) te kontrolnih instrukcija za upravljanje keiranjem (engl. cache management). SSE donosi takoer i 8 novih 128-bitnih registara XMM0-XMM7, podijeljenih na 4 32-bitne komponente s pominim zarezom. Dodatni kontrolni registar, MXCSR, je takoer na raspolaganju za kontrolu i provjeru stanja izvrenja pojedinih instrukcija. SSE instrukcije koriste ove 128-bitne registre, 64-bitne MMX registre MM0-MM7 te u nekim sluajevima i 32-bitne registre ope namjene (engl. general purpose registers).
128 bita

XMM0 XMM1 XMM2 XMM3 XMM4 XMM5 XMM6 XMM7


Slika 6: SSE vektor registri

U sljedeem je primjeru na jednostavan nain prikazana prednost koritenja SSE skupa instrukcija nad standardnim. Razmotrite operaciju kao to je vektorsko zbrajanje koje se vrlo esto koristi u raunalnoj grafici. Zbrajanje dva 4-komponentna vektora s jednostrukom preciznou zahtijeva koritenje etiri standardne x86 instrukcije FADD za zbrajanje brojeva s pominim zarezom:
fadd c.x, a.x, b.x fadd c.y, a.y, b.y fadd c.z, a.z, b.z //c.x := a.x + b.x //c.y := a.y + b.y //c.z := a.z + b.z

fadd c.w, a.w, b.w //c.w := a.w + b.w Programski kod 1: Zbrajanje vektora koritenjem standardnih instrukcija

S druge strane, kao to je prikazano u nastavku, jedna 128-bitna SSE vektorska instrukcija moe zamijeniti etiri skalarne instrukcije za zbrajanje:

2.Teorijski dio

movaps xmm0, <adresa od a> addps xmm0, <adresa od b>

//xmm0 := a.w | a.z | a.y | a.x //xmm0 := a.w+b.w | a.z+b.z | a.y+b.y | a.x+b.x

movaps <adresa od c>, xmm0 //c := xmm0 Programski kod 2: Zbrajanje vektora koritenjem SSE vektorskih instrukcija

Za potrebe praktinog uratka koritene su vektorske instrukcije iz SSE skupa instrukcija prvenstveno iz dva razloga: velika rasprostranjenost sklopovske podrke i podranost od strane veine dananjih programskih (C/C++) prevoditelja. No, zbog koritene metode pri vektorizaciji ovog algoritma ne bi smjelo biti prevelikih problema za uporabu i nekog drugog vektorskog skupa instrukcija. Kako bi se stekli temelji za razumijevanje koda iznesenog u praktinom uratku, u nastavku slijedi opis osnovnih koritenih SSE instrukcija. Intrinzina (engl. intrinsic) funkcija naziv je za funkciju koju prevoditelj direktno prevodi u niz od jedne ili vie asemblerskih (engl. assembler) instrukcija. Takva vrsta funkcija je inherentno efikasnija od obinih funkcija jer se pri njihovom izvravanju ne koristi uobiajeni mehanizam zvanja (engl. calling mechanism). Intrinzine funkcije omoguuju jednostavnije koritenje poboljanja specifinih za procesor, poput vektorskih instrukcija, jer osiguravaju suelje u visokom programskom jeziku (u naem sluaju C/C++) prema asemblerskim instrukcijama. Kako bi se to omoguilo prevoditelj umjesto korisnika automatski vodi rauna o stvarima poput imena koritenih registara, alokaciji registara te memorijskim lokacijama podataka. Intrinzina funkcija _mm_load_ps se koristi za uitavanje 4-komponentnog vektora (tip podataka __m128) s jednostrukom preciznou (engl. single precision) sa zadane adrese. Predviena je za brzo uitavanje vrijednosti iskljuivo s adresa poravnatih (engl. aligned) na 16 bajta:
__m128 _mm_load_ps(float * p); r0 := p[0] r1 := p[1] r2 := p[2] r3 := p[3] Programski kod 3: Zaglavlje intrinzine funkcije _mm_load_ps i pripadni pseudokod

Intrinzina funkcija _mm_set_ss slui za postavljanje najnie rijei vektor registra na zadanu vrijednost tipa float pri emu se ostale rijei postavljaju na vrijednost 0:
__m128 _mm_set_ss(float w); r0 := w r1 := r2 := r3 := 0.0 Programski kod 4: Zaglavlje intrinzine funkcije _mm_set_ss i pripadni pseudokod

Intrinzina funkcija _mm_add_ps vraa rezultat aritmetikog zbrajanja dvaju vektor registara:

10

2.Teorijski dio

__m128 _mm_add_ps(__m128 a, __m128 b); r0 := a0 + b0 r1 := a1 + b1 r2 := a2 + b2 r3 := a3 + b3 Programski kod 5: Zaglavlje intrinzine funkcije _mm_add_ps i pripadni pseudokod

Intrinzina funkcija _mm_sub_ps namijenjena je za aritmetiko oduzimanje dvaju vektor registara:


__m128 _mm_sub_ps(__m128 a, __m128 b); r0 := a0 - b0 r1 := a1 - b1 r2 := a2 - b2 r3 := a3 - b3 Programski kod 6: Zaglavlje intrinzine funkcije _mm_sub_ps i pripadni pseudokod

Intrinzina funkcija _mm_mul_ps namijenjena je za aritmetiko mnoenje komponenti vektor registara:


__m128 _mm_mul_ps(__m128 a, __m128 b); r0 := a0 * b0 r1 := a1 * b1 r2 := a2 * b2 r3 := a3 * b3 Programski kod 7: Zaglavlje intrinzine funkcije _mm_mul_ps i pripadni pseudokod

Svrha intrinzine funkcije _mm_shuffle_ps je selekcija odreenih dijelova zadanih vektora na temelju specificirane maske (detaljnije u [17]):
__m128 _mm_shuffle_ps(__m128 a , __m128 b , int mask ); Programski kod 8: Zaglavlje intrinzine funkcije _mm_shuffle_ps

Intrinzina funkcija _mm_movehl_ps vraa samo gornji dio zadanih vektor registara:
__m128 _mm_movehl_ps(__m128 a , __m128 b); r3 := a3 r2 := a2 r1 := b3 r0 := b2 Programski kod 9: Zaglavlje intrinzine funkcije _mm_movehl_ps i pripadni pseudokod

11

3.Praktini dio

3. Praktini dio
U praktinom dijelu ovog uratka bit e prezentiran rezultat vektorizacije De Casteljauovog algoritma koritenjem SSE vektorskog skupa instrukcija. Kao programski jezik koristio se C/C++, koriteno je programsko razvojno suelje GCC 4.4.1, dok se testna platforma bazirala na operativnom sustavu Ubuntu Linux 9.10. i raunalnom procesoru Intel Q6600 (4x2.4GHz) s ukljuenom podrkom za izvoenje SSE instrukcija. Prije samog postupka optimizacije napisan je program za raunanje toaka na Bezierovoj krivulji implementacijom osnovne varijante De Casteljauovog algoritma. Koritenjem zadanih kontrolnih toaka, proizvoljnog parametra t i postupka opisanog u teorijskom dijelu ovog uratka (2.1.4.) u mogunosti smo izraunati bilo koju toku na kubnoj 2D Bzierovoj krivulji uporabom sljedeeg programskog koda:
#include <stdio.h> struct point { float x; float y; }; // Bezierovu krivulju odreujemo s 4 kontrolne toke // u ovom su sluaju upotrijebljene iste one toke koritene za grafiki prikaz rada algoritma (2.1.4.) point a = { 40, 100 }; point b = { 80, 20 }; point c = { 150, 180 }; point d = { 260, 100 }; // jednostavna linearna interpolacija izmeu dvije toke void lerp (point &dest, point &a, point &b, float t) { dest.x = a.x + (b.x - a.x)*t; dest.y = a.y + (b.y - a.y)*t; } // izraunava koordinate toke na Bezierovoj krivulju zadane parametrom t // u rasponu [0.0, 1.0]

12

3.Praktini dio

void bezier (point &dest, float t) { point ab, bc, cd, abbc, bccd; lerp (ab, a, b, t); lerp (bc, b, c, t); lerp (cd, c, d, t); lerp (abbc, ab, bc, t); lerp (bccd, bc, cd, t); lerp (dest, abbc, bccd, t); 5) } // mali testni program koji slui za ispis dobivenih koordinata toki Bezierove krivulje void main (void) { point p; for ( int i=0; i<1000; i++ ) { float t = (float)i/999.0; bezier (p, t); printf ("%f %f\n", p.x, p.y); } } Programski kod 10: Osnovni program za raunanje toaka na kubnoj 2D Bzierovoj krivulji pomou De Casteljauovog algoritma // parametar t kojim se jednoznano odreuje // toka na Bezierovoj krivulji // toka izmeu a i b (zelena na slici 3) // toka izmeu b i c (zelena na slici 3) // toka izmeu c i d (zelena na slici 3) // toka izmeu ab i bc (plava na slici 4) // toka izmeu bc i cd (plava na slici 4) // toka na Bezierovoj krivulji (crna na slici

Iz gornjeg programskog koda moe se vidjeti da se funkcija bezier koristi za raunanje koordinata toaka koritenjem De Casteljauvog algoritma dok se pomona funkcija lerp koristi za izraunavanje linearne interpolacije izmeu dvije toke. Te dvije funkcije ine procesorski najzahtjevniji dio ovog programa te su kao takve idealni kandidati za optimizaciju. Ako znamo da su podaci tipa float veliine 32 bita, a SSE registri 128 bita, jasno je da ukoliko elimo upotrijebiti ovu vrstu optimizacije, maksimalno teoretsko ubrzanje koje moemo oekivati jest 4 puta. Kako bi se prezentirani programski kod mogao vektorizirati potrebno ga je raspisati u oblik prikladan za vektorsku implementaciju. Kao to je opisano u teorijskom dijelu ovog uratka, kod programske se optimizacije razmatraju promjene samo onih dijelova programa koji su resursno najzahtjevniji. U naem sluaju govorimo o funkciji bezier i pripadnoj pomonoj funkciji lerp.
13

3.Praktini dio

Ukoliko funkciju za linearnu interpolaciju lerp prikaemo u prikladnijem obliku lerp a , b = a b a t = a 1 t b t = a u b t , tada dani programski kod moemo raspisati u sljedeem obliku: dest = abbcu bccdt u =1t = abu bct u bcu cdt t = au bt u bu ct t u bu ct u cu dt t t 2 2 2 2 = au b2 t u c t u bu c2 t u dt t a u2 c t 2 u b 2 t u = t b c d

[ ] [ ] [ ] [ ]

Ova vektorska prezentacija De Casteljauovog algoritma predstavlja glavnu okosnicu ovog dijela uratka i idealna je za implementaciju koritenjem SSE vektorskog skupa instrukcija. Ako primijetimo da se kod originalne implementacije u pozivu funkcije bezier 6 puta zvala funkcija lerp, u kojoj se nadalje koristilo 6 aritmetikih operacija (2 zbrajanja, 2 oduzimanja i 2 mnoenja), tada moemo zakljuiti da se u tom sluaju sveukupno radilo o 36 aritmetikih operacija. U krajnje raspisanom sluaju imamo samo 11 aritmetikih operacija (2 zbrajanja, 8 mnoenja i 1 oduzimanje) to predstavlja teoretsko ubrzanje od 227%. a predstavlja vektor stupac sainjen od komponenti b sadranih vektora ( a x , a y , b x , b y ). Poto su u naem sluaju komponente x i y veliine 32 bita upotrijebljeni vektor stupci tono stanu u 128 bitne SSE registre. U zadanoj formi oblik U nastavku slijedi implementacija De Casteljauvog algoritma koritenjem SSE vektorskog skupa instrukcija. Linije su pobrojane kako bi se poslije mogao lake dati detaljan opis rada svake od njih pojedinano:
1. #include <xmmintrin.h> 2. 3. static void bezierSSE(float *array, float *result, float t) 4. { 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. R1 = _mm_set_ss(1); R5 = _mm_load_ps(&array[0]); R2 = _mm_set_ss(t); // [ab] // t // [1 1 1 1] // [t t t t] register __m128 R1, R2, R3, R4, R5, R6, R7; __m128* pResult = (__m128*) result;

[]

R1 = _mm_shuffle_ps(R1, R1, _MM_SHUFFLE(0, 0, 0, 0)); R2 = _mm_shuffle_ps(R2, R2, _MM_SHUFFLE(0, 0, 0, 0)); R6 = _mm_load_ps(&array[4]); R3 = _mm_sub_ps(R1, R2); // [cd] // u = 1-t

14

3.Praktini dio

16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34.

R1 = _mm_add_ps(R1, R1); R1 = _mm_mul_ps(R1, R2); R2 = _mm_mul_ps(R2, R2); R1 = _mm_mul_ps(R1, R3); R3 = _mm_mul_ps(R3, R3);

// 2 // [u u t t] // 2t // t^2 // 2tu // u^2 // [bc]

R4 = _mm_shuffle_ps(R3, R2, _MM_SHUFFLE(0, 0, 0, 0));

R7 = _mm_shuffle_ps(R5, R6, _MM_SHUFFLE(1, 0, 3, 2)); R6 = _mm_mul_ps(R6, R2); R5 = _mm_mul_ps(R5, R3); R7 = _mm_mul_ps(R7, R1); R6 = _mm_add_ps(R6, R5); R6 = _mm_add_ps(R6, R7); R1 = _mm_mul_ps(R6, R4); R2 = _mm_movehl_ps(R1, R1); *pResult = _mm_add_ps(R1, R2); // [cd] * t^2 // [ab] * u^2 // [bc] * 2tu

// [ab] * u^2 + [cd] * t^2 // (...) + [bc] * 2tu // (...) * [u u t t] = [x y z w] // [z w z w] // [x+z, y+w, ...]

35. } Programski kod 11: Optimirana SSE inaica funkcije za raunanje toaka na kubnoj 2D Bzierovoj krivulji pomou De Casteljauovog algoritma

U zaglavlju programa u kojem se koriste SSE intrinzine funkcije, pripadni tipovi podataka i/ili pomone makro naredbe, obavezno mora biti ukljuena datoteka xmmintrin.h (linija 1). Osnovni tip podataka u SSE programima zadan je s kljunom rijei __m128 (linija 5 i 7). U osnovama optimiranja C/C++ koda izmeu ostalog stoji da bi programer trebao nastojati kritine funkcije deklarirati kao statike koritenjem kljune rijei static (linija 3) te kljune lokalne varijable deklarirati kao registarske koritenjem kljune rijei register (linija 7) [6]. Deklarirane varijable R1..R7 u idealnom sluaju predstavljaju registarske SSE varijable bez uporabe mehanizma uvanja (na stogu). No, dekompajliranjem binarne prezentacije prevedenog programskog koda (koritenjem objdump Linux programa) jasno je vidljivo da u sluaju iskljuene optimizacije prevoditelja to nije sluaj. Naime, tada se usprkos deklariranju razliitih registarskih varijabli koristio mehanizam uvanja to je nepotrebno usporavalo izvoenje programa. Stoga je za postavku optimizacije proizvoljno uzeta druga razina optimizacije ( -O2), no rezultati su se pokazali pozitivnima i u ostalim sluajevima ( -O1 i -O3). Bitno je napomenuti da su se prilikom testiranja koristile identine postavke optimizacije i za potrebe prevoenja originalne verzije algoritma, prvenstveno kako bi se izbjegla mogua nekonzistentnost dobivenih rezultata.

15

3.Praktini dio

Prema jednom od naputaka [12] radi poveanja propusnosti SSE vektorskih instrukcija trebalo bi se voditi rauna o to veem ispreplitanju tzv. pakiranih (engl. packed) i nepakiranih (engl. unpacked) intrinzinih funkcija. Na taj je nain mogue izvesti vie instrukcija paralelno u istom trenutku. Razlika je u tome da se pakirane funkcije (sa sufiksom PS) izvravaju nad cjeloukupnim operandima, dok se nepakirane (sa sufiksom SS) izvravaju samo nad najniim rijeima operanada. Stoga se, umjesto koritenja pakirane funkcije _mm_set_ps za postavljanje sadraja vektora na konstantu vrijednost, koristila nepakirana funkcija _mm_set_ss u kombinaciji s (jeftinom) funkcijom _mm_shuffle_ps. Na ovaj se nain, iako se koristila pakirana funkcija _mm_shuffle_ps, dobilo par postotaka bre izvoenje koda.
__m128 _mm_set_ss(float w ); r0 := w r1 := r2 := r3 := 0 __m128 _mm_set_ps(float z , float y , float x , float w ); r0 := w r1 := x r2 := y r3 := z Programski kod 12: Razlika izmeu nepakirane i pakirane inaice intrinzine funkcije za postavljanje sadraja vektora na konstantu

Nadalje, jedna od prvih natuknica kod optimiranja SSE programskog koda definitivno je poravnavanje (engl. aligning) koritenih memorijskih adresa na 16 bajta ([12],[14],[15]). To znai da e u sluaju krenja ovog naputka doi i do vie nego osjetnog usporenja izvoenja programskog koda, pa u nekim sluajevima i do nasilnog prekidanja (npr. ako se u jednom takvom sluaju koristi funkcija _mm_load_ps opisana u programskom kodu 3). Stoga se radi potivanja navedenog i smanjenja moguih komplikacija oko prosljeivanja parametara s komponentama kontrolnih toaka dolo do kompromisnog rjeenja. Sve se komponente kontrolnih toaka zapisuju na jednom mjestu u obliku vektorskog stupca, odnosno, polja tipa float s poravnatom memorijskom adresom te se u tom obliku prosljeuju optimiranoj funkciji (Dodatak A.1.). Sadraj vektor stupca koritenog za spremanje komponenti kontrolnih toaka moe se prikazati u sljedeem obliku: [ a x , a y , b x , b y , c x , c y , d x , d y ] . Ako znamo da je veliina svake komponente 32 bita, tada lako moemo obrazloiti nain njihovog uitavanja u SSE registre (linije 10 i 14 u programskom kodu 11). a c i s b d pripadnih adresa (&array[0] i &array[4]) poravnatih na 16 bajta. Trei traeni vektor stupac b dobiven je selekcijom (linija 23 u programskom kodu 11) donjeg dijela c Koritenjem funkcije _mm_load_ps uitani su registri sadrajima

[] []

[]

16

3.Praktini dio

prvog i gornjeg dijela drugog prethodno uitanog vektora koritenjem intrinzine funkcije _mm_shuffle_ps opisane u programskom kodu 8. U nastavku programskog koda slijedi raunanje pojedinih komponenti sadranih u prethodno raspisanoj jednadbi. Prvenstveno se vodilo rauna o meuovisnosti koritenih registara operanada te minimalizaciji broja koritenih registara radi izbjegavanja sporog mehanizma memorijske pohrane od strane programskog prevoditelja. Broj koritenih SSE registara smanjen je na 7 (linija 7 u programskom kodu 11) od moguih 8 te se u svim sluajevima uklonila registarska meuovisnost susjednih instrukcija [19] (RAW engl. krat. za Read-After-Write, WAR engl. krat. za Write-After-Read, WAW engl. krat. za Write-After-Write) gdje je to ikako bilo mogue. Na kraju su se zbrojila dva dvodimenzionalna (pod)vektora sadrana u registru R1 (linije 32 i 34) te se dobiveni rezultat zapisao u izlazno polje tipa float kao konaan rezultat izvoenja funkcije. Bitno je napomenuti da se radi ubrzanja rada funkcije rezultat nije izvlaio iz donjeg dijela konanog vektora te se stoga u izlaznom polju zadnja dva broja mogu slobodno zanemariti (predstavljeno tokicama u liniji 34 u programskom kodu 11).

4. Rezultati i rasprava
Za potrebe testiranja program se iz dodatka A.1. izveo 100 puta uzastopno. Rezultat svakog izvoenja bila su dva broja: broj procesorskih taktova potrebnih za raunanje toaka na Bezierovoj krivulji s rezolucijom od 10000 toaka koritenjem originalne funkcije te broj procesorskih taktova potrebnih za istu svrhu no koritenjem optimirane verzije funkcije. Grafiki prikaz dobivenih rezultata moe se pronai na slici 7. Na slici se takoer mogu vidjeti rezultati koji bi se dobili u teoretskom sluaju optimizacije funkcije, dobiveni skaliranjem vremena izvoenja originalne funkcije s vrijednou teoretskog ubrzanja navedenog u prethodnom poglavlju (227%). Kao to je vidljivo iz rezultata, optimirana funkcija uvjerljivo se bre izvodila pri svakom od izvoenja. U konanici je kao srednja vrijednost taktova originalne funkcije dobivena brojka od 522586, dok je u sluaju optimirane funkcije to bila brojka od 320436, to predstavlja prosjeno ubrzanje od 63%. Premda se vodilo rauna o prioritetima izvoenja i programskom rastereenju testnog okruenja, vidljive neravnomjernosti (skokovi i padovu) u rezultatima najveim su dijelom rezultat izvoenja na viedretvenom operativnom sustavu (Ubuntu 9.10). I najmanji prekidi pri ovako kratkom vremenu izvoenja uvode nepoeljni um u krajnje rezultate. No, poto je velika veina dananjih korisnika orijentirana na ovu vrstu okruenja, zateeni su uvjeti, stoga, samo jo blie stvarnim. Razlika izmeu teoretskog (227%) i dobivenog (63%) ubrzanja lei u nizu razloga. Kao prvi i glavni je propusnost (engl. throughput) instrukcija. Propusnost predstavlja broj instrukcija koji je procesorska jedinica u stanju izvoditi u jednom trenutku te ponajprije ovisi o tipu i njihovoj sloenosti. Kod jednostavnih instrukcija (npr. jednostavne aritmetike instrukcije s neovisnim operandima) propusnost moe biti i

17

4.Rezultati i rasprava

po nekoliko puta dok je u sluaju vektorskih instrukcija propusnost jako mala. Razlog je taj to je sklopovska podrka za izvoenje vektorskih instrukcija u x86 arhitekturi jo uvijek u fazi razvoja dok je aritmetiki dio u dananjim procesorima doveden gotovo do savrenstva. No, svijetla se budunost u ovom pogledu nazire u obliku nadolazee AVX (engl. krat. za Advanced Vector Extensions) arhitekture [18]. Drugi razlog razlike u rezultatima lei u podrci dananjih programskih prevoditelja. Naime, podrka vektorskih skupova instrukcija u programima za x86 procesore takoer je jo uvijek u fazi razvoja. Ubrzano izdavanje novih (verzija) skupova instrukcija (SSE3, SSSE3, SSE4, SSE5, FMA3, FMA4,...) i zanemarivanje ponekih starih (MMX, 3DNow!,...) dosta oteava posao piscima programskih prevoditelja to i na kraju rezultira implementacijom samo osnove podoptimalne podrke. Trei je razlog priroda samog vektorskog programiranja. Klasino programiranje u opem je sluaju orijentirano prema skalarnom programiranju. Programski jezici su u najveem broju sluajeva tako i koncipirani, to kod pokuaja pisanja vektorskih programa zahtjeva pisanje posebnih suelja za premoivanje tog jaza. U naem se
850000 Originalna funkcija Optimizirana funkcija 750000 Teoretska funkcija

650000

Broj procesorskih taktova

550000

450000

350000

250000

150000

50000 3 1 5 7 9 11 15 19 23 27 31 35 39 43 47 51 55 59 63 67 71 75 79 83 87 91 95 99 13 17 21 25 29 33 37 41 45 49 53 57 61 65 69 73 77 81 85 89 93 97

Redni broj izvoenja

Slika 7: Rezultati izvoenja testnog programa


18

4.Rezultati i rasprava

sluaju to oitovalo u pretvaranju iz skalarne u vektorsku formu, kao i obrnuti postupak, to je zauzelo gotovo treinu pripadnog programskog koda.

5. Zakljuak
U ovom se uratku pokuao dati kratak osvrt na vektorizaciju obradom De Casteljauovog algoritma iz polja raunalne grafike. Vektorizacija danas predstavlja bitan smjer u daljnjem razvoju snage raunalnih procesora. Iskoritavanje punog potencijala zahtjeva koritenje posebnih funkcija u programskom jeziku po izboru te sklopovlje koje je u mogunosti izvesti takve funkcije. Praktinim uratkom je utvreno da su dobivena ubrzanja daleko od potencijalnih, no ako znamo da proizvoai procesora te programskih prevoditelja uurbano rade na ovom polju, moemo se samo nadati svijetloj budunosti. Takoer je utvreno da je obraeni De Casteljauov algoritam pogodan za ovu vrstu optimizacije. Zanimljivo bi bilo vidjeti mogunosti koje nudi vektorizacija u kombinaciji s paralelnim viedretvenim izvoenjem na dananjim viejezgrenim procesorima, no to neka ostane tema za neki budui uradak.

19

6.Literatura

6. Literatura
[1] Rich Karlgaard: Ten Laws Of The Modern World, http://www.forbes.com/2005/04/19/cz_rk_0419karlgaard.html, Forbes, April, 2005. [2] Amy Bennett: Intel reaches Pentium 4 speed limit at 3.8GHz, http://www.itworld.com/041116intelp4, IDG News Service, November, 2004. [3] Robert Sedgewick: Algorithms, Addison-Wesley Pub, 1988. [4] ld Fosdick, er Jessup, cjc Schauble and G. Domik: An Introduction to HighPerformance Scientific Computing, MIT Press, Cambridge, MA, 1996. [5] Apple Inc.: AltiVec/SSE Migration Guide, http://developer.apple.com/legacy/mac/library/documentation/Performance/C onceptual/Accelerate_sse_migration/Accelerate_sse_migration.pdf, 2005. [6] Agner Fog: Optimization manuals, http://www.agner.org/optimize, Copenhagen University College of Engineering, 2010. [7] George M. Phillips: A de Casteljau algorithm for generalized Bernstein polynomials, BIT Numerical Mathematics, Volume 37, Number 1, 232-236, 1997. [8] Wikipedia, the free encyclopedia: Bzier curve, http://en.wikipedia.org/wiki/Bzier_curve, September, 2010. [9] Nils Pipenbrinck: DeCasteljau Algorithm, http://www.cubic.org/docs/Bzier.htm, September, 1999. [10] Wikipedia, the free encyclopedia: Vectorization (computer science), http://en.wikipedia.org/wiki/Vectorization_(computer_science), August, 2010. [11] Intel Corporation: Intel 64 and IA-32 Architectures Software Developers Manual, http://developer.intel.com/assets/pdf/manual/253665.pdf, March, 2010. [12] Cort Danger William Folberth Stratton: Optimizing for SSE - A Case Study, http://www.cortstratton.org/articles/OptimizingForSSE.php, 2002. [13] Chew Yean Yam: Optimizing Video Compression for Intel Digital Security Surveillance applications with SIMD and Hyper-Threading Technology , http://download.intel.com/design/intarch/papers/30962901.pdf, 2006. [14] Apple Inc.: SSE Performance Programming, http://developer.apple.com/hardwaredrivers/ve/sse.html, 2008. [15] Intel Corporation: Intel Architecture Optimization Reference Manual , http://www.intel.com/design/pentiumii/manuals/245127.htm, 1999. [16] Intel Corporation: Using the RDTSC Instruction for Performance Monitoring , http://www.ccsl.carleton.ca/~jamuir/rdtscpm1.pdf, 1997.
20

6.Literatura

[17] Microsoft Corporation: MSDN - Streaming SIMD Extensions (SSE), http://msdn.microsoft.com/en-us/library/t467de55.aspx, 2010. [18] Intel Corporation: Intel AVX: New Frontiers in Performance Improvements and Energy Efficiency, http://software.intel.com/file/16820, April, 2009. [19] Joe Pfeiffer: CS473 - Pipeline Hazards, http://www.cs.nmsu.edu/~pfeiffer/classes/473/notes/hazards.html, New Mexico State University, February, 2005.

21

Dodatak A:Koriteni programski kod

Dodatak A: Koriteni programski kod


A.1. Test.cpp
#include <iostream> #include <xmmintrin.h> using namespace std; static __inline__ __attribute__((__always_inline__)) unsigned long long int rdtsc(void) { unsigned long long int x; __asm__ volatile (".byte 0x0f, 0x31" : "=A" (x)); return x; } /* http://msdn.microsoft.com/en-us/library/hskdteyh%28VS.80%29.aspx http://www.koders.com/cpp/fidF6A8E722EF8A8D0E3BF173BC25726F66C56BB6B5.aspx? s=windows.h */ static __inline__ __attribute__((__always_inline__)) void cpuid(int CPUInfo[], const int InfoType = 0) { __asm__ volatile ("cpuid" : "=a" (CPUInfo[0]), "=b" (CPUInfo[1]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3]) : "a" (InfoType)); } static __inline__ __attribute__((__always_inline__)) int *cpuid(const int InfoType = 0) { int *CPUInfo = new int[4]; cpuid(CPUInfo, InfoType); return CPUInfo; }

22

Dodatak A:Koriteni programski kod

long long int start, end, delta; #define TIMER_START() cpuid(); rdtsc(); cpuid(); rdtsc(); cpuid(); start = rdtsc(); cpuid(); end = rdtsc(); delta = end - start; start = rdtsc(); #define TIMER_END() cpuid(); end = rdtsc(); cout << end - start - delta << " ticks elapsed" << endl; struct point { float x; float y; }; // Bezierovu krivulju odreujemo s 4 kontrolne toke // u ovom su sluaju upotrebljene iste one toke koritene za grafiki prikaz rada algoritma (2.1.4.) point a = { 40, 100 }; point b = { 80, 20 }; point c = { 150, 180 }; point d = { 260, 100 }; // jednostavna linearna interpolacija izmeu dvije toke void lerp (point &dest, point &a, point &b, float t) { dest.x = a.x + (b.x - a.x)*t; dest.y = a.y + (b.y - a.y)*t; } // izraunava koordinate toke na Bezierovoj krivulju zadane parametrom t // u rasponu [0.0, 1.0] void bezier (point &dest, float t) { point ab, bc, cd, abbc, bccd; lerp (ab, a, b, t); lerp (bc, b, c, t); // toka izmeu a i b (zelena na slici 3) // toka izmeu b i c (zelena na slici 3)

23

Dodatak A:Koriteni programski kod

lerp (cd, c, d, t); lerp (abbc, ab, bc, t); lerp (bccd, bc, cd, t); lerp (dest, abbc, bccd, t); slici 5) }

// toka izmeu c i d (zelena na slici 3) // toka izmeu ab i bc (plava na slici 4) // toka izmeu bc i cd (plava na slici 4) // toka na Bezierovoj krivulji (crna na

// vektorizirana verzija funkcije bezier() static void bezierSSE(float *array, float *result, float t) { __m128* pResult = (__m128*) result; register __m128 R1, R2, R3, R4, R5, R6, R7; R1 = _mm_set_ss(1); R5 = _mm_load_ps(&array[0]); R2 = _mm_set_ss(t); R1 = _mm_shuffle_ps(R1, R1, \ _MM_SHUFFLE(0, 0, 0, 0)); R2 = _mm_shuffle_ps(R2, R2, \ _MM_SHUFFLE(0, 0, 0, 0)); R6 = _mm_load_ps(&array[4]); R3 = _mm_sub_ps(R1, R2); R1 = _mm_add_ps(R1, R1); R4 = _mm_shuffle_ps(R3, R2, \ _MM_SHUFFLE(0, 0, 0, 0)); R1 = _mm_mul_ps(R1, R2); R2 = _mm_mul_ps(R2, R2); R1 = _mm_mul_ps(R1, R3); R3 = _mm_mul_ps(R3, R3); R7 = _mm_shuffle_ps(R5, R6, \ _MM_SHUFFLE(1, 0, 3, 2)); R6 = _mm_mul_ps(R6, R2); // [bc] // [cd] * t^2 // [u u t t] // 2t // t^2 // 2tu // u^2 // [t t t t] // [cd] // u = 1-t // 2 // [1 1 1 1] // [ab] // t

24

Dodatak A:Koriteni programski kod

R5 = _mm_mul_ps(R5, R3); R7 = _mm_mul_ps(R7, R1); R6 = _mm_add_ps(R6, R5); R6 = _mm_add_ps(R6, R7); R1 = _mm_mul_ps(R6, R4); R2 = _mm_movehl_ps(R1, R1); *pResult = _mm_add_ps(R1, R2); } int main (void) {

// [ab] * u^2 // [bc] * 2tu // [ab] * u^2 + [cd] * t^2 // (...) + [bc] * 2tu // (...) * [u u t t] = [x y z w] // [z w z w] // [x+z, ya+w, ...]

float array[] __attribute__((aligned(16))) = { a.x, a.y, b.x, b.y, c.x, c.y, d.x, d.y }; float __attribute__((aligned(16))) result[4]; const int N = 10000; float t; TIMER_START(); point r; t = 0; for ( int i=0; i<N; i++, t += 1./N) { bezier(r, t); } TIMER_END(); printf ("-------t=%f, x=%f, y=%f\n", t, r.x, r.y);

TIMER_START(); t = 0; for ( int i=0; i<N; i++, t += 1./N )

25

Dodatak A:Koriteni programski kod

{ bezierSSE(array, result, t); } TIMER_END(); printf ("-------t=%f, x=%f, y=%f\n", t, result[0], result[1]); }

A.2. Makefile
CC = g++ CXX = $(CC) CFLAGS = CXXFLAGS = -msse -O2 -fverbose-asm -ggdb #-cxxlib-icc LDFLAGS = OBJS = test.o SOURCES = test.cpp EXECUTABLE = test all: $(SOURCES) $(EXECUTABLE) test: clean: @rm -rf ${OBJS} ${EXECUTABLE} ${OBJS} @${CC} -o ${EXECUTABLE} ${CFLAGS} ${OBJS}

26

You might also like