29
FAKULTET ELEKTROTEHNIKE I RAČUNARSTVA ZAVOD ZA ELEKTRONIKU, MIKROELEKTRONIKU, RAČUNALNE I INTELIGENTNE SUSTAVE SEMINARSKI RAD: OPTIMIZACIJA DE CASTELJAUOVOG ALGORITMA VEKTORIZACIJOM Predmet: Računalna grafika Nastavnik: prof. dr. sc. Željka Mihajlović Student: Miroslav Štampar, dipl. ing. MB: R-10/2008 Datum: 03.10.2010.

Optimizacija De Casteljauovog algoritma vektorizacijom

Embed Size (px)

DESCRIPTION

Seminarski rad

Citation preview

Page 1: Optimizacija De Casteljauovog algoritma vektorizacijom

FAKULTET ELEKTROTEHNIKE I RAČUNARSTVAZAVOD ZA ELEKTRONIKU, MIKROELEKTRONIKU, RAČUNALNE I

INTELIGENTNE SUSTAVE

SEMINARSKI RAD:OPTIMIZACIJA DE CASTELJAUOVOG

ALGORITMA VEKTORIZACIJOM

Predmet: Računalna grafikaNastavnik: prof. dr. sc. Željka MihajlovićStudent: Miroslav Štampar, dipl. ing.MB: R-10/2008Datum: 03.10.2010.

Page 2: Optimizacija De Casteljauovog algoritma vektorizacijom
Page 3: Optimizacija De Casteljauovog algoritma vektorizacijom

Sadržaj1. Uvod...................................................................................................................................12. Teorijski dio........................................................................................................................2

2.1.1. Optimizacija.........................................................................................................22.1.2. Vektorizacija........................................................................................................32.1.3. Bézierove krivulje................................................................................................52.1.4. De Casteljauov algoritam....................................................................................62.1.5. SSE vektorski skup instrukcija............................................................................8

3. Praktični dio......................................................................................................................124. Rezultati i rasprava..........................................................................................................175. Zaključak..........................................................................................................................196. Literatura..........................................................................................................................20Dodatak A: Korišteni programski kod..................................................................................22

A.1. Test.cpp....................................................................................................................22A.2. Makefile....................................................................................................................26

Page 4: Optimizacija De Casteljauovog algoritma vektorizacijom

1.Uvod

1. UvodList Forbes je devedesetih godina prošlog stoljeća među 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 tržištu Microsoft bi na čelu s Bill Gatesom odmah nadogradio svoj softver i „opteretio” novi procesor u potpunosti.

Ipak, iza te rečenice skriva se jedna velika istina. Mooreov zakon do sada je konstantno omogućavao razvoj novog softvera i otvaranje novih mogućnosti. No, premda se do sada taj zakon (o udvostručenju procesorskih performansi svakih 18 mjeseci) održavao sirovim podizanjem frekvencije, od 2004. godine došlo je do naglih promjena. Intel je, naime, tada izdao i trenutno najbrži procesor (Pentium 4 na 3.8GHz [2]), i od tada slijedi prekid rasta u tom pogledu. Radi toga su se proizvođači procesora morali okrenuti drugim mogućnostima kako bi Mooreov zakon barem prividno održali na životu.

Jedan od smjerova na čijem se razvoju u posljednje vrijeme užurbano radi jest vektorizacija. U ovom će se uratku pokušati predstaviti osvrt te kroz optimizaciju De Casteljauovog algoritma predstaviti mogućnosti ove vrste optimizacije.

1

Page 5: Optimizacija De Casteljauovog algoritma vektorizacijom

2.Teorijski dio

2. Teorijski dio2.1.1. Optimizacija

U računalnoj znanosti programska optimizacija naziv je za proces u kojem se sustavnom promjenom obavlja promjena određenih dijelova programa u svrhu povećanja efikasnosti određenih aspekata izvođenja i/ili smanjenja korištenih resursa [3]. Općenito govoreći, računalni program može se optimirati kako bi se izvšavao brže, kako bi se smanjila uporaba memorijskog prostora i/ili drugih resursa ili kako bi se smanjila uporaba električne energije.

Postoji više razina optimizacije poredanih silazno po stupnju apstrakcije: dizajnerski, kodni, prevodilački, asemblerski i izvodilački. Važnost pojedine razine, kao i (preporučena) potrebna količina utrošenog vremena za njihovu primjenu, najčešće istovjetno prati navedeni poredak. Na najvišoj 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 današnje doba sve snažniji „trend” paralelizacije uvjetovan mogućnostima novijeg sklopovlja (engl. hardware). Prevodilačka razina sastoji se od odabira i testiranja optimizacijskih mogućnosti korištenog programskog prevoditelja, pri čemu valja imati na umu da su noviji prevoditelji na ovom području sve kvalitetniji. Asemblerska razina optimizacije bila je vrlo popularna u prošlom desetljeću, no u pravilu, u doba današnjih prevoditelja koji optimizaciju na ovoj razini izvode znatno bolje od većine programera, postaje sve slabije korištena. Krajnja razina, izvodilačka, odnosi se samo na interpretirane jezike poput Python-a i Java-e, odnosno na svojstva automatske optimizacije korištenog interpretera.

Ukoliko je vođeno računa o kvalitetnom odabiru algoritma i arhitekturi programskog sustava na dizajnerskoj razini, od razina na kojem može napraviti značajnije zahvate programeru ostaje samo kodna. Na toj razini proces optimizacije obično započinje identifikacijom tzv. „uskog grla” (engl. bottleneck) u programu, kritičnog dijela koji postaje glavni potrošač potrebnih resursa. Kod ove vrste optimizacije možemo primijeniti tzv. „Paretov princip” koji kaže da se 80% resursa obično koristi od strane 20% koda. To znači, umjesto da krenemo s optimizacijom cijelog programa i samim time vjerojatno smanjimo čitljivost programskog koda, trebamo identificirati tih 20% „sporog” koda i usredotočimo se samo na njega.

Računalna paralelizacija naziv je za formu računanja u kojoj se više kalkulacija izvodi istovremeno vodeći se principom da se „veliki” problemi mogu podijeliti na više manjih, koji se zatim mogu izvesti konkurentno („paralelno”). Paralelizam se koristi već dulji niz godina većinom kod visoko-performansnog računanja (engl. High-Performance Computing), no veliki je interes zavladao tek zadnjih nekoliko godina zbog posljedica fizičkih ograničenja daljnjeg frekvencijskog skaliranja.

Tradicionalno se računalni programi pišu serijski. Takvi programi namijenjeni su izvođenju na jednom središnjem procesoru (engl. Central Processing Unit). Zadani

2

Page 6: Optimizacija De Casteljauovog algoritma vektorizacijom

2.Teorijski dio

problem u tom se slučaju dijeli na diskretne serije instrukcija koje se izvode jedna za drugom te se u jednom trenutku može izvesti samo jedna instrukcija.

U svom osnovnom obliku paralelizacija je naziv za simultano korištenje većeg broja računalnih resursa kako bi se u što kraćem vremenu riješio zadani problem. Kako bi se mogao izvršiti „paralelno” korištenjem više procesorskih jedinica problem se dijeli na više konkurentnih diskretnih dijelova. Svaki takav dio se nadalje dijeli na više nizova instrukcija te se same instrukcije izvršavaju simultano na različitim procesorskim (ili vektorskim) jedinicama.

2.1.2. Vektorizacija

Jedan od najraširenijih i najčešće korištenih tipova paralelizacije je vektorizacija, poznata i kao SIMD (engl. krat. za Single Instruction Multiple Data) način računalnog 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 korištenjem posebnih instrukcija u jednom trenutku obrađuje više podataka. Prednost ove vrste optimizacije i njena snaga leži u činjenici da većina današnjih procesora namijenjenih širokom tržištu imaju podržan barem jedan oblik ove vrste paralelizacije.

Po načinu primjene raspoznajemo ručnu (engl. manual) i automatsku prevodilačku (engl. auto-compiler) vektorizaciju. Ručnoj vektorizaciji najčešće pristupamo korištenjem posebnih intrinzičnih (svojstvenih) prevodilačkih naredbi prilagođenih korištenju u jezicima opće namjene (npr. C/C++). Prevodilačkoj vektorizaciji pristupamo korištenjem posebnih parametara korištenog prevoditelja. U tom slučaju programski prevoditelj sam automatski prepoznaje dijelove prevođenog programskog koda koji su prikladni za pretvaranje u vektorski oblik. Problem kod ovog oblika vektorizacije leži u činjenici da današnji programski prevoditelji, iako su dosta napredovali u zadnjih desetak godina, prepoznaju vrlo mali broj programskih struktura pogodnih za ovu vrstu obrade.

Sklopovlje s podrškom za izvođenje vektorskih instrukcija svoju popularnost među širokom masom najviše duguje pojavi Intel-ovog MMX (engl. krat. za Multimedia Extensions) vektorskog skupa instrukcija na x86 arhitekturi. Iako su se vektorske

3

Slika 1: Grafički prikaz razlike između SISD i SIMD načina rada

Page 7: Optimizacija De Casteljauovog algoritma vektorizacijom

2.Teorijski dio

instrukcije mogle pronaći i ranije u osobnim računalima (npr. Altivec za IBM PowerPC i VIS za Sun SPARC), dolaskom Pentium MMX procesora 1996. godine te zahvaljujući prvenstveno agresivnom marketingu i novim mogućnostima usmjerenim prvenstveno poboljšanju performansi tadašnjih video igara, započela je prava mala revolucija čijim posljedicama svjedočimo i danas.

Vektorski skupovi instrukcija mogu se pronaći više manje na svim modernim procesorima, uključujući 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 podržane veličine obrađivanih vektora današnjih procesora mahom 128 i 256 bitova.

Uz povećanje broja osnovnih vektorskih instrukcija te uvođenje specijaliziranih tzv. „brzih” instrukcija, Intel-ov novi skup AVX (engl. krat. za Advanced Vector Extensions) koristit će se za obrađivanje vektora veličine 256 bitova, dok će Intel-ova arhitektura Larrabee omogućiti veličinu vektora od čak 512 bitova.

Što se tiče trendova, Apple-ovim prebacivanjem na x86 arhitekturu 2006. godine, te jakim marketingom iz Intelovog tabora, najpoznatiji i najčešće korišteni vektorski skup instrukcija (u osobnim računalima) definitivno postaje SSE (engl. krat. za Streaming SIMD Extensions) - direktan nasljednik skupa MMX. Trenutna verzija (SSE5) također podržava i sve inačice nastale nakon objave originalne verzije iz 1999. godine.

Zanimljivo je da se u počecima šire primjene vektorizacije još vodila kako takva bitka između tabora Intel-a, AMD-a i IBM-a, no dvije crtice iz prošlosti bile su presudne. AMD je u početku 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 odlučili da će podržati obje tehnologije. Uz to što sam Intel drži velik udio tržišta prevoditelja, kasnije će se uvidjeti da je ova odluka samo doprinijela polakom izumiranju AMD-ovih vektorskih ekstenzija (3DNow!, XOP, FMA4, CVT16).

Drugi bitan događaj definitivno je prelaženje Apple-ovog MacOS-a s dosadašnje IBM-ove PowerPC arhitekture isključivo na Intel-ovu x86 arhitekturu s obaveznom podrškom za SSE2 instrukcijski skup. O razlozima prelaska na ovu arhitekturu i dan se danas nagađa, no razlog obavezne podrške za vektorskim SSE2 instrukcijama leži u činjenici da se prethodno napisani računalni programi, koji koriste (sad već zastarjele) AltiVec instrukcije, mogu prebaciti na novu arhitekturu korištenjem jednostavnog skupa pravila bez značajnijih gubitaka u funkcionalnosti [5].

Korištenjem vektorizacije u svrhu značajnijih poboljšanja performansi teoretski možemo dobiti ubrzanja jednaka broju komponenata u podržanim vektorskim veličinama. Tako na primjer, ukoliko su osnovne komponente (operandi) veličine 32 bita, a vektora 128 bita, teoretsko ubrzanje iznosi 4. Najveća teoretska ubrzanja mogu se tako očekivati kod vektorizacije dosadašnje skalarne obrade teksta, šifriranja podataka te filtera za obradu slike, najčešće zbog njihove male veličine operanada.

4

Page 8: Optimizacija De Casteljauovog algoritma vektorizacijom

2.Teorijski dio

U stvarnosti, teorija i praksa nisu isto. Za postizanje ubrzanja što bližeg teoretskom, potrebno je zadovoljiti dosta uvjeta. Između ostalih ovdje spadaju: modifikacija programa/algoritma u vektorski oblik, pretvorba ulaznih i izlaznih podataka u prikladnu formu, izbjegavanje miješanja vektorskih i skalarnih instrukcija zbog često negativnih posljedica po performanse, minimalizacija broja instrukcija s memorijskim pristupom, optimalno raspoređivanje instrukcija ovisno o međuovisnosti korištenih operanada, optimalno korištenje vektorskih registara, izbjegavanje pristupa neporavnatim (engl. unaligned) memorijskim lokacijama, zadovoljenje općih 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 slučajeva i to za ubrzanje za to pogodnih algoritama. Jedan od takvih algoritama je De Casteljauov.

2.1.3. Bézierove krivulje

Bézierove krivulje su forme sveprisutne u računalnoj grafici. U teoretsku matematiku implicitno su ih uveli puno prije računala dvojica matematičara: Francuz Charles Hermite te Rus Sergei Bernstein. No, za njihovu današnju popularnost i jednostavnost primjene prvenstveno su zaslužni Pierre Bézier (Renault) te Paul De Casteljau (Citroen) zaposlenici u auto industriji 60-ih godina prošlog stoljeća.

Bézierove se krivulje često koriste u računalnoj grafici za modeliranje tzv. „glatkih” (engl. smooth) krivulja. Pošto je takva vrsta krivulje u cijelosti sadržana u konveksnoj ljusci svojih kontrolnih točaka, točke se mogu grafički prikazati i iskoristiti za intuitivno upravljanje krivuljom. Također, afine se transformacije, kao što su translacija i rotacija, mogu primijeniti na krivulju primjenom odgovarajućih transformacija nad pripadnim kontrolnim točkama. Kvadratne i kubne Bézierove krivulje su najčešće; krivulje višeg stupnja rjeđe se koriste jer su zahtjevnije za procjenu. Kada su potrebni složeniji oblici, koriste se Bézierove krivulje nižeg stupnja spojene zajedno u tzv. „stazu” (engl. path).

Kako bi se objasnio princip rada De Casteljauovog algoritma, najprije je potrebno upoznati osnove Bézierovih krivulja za čiju se kalkulaciju koristi između ostalih i ovaj algoritam.

Bézierova krivulja stupnja n može se generalizirati na sljedeći način. S obzirom na tzv. kontrolne točke P0,P1,... , Pn , Bézierova krivulja zadana je sljedećom jednadžbom:

B t = ∑i=0

n

ni 1−t n−i ti P i= 1−t n P0n11−t n−1t P1⋯

= ⋯nn−11−t t n−1Pn−1t n PnTako na primjer za n=3 dobivamo kubnu Bézierovu krivulju:

B t =1−t 3 P031− t 2t P131−t t

2 P2t3P3

5

Page 9: Optimizacija De Casteljauovog algoritma vektorizacijom

2.Teorijski dio

Ova se (generalizirana) formula može prikazati rekurzivno na sljedeći način. NekaBP0 P1 ... P nt predstavlja Bézierovu krivulja zadanu točkama P0 , P1 , ... , Pn . Tada

vrijedi rekurzivna formula: B t =BP0 P1 ... P nt =1−t BP0 P1 ...Pn−1t t BP1 P2 ...P nt .

Drugim riječima, Bézierova krivulja n-tog stupnja linearna je interpolacija između dviju Bézierovih krivulja stupnja n-1.

U terminologiji se može pronaći i sljedeći prikaz:

B t =∑i=0

n

bi , nt Pi , t∈[0,1]

gdje su polinomi b i , nt =ni t i1−t n−i , i=0,... , n poznati i kao Bernsteineovi bazni

polinomi stupnja n, pri čemu je zadano t 0=1 i 1−t 0=1 .

2.1.4. De Casteljauov algoritam

U matematičkom polju numeričke analize De Casteljauov algoritam, nazvan po svom pronalazaču Paul De Casteljau, naziv je za rekurzivnu metodu procjene polinoma u Bernsteinovoj formi i/ili Bézierovim krivuljama

De Casteljauov algoritam definiramo na sljedeći način. Ako se polinom B može prikazati u Bernsteinovoj formi stupnja n na sljedeći način:

B t =∑i=0

n

ibi , nt

pri čemu je s bi,n označen Bernsteinov bazni polinom, tada se njegova vrijednost u točki t0 može izračunati korištenjem rekurzivne relacije:

i0 :=i , i=0,. .. , n

i j :=i

j−11−t0i1 j−1 t 0 , i=0,. .. , n− j , j=1,... , n

6

Slika 2: Kubna Bézierova krivulja

Page 10: Optimizacija De Casteljauovog algoritma vektorizacijom

2.Teorijski dio

Računanje vrijednosti B u točki t0 može se izračunati korištenjem algoritma u n koraka. Rezultat B(t0) je zadan kao:

B t0=0n .

Na primjer, recimo da želimo izračunati Bernsteinov polinom stupnja 2 s Bernsteinovim koeficijentima u točki t0:

00 =0

10 =1

20 =2

Započinjemo rekurziju na sljedeći način:

01 =0

0 1−t 010 t 0=01−t 01t 0

11 =1

0 1−t 020 t 0=1 1−t 02 t0

te u sljedećoj iteraciji rekurzija završava s rezultatom:

02 = 0

1 1−t 011 t0

= 01−t 01−t 01 t0 1−t 011−t 0 t02 t 0t 0= 01−t 0

212 t 01−t02 t 02

što je očekivani Bernsteinov polinom stupnja n.Možda je najbolji način za objasniti rad De Casteljuovog algoritma grafički kroz

primjer. Kubna Bézierova krivulja ima četiri kontrolne točke. Na početku algoritma povežemo te četiri točke s tri linije (prikazano crvenom bojom na slici 3). Nakon toga označimo sredine tih linija s tri nove točke (zelene boje).

Nadalje, te tri nove točke povežemo s dvije nove linije (prikazano zelenom bojom na slici 4). Ponovno označimo središnje točke (plave boje) tih linija.

7

Slika 3: Rezultat nakon prvog koraka De Casteljauovog algoritma

Page 11: Optimizacija De Casteljauovog algoritma vektorizacijom

2.Teorijski dio

Na kraju povežemo zadnje dvije točke (plave boje) s linijom (prikazano plavom bojom na slici 5) i označimo sredinu te linije s točkom. Ta zadnja točka predstavlja jednu od točki Bézierove krivulje.

U ovom trenutku znamo izračunati koordinate jedne točke na Bézierovoj krivulji. Ukoliko želimo to isto uraditi za bilo koju točku na krivulji moramo prilagodit način izračuna. To ćemo uraditi na način da umjesto opisanog korištenja središnjih točaka koristimo jednostavnu linearnu interpolaciju (jednadžba pravca kroz dvije točke) na opisanim pomoćnim linijama.

Na taj način možemo pronaći bilo koju točku na Bézierovoj krivulju, što odgovara zaključku iz 2.1.3. da je Bézierova krivulja n-tog stupnja linearna interpolacija između dviju Bézierovih krivulja stupnja n-1.

2.1.5. SSE vektorski skup instrukcija

U veljači 1999. godine Intel je predstavio Pentium III procesor u kojem je jedna od novina bila i nadogradnja postojećeg MMX (engl. krat. za MultiMedia Extensions) s novim SSE (engl. krat. za Streaming SIMD Extensions) vektorskim skupom instrukcija. Te instrukcije također su bile poznate i pod kraticom KNI (engl. krat. za

8

Slika 4: Rezultat nakon drugog koraka De Casteljauovog algoritma

Slika 5: Rezultat nakon trećeg (završnog) koraka De Casteljauovog algoritma

Page 12: Optimizacija De Casteljauovog algoritma vektorizacijom

2.Teorijski dio

Katmai New Instructions) do konačnog predstavljanja jer je Katmai bio kodni naziv za Pentium III procesor.

SSE sadržava 70 novih instrukcija za grafičku i zvučnu obradu kao novina naspram onoga što je dotad omogućavao MMX. SSE je vrlo sličan skup MMX-u. Zapravo, prije nego što je objavljen, neki su ga još i zvali MMX-2. Uz nove instrukcije slične dosadašnjim iz skupa MMX, SSE omogućuje računanje nad brojevima s pomičnim zarezom (engl. floating point), a za njihovo se izvršavanje sada koristi poseban dio procesora umjesto dosadašnje (MMX) metode dijeljenja standardne jedinice za brojeve s pomičnim zarezom (engl. krat. FPU - floating point unit). SSE se sastoji od 70 novih instrukcija, uključujući vektorske za rad s brojevima s pomičnim zarezom, dodatne vektorske za rad s cijelim brojevima (engl. integer) te kontrolnih instrukcija za upravljanje keširanjem (engl. cache management).

SSE donosi također i 8 novih 128-bitnih registara XMM0-XMM7, podijeljenih na 4 32-bitne komponente s pomičnim zarezom. Dodatni kontrolni registar, MXCSR, je također na raspolaganju za kontrolu i provjeru stanja izvršenja pojedinih instrukcija. SSE instrukcije koriste ove 128-bitne registre, 64-bitne MMX registre MM0-MM7 te u nekim slučajevima i 32-bitne registre opće namjene (engl. general purpose registers).

U sljedećem je primjeru na jednostavan način prikazana prednost korištenja SSE skupa instrukcija nad standardnim. Razmotrite operaciju kao što je vektorsko zbrajanje koje se vrlo često koristi u računalnoj grafici. Zbrajanje dva 4-komponentna vektora s jednostrukom preciznošću zahtijeva korištenje četiri standardne x86 instrukcije FADD za zbrajanje brojeva s pomičnim zarezom:fadd c.x, a.x, b.x //c.x := a.x + b.xfadd c.y, a.y, b.y //c.y := a.y + b.yfadd c.z, a.z, b.z //c.z := a.z + b.zfadd c.w, a.w, b.w //c.w := a.w + b.w

Programski kod 1: Zbrajanje vektora korištenjem standardnih instrukcija

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

9

Slika 6: SSE vektor registri

XMM0XMM1XMM2XMM3XMM4XMM5XMM6XMM7

128 bita

Page 13: Optimizacija De Casteljauovog algoritma vektorizacijom

2.Teorijski dio

movaps xmm0, <adresa od a> //xmm0 := a.w | a.z | a.y | a.x addps xmm0, <adresa od b> //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 korištenjem SSE vektorskih instrukcija

Za potrebe praktičnog uratka korištene su vektorske instrukcije iz SSE skupa instrukcija prvenstveno iz dva razloga: velika rasprostranjenost sklopovske podrške i podržanost od strane većine današnjih programskih (C/C++) prevoditelja. No, zbog korištene 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 praktičnom uratku, u nastavku slijedi opis osnovnih korištenih SSE instrukcija.

Intrinzična (engl. intrinsic) funkcija naziv je za funkciju koju prevoditelj direktno prevodi u niz od jedne ili više asemblerskih (engl. assembler) instrukcija. Takva vrsta funkcija je inherentno efikasnija od običnih funkcija jer se pri njihovom izvršavanju ne koristi uobičajeni mehanizam zvanja (engl. calling mechanism). Intrinzične funkcije omogućuju jednostavnije korištenje poboljšanja specifičnih za procesor, poput vektorskih instrukcija, jer osiguravaju sučelje u visokom programskom jeziku (u našem slučaju C/C++) prema asemblerskim instrukcijama. Kako bi se to omogućilo prevoditelj umjesto korisnika automatski vodi računa o stvarima poput imena korištenih registara, alokaciji registara te memorijskim lokacijama podataka.

Intrinzična funkcija _mm_load_ps se koristi za učitavanje 4-komponentnog vektora (tip podataka __m128) s jednostrukom preciznošću (engl. single precision) sa zadane adrese. Predviđena je za brzo učitavanje vrijednosti isključivo 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 intrinzične funkcije _mm_load_ps i pripadni pseudokod

Intrinzična funkcija _mm_set_ss služi za postavljanje najniže riječi vektor registra na zadanu vrijednost tipa float pri čemu se ostale riječi postavljaju na vrijednost 0:__m128 _mm_set_ss(float w);

r0 := w r1 := r2 := r3 := 0.0

Programski kod 4: Zaglavlje intrinzične funkcije _mm_set_ss i pripadni pseudokod

Intrinzična funkcija _mm_add_ps vraća rezultat aritmetičkog zbrajanja dvaju vektor registara:

10

Page 14: Optimizacija De Casteljauovog algoritma vektorizacijom

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 intrinzične funkcije _mm_add_ps i pripadni pseudokod

Intrinzična funkcija _mm_sub_ps namijenjena je za aritmetičko 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 intrinzične funkcije _mm_sub_ps i pripadni pseudokod

Intrinzična funkcija _mm_mul_ps namijenjena je za aritmetičko množenje 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 intrinzične funkcije _mm_mul_ps i pripadni pseudokod

Svrha intrinzične funkcije _mm_shuffle_ps je selekcija određenih dijelova zadanih vektora na temelju specificirane maske (detaljnije u [17]):__m128 _mm_shuffle_ps(__m128 a , __m128 b , int mask );

Programski kod 8: Zaglavlje intrinzične funkcije _mm_shuffle_psIntrinzična funkcija _mm_movehl_ps vraća 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 intrinzične funkcije _mm_movehl_ps i pripadni pseudokod

11

Page 15: Optimizacija De Casteljauovog algoritma vektorizacijom

3.Praktični dio

3. Praktični dioU praktičnom dijelu ovog uratka bit će prezentiran rezultat vektorizacije De

Casteljauovog algoritma korištenjem SSE vektorskog skupa instrukcija. Kao programski jezik koristio se C/C++, korišteno je programsko razvojno sučelje GCC 4.4.1, dok se testna platforma bazirala na operativnom sustavu Ubuntu Linux 9.10. i računalnom procesoru Intel Q6600 (4x2.4GHz) s uključenom podrškom za izvođenje SSE instrukcija.

Prije samog postupka optimizacije napisan je program za računanje točaka na Bezierovoj krivulji implementacijom osnovne varijante De Casteljauovog algoritma. Korištenjem zadanih kontrolnih točaka, proizvoljnog parametra t i postupka opisanog u teorijskom dijelu ovog uratka (2.1.4.) u mogućnosti smo izračunati bilo koju točku na kubnoj 2D Bézierovoj krivulji uporabom sljedećeg programskog koda:#include <stdio.h>

struct point{ float x; float y;};

// Bezierovu krivulju određujemo s 4 kontrolne točke// u ovom su slučaju upotrijebljene iste one točke korištene za grafički 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 između dvije točkevoid 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;}

// izračunava koordinate točke na Bezierovoj krivulju zadane parametrom t // u rasponu [0.0, 1.0]

12

Page 16: Optimizacija De Casteljauovog algoritma vektorizacijom

3.Praktični dio

void bezier (point &dest, float t){ point ab, bc, cd, abbc, bccd; lerp (ab, a, b, t); // točka između a i b (zelena na slici 3) lerp (bc, b, c, t); // točka između b i c (zelena na slici 3) lerp (cd, c, d, t); // točka između c i d (zelena na slici 3) lerp (abbc, ab, bc, t); // točka između ab i bc (plava na slici 4) lerp (bccd, bc, cd, t); // točka između bc i cd (plava na slici 4) lerp (dest, abbc, bccd, t); // točka na Bezierovoj krivulji (crna na slici 5)}

// mali testni program koji služi za ispis dobivenih koordinata točki Bezierove krivuljevoid main (void){ point p; for ( int i=0; i<1000; i++ ) { float t = (float)i/999.0; // parametar t kojim se jednoznačno određuje // točka na Bezierovoj krivulji bezier (p, t); printf ("%f %f\n", p.x, p.y); }}

Programski kod 10: Osnovni program za računanje točaka na kubnoj 2D Bézierovoj krivulji pomoću De Casteljauovog algoritma

Iz gornjeg programskog koda može se vidjeti da se funkcija bezier koristi za računanje koordinata točaka korištenjem De Casteljauvog algoritma dok se pomoćna funkcija lerp koristi za izračunavanje linearne interpolacije između dvije točke. Te dvije funkcije čine procesorski najzahtjevniji dio ovog programa te su kao takve idealni kandidati za optimizaciju.

Ako znamo da su podaci tipa float veličine 32 bita, a SSE registri 128 bita, jasno je da ukoliko želimo upotrijebiti ovu vrstu optimizacije, maksimalno teoretsko ubrzanje koje možemo očekivati 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 našem slučaju govorimo o funkciji bezier i pripadnoj pomoćnoj funkciji lerp.

13

Page 17: Optimizacija De Casteljauovog algoritma vektorizacijom

3.Praktični dio

Ukoliko funkciju za linearnu interpolaciju lerp prikažemo u prikladnijem obliku lerpa ,b=ab−at=a 1−t b t=a ub t , tada dani programski kod možemo raspisati u sljedećem obliku:dest = abbc⋅ubccd⋅t u=1−t

= ab⋅ubc⋅t ⋅ubc⋅ucd⋅t ⋅t= a⋅ub⋅t ⋅ub⋅uc⋅t ⋅t ⋅ub⋅uc⋅t ⋅uc⋅ud⋅t ⋅t ⋅t= a⋅u2b⋅2 t uc ˙t 2⋅ub⋅u2c⋅2 t ud⋅t 2⋅t

= [ab ]⋅u2[bc]⋅2 t u[cd ]⋅t 2⋅[ut ]Ova vektorska prezentacija De Casteljauovog algoritma predstavlja glavnu

okosnicu ovog dijela uratka i idealna je za implementaciju korištenjem 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 aritmetičkih operacija (2 zbrajanja, 2 oduzimanja i 2 množenja), tada možemo zaključiti da se u tom slučaju sveukupno radilo o 36 aritmetičkih operacija. U krajnje raspisanom slučaju imamo samo 11 aritmetičkih operacija (2 zbrajanja, 8 množenja i 1 oduzimanje) što predstavlja teoretsko ubrzanje od 227%.

U zadanoj formi oblik [ab ] predstavlja vektor stupac sačinjen od komponenti

sadržanih vektora ( a x , a y , bx , b y ). Pošto su u našem slučaju komponente x i y veličine 32 bita upotrijebljeni vektor stupci točno stanu u 128 bitne SSE registre.

U nastavku slijedi implementacija De Casteljauvog algoritma korištenjem SSE vektorskog skupa instrukcija. Linije su pobrojane kako bi se poslije mogao lakše dati detaljan opis rada svake od njih pojedinačno:1. #include <xmmintrin.h>2. 3. static void bezierSSE(float *array, float *result, float t)4. { 5. __m128* pResult = (__m128*) result;6. 7. register __m128 R1, R2, R3, R4, R5, R6, R7;8.9. R1 = _mm_set_ss(1);10. R5 = _mm_load_ps(&array[0]); // [ab]11. R2 = _mm_set_ss(t); // t12. R1 = _mm_shuffle_ps(R1, R1, _MM_SHUFFLE(0, 0, 0, 0)); // [1 1 1 1]13. R2 = _mm_shuffle_ps(R2, R2, _MM_SHUFFLE(0, 0, 0, 0)); // [t t t t]14. R6 = _mm_load_ps(&array[4]); // [cd]15. R3 = _mm_sub_ps(R1, R2); // u = 1-t

14

Page 18: Optimizacija De Casteljauovog algoritma vektorizacijom

3.Praktični dio

16. R1 = _mm_add_ps(R1, R1); // 217. R4 = _mm_shuffle_ps(R3, R2, _MM_SHUFFLE(0, 0, 0, 0)); // [u u t t]18. R1 = _mm_mul_ps(R1, R2); // 2t19. R2 = _mm_mul_ps(R2, R2); // t^220. R1 = _mm_mul_ps(R1, R3); // 2tu21. R3 = _mm_mul_ps(R3, R3); // u^222. 23. R7 = _mm_shuffle_ps(R5, R6, _MM_SHUFFLE(1, 0, 3, 2)); // [bc]24. R6 = _mm_mul_ps(R6, R2); // [cd] * t^225. R5 = _mm_mul_ps(R5, R3); // [ab] * u^226. R7 = _mm_mul_ps(R7, R1); // [bc] * 2tu27. 28. R6 = _mm_add_ps(R6, R5); // [ab] * u^2 + [cd] * t^229. R6 = _mm_add_ps(R6, R7); // (...) + [bc] * 2tu30. 31. R1 = _mm_mul_ps(R6, R4); // (...) * [u u t t] = [x y z w]32. R2 = _mm_movehl_ps(R1, R1); // [z w z w]33. 34. *pResult = _mm_add_ps(R1, R2); // [x+z, y+w, ...]35. }

Programski kod 11: Optimirana SSE inačica funkcije za računanje točaka na kubnoj 2D Bézierovoj krivulji pomoću De Casteljauovog algoritma

U zaglavlju programa u kojem se koriste SSE intrinzične funkcije, pripadni tipovi podataka i/ili pomoćne makro naredbe, obavezno mora biti uključena datoteka xmmintrin.h (linija 1). Osnovni tip podataka u SSE programima zadan je s ključnom riječi __m128 (linija 5 i 7). U osnovama optimiranja C/C++ koda između ostalog stoji da bi programer trebao nastojati kritične funkcije deklarirati kao statičke korištenjem ključne riječi static (linija 3) te ključne lokalne varijable deklarirati kao registarske korištenjem ključne riječi register (linija 7) [6].

Deklarirane varijable R1..R7 u idealnom slučaju predstavljaju registarske SSE varijable bez uporabe mehanizma čuvanja (na stogu). No, dekompajliranjem binarne prezentacije prevedenog programskog koda (korištenjem objdump Linux programa) jasno je vidljivo da u slučaju isključene optimizacije prevoditelja to nije slučaj. Naime, tada se usprkos deklariranju različitih registarskih varijabli koristio mehanizam čuvanja što je nepotrebno usporavalo izvođenje programa. Stoga je za postavku optimizacije proizvoljno uzeta druga razina optimizacije (-O2), no rezultati su se pokazali pozitivnima i u ostalim slučajevima (-O1 i -O3). Bitno je napomenuti da su se prilikom testiranja koristile identične postavke optimizacije i za potrebe prevođenja originalne verzije algoritma, prvenstveno kako bi se izbjegla moguća nekonzistentnost dobivenih rezultata.

15

Page 19: Optimizacija De Casteljauovog algoritma vektorizacijom

3.Praktični dio

Prema jednom od naputaka [12] radi povećanja propusnosti SSE vektorskih instrukcija trebalo bi se voditi računa o što većem ispreplitanju tzv. „pakiranih” (engl. packed) i „nepakiranih” (engl. unpacked) intrinzičnih funkcija. Na taj je način moguće izvesti više instrukcija paralelno u istom trenutku. Razlika je u tome da se pakirane funkcije (sa sufiksom PS) izvršavaju nad cjeloukupnim operandima, dok se nepakirane (sa sufiksom SS) izvršavaju samo nad najnižim riječima operanada. Stoga se, umjesto korištenja pakirane funkcije _mm_set_ps za postavljanje sadržaja vektora na konstantu vrijednost, koristila nepakirana funkcija _mm_set_ss u kombinaciji s (jeftinom) funkcijom _mm_shuffle_ps. Na ovaj se način, iako se koristila pakirana funkcija _mm_shuffle_ps, dobilo par postotaka brže izvođenje koda.__m128 _mm_set_ss(float w );r0 := wr1 := r2 := r3 := 0

__m128 _mm_set_ps(float z , float y , float x , float w );r0 := wr1 := xr2 := yr3 := z

Programski kod 12: Razlika između nepakirane i pakirane inačice intrinzične funkcije za postavljanje sadržaja vektora na konstantu

Nadalje, jedna od prvih natuknica kod optimiranja SSE programskog koda definitivno je poravnavanje (engl. aligning) korištenih memorijskih adresa na 16 bajta ([12],[14],[15]). To znači da će u slučaju kršenja ovog naputka doći i do više nego osjetnog usporenja izvođenja programskog koda, pa u nekim slučajevima i do nasilnog prekidanja (npr. ako se u jednom takvom slučaju koristi funkcija _mm_load_ps opisana u programskom kodu 3). Stoga se radi poštivanja navedenog i smanjenja mogućih komplikacija oko prosljeđivanja parametara s komponentama kontrolnih točaka došlo do kompromisnog rješenja. Sve se komponente kontrolnih točaka zapisuju na jednom mjestu u obliku vektorskog stupca, odnosno, polja tipa float s poravnatom memorijskom adresom te se u tom obliku prosljeđuju optimiranoj funkciji (Dodatak A.1.).

Sadržaj vektor stupca korištenog za spremanje komponenti kontrolnih točaka može se prikazati u sljedećem obliku: [ax , ay , b x , by , cx , c y , d x , d y ] . Ako znamo da je veličina svake komponente 32 bita, tada lako možemo obrazložiti način njihovog učitavanja u SSE registre (linije 10 i 14 u programskom kodu 11).

Korištenjem funkcije _mm_load_ps učitani su registri sadržajima [ab] i [cd ] s

pripadnih adresa (&array[0] i &array[4]) poravnatih na 16 bajta. Treći traženi vektor

stupac [bc] dobiven je selekcijom (linija 23 u programskom kodu 11) donjeg dijela

16

Page 20: Optimizacija De Casteljauovog algoritma vektorizacijom

3.Praktični dio

prvog i gornjeg dijela drugog prethodno učitanog vektora korištenjem intrinzične funkcije _mm_shuffle_ps opisane u programskom kodu 8.

U nastavku programskog koda slijedi računanje pojedinih komponenti sadržanih u prethodno raspisanoj jednadžbi. Prvenstveno se vodilo računa o međuovisnosti korištenih registara operanada te minimalizaciji broja korištenih registara radi izbjegavanja sporog mehanizma memorijske pohrane od strane programskog prevoditelja. Broj korištenih SSE registara smanjen je na 7 (linija 7 u programskom kodu 11) od mogućih 8 te se u svim slučajevima uklonila registarska međuovisnost 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 moguće.

Na kraju su se zbrojila dva dvodimenzionalna (pod)vektora sadržana u registru R1 (linije 32 i 34) te se dobiveni rezultat zapisao u izlazno polje tipa float kao konačan rezultat izvođenja funkcije. Bitno je napomenuti da se radi ubrzanja rada funkcije rezultat nije izvlačio iz donjeg dijela konačnog vektora te se stoga u izlaznom polju zadnja dva broja mogu slobodno zanemariti (predstavljeno točkicama u liniji 34 u programskom kodu 11).

4. Rezultati i raspravaZa potrebe testiranja program se iz dodatka A.1. izveo 100 puta uzastopno.

Rezultat svakog izvođenja bila su dva broja: broj procesorskih taktova potrebnih za računanje točaka na Bezierovoj krivulji s rezolucijom od 10000 točaka korištenjem originalne funkcije te broj procesorskih taktova potrebnih za istu svrhu no korištenjem optimirane verzije funkcije. Grafički prikaz dobivenih rezultata može se pronaći na slici 7. Na slici se također mogu vidjeti rezultati koji bi se dobili u teoretskom slučaju optimizacije funkcije, dobiveni skaliranjem vremena izvođenja originalne funkcije s vrijednošću teoretskog ubrzanja navedenog u prethodnom poglavlju (227%).

Kao što je vidljivo iz rezultata, optimirana funkcija uvjerljivo se brže izvodila pri svakom od izvođenja. U konačnici je kao srednja vrijednost taktova originalne funkcije dobivena brojka od 522586, dok je u slučaju optimirane funkcije to bila brojka od 320436, što predstavlja prosječno ubrzanje od 63%.

Premda se vodilo računa o prioritetima izvođenja i programskom rasterećenju testnog okruženja, vidljive neravnomjernosti (skokovi i padovu) u rezultatima najvećim su dijelom rezultat izvođenja na višedretvenom operativnom sustavu (Ubuntu 9.10). I najmanji prekidi pri ovako kratkom vremenu izvođenja uvode nepoželjni šum u krajnje rezultate. No, pošto je velika većina današnjih korisnika orijentirana na ovu vrstu okruženja, zatečeni su uvjeti, stoga, samo još bliže stvarnim.

Razlika između teoretskog (227%) i dobivenog (63%) ubrzanja leži 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 složenosti. Kod jednostavnih instrukcija (npr. jednostavne aritmetičke instrukcije s neovisnim operandima) propusnost može biti i

17

Page 21: Optimizacija De Casteljauovog algoritma vektorizacijom

4.Rezultati i rasprava

po nekoliko puta dok je u slučaju vektorskih instrukcija propusnost jako mala. Razlog je taj što je sklopovska podrška za izvođenje vektorskih instrukcija u x86 arhitekturi još uvijek u fazi razvoja dok je aritmetički dio u današnjim procesorima doveden gotovo do savršenstva. No, svijetla se budućnost u ovom pogledu nazire u obliku nadolazeće AVX (engl. krat. za Advanced Vector Extensions) arhitekture [18].

Drugi razlog razlike u rezultatima leži u podršci današnjih programskih prevoditelja. Naime, podrška vektorskih skupova instrukcija u programima za x86 procesore također 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 otežava posao piscima programskih prevoditelja što i na kraju rezultira implementacijom samo osnove podoptimalne podrške.

Treći je razlog priroda samog vektorskog programiranja. Klasično programiranje u općem je slučaju orijentirano prema skalarnom programiranju. Programski jezici su u najvećem broju slučajeva tako i koncipirani, što kod pokušaja pisanja vektorskih programa zahtjeva pisanje posebnih sučelja za premošćivanje tog jaza. U našem se

18

Slika 7: Rezultati izvođenja testnog programa

13

57

911

1315

1719

2123

2527

2931

3335

3739

4143

4547

4951

5355

5759

6163

6567

6971

7375

7779

8183

8587

8991

9395

9799

50000

150000

250000

350000

450000

550000

650000

750000

850000Originalna funkcijaOptimizirana funkcijaTeoretska funkcija

Redni broj izvođenja

Bro

j pro

ceso

rski

h ta

ktov

a

Page 22: Optimizacija De Casteljauovog algoritma vektorizacijom

4.Rezultati i rasprava

slučaju to očitovalo u pretvaranju iz skalarne u vektorsku formu, kao i obrnuti postupak, što je zauzelo gotovo trećinu pripadnog programskog koda.

5. ZaključakU ovom se uratku pokušao dati kratak osvrt na vektorizaciju obradom De

Casteljauovog algoritma iz polja računalne grafike.Vektorizacija danas predstavlja bitan smjer u daljnjem razvoju snage računalnih

procesora. Iskorištavanje punog potencijala zahtjeva korištenje posebnih funkcija u programskom jeziku po izboru te sklopovlje koje je u mogućnosti izvesti takve funkcije.

Praktičnim uratkom je utvrđeno da su dobivena ubrzanja daleko od potencijalnih, no ako znamo da proizvođači procesora te programskih prevoditelja užurbano rade na ovom polju, možemo se samo nadati svijetloj budućnosti. Također je utvrđeno da je obrađeni De Casteljauov algoritam pogodan za ovu vrstu optimizacije.

Zanimljivo bi bilo vidjeti mogućnosti koje nudi vektorizacija u kombinaciji s paralelnim višedretvenim izvođenjem na današnjim višejezgrenim procesorima, no to neka ostane tema za neki budući uradak.

19

Page 23: Optimizacija De Casteljauovog algoritma vektorizacijom

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 High-

Performance Scientific Computing, MIT Press, Cambridge, MA, 1996.[5] Apple Inc.: AltiVec/SSE Migration Guide,

http://developer.apple.com/legacy/mac/library/documentation/Performance/Conceptual/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: Bézier curve, http://en.wikipedia.org/wiki/Bézier_curve, September, 2010.

[9] Nils Pipenbrinck: DeCasteljau Algorithm, http://www.cubic.org/docs/Bézier.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 Developer’s 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

Page 24: Optimizacija De Casteljauovog algoritma vektorizacijom

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

Page 25: Optimizacija De Casteljauovog algoritma vektorizacijom

Dodatak A:Korišteni programski kod

Dodatak A: Korišteni 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

Page 26: Optimizacija De Casteljauovog algoritma vektorizacijom

Dodatak A:Korišteni 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 određujemo s 4 kontrolne točke// u ovom su slučaju upotrebljene iste one točke korištene za grafički 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 između dvije točkevoid 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;}

// izračunava koordinate točke 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); // točka između a i b (zelena na slici 3) lerp (bc, b, c, t); // točka između b i c (zelena na slici 3)

23

Page 27: Optimizacija De Casteljauovog algoritma vektorizacijom

Dodatak A:Korišteni programski kod

lerp (cd, c, d, t); // točka između c i d (zelena na slici 3) lerp (abbc, ab, bc, t); // točka između ab i bc (plava na slici 4) lerp (bccd, bc, cd, t); // točka između bc i cd (plava na slici 4) lerp (dest, abbc, bccd, t); // točka na Bezierovoj krivulji (crna na slici 5)}

// 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]); // [ab] R2 = _mm_set_ss(t); // t R1 = _mm_shuffle_ps(R1, R1, \ _MM_SHUFFLE(0, 0, 0, 0)); // [1 1 1 1] R2 = _mm_shuffle_ps(R2, R2, \ _MM_SHUFFLE(0, 0, 0, 0)); // [t t t t] R6 = _mm_load_ps(&array[4]); // [cd] R3 = _mm_sub_ps(R1, R2); // u = 1-t R1 = _mm_add_ps(R1, R1); // 2 R4 = _mm_shuffle_ps(R3, R2, \ _MM_SHUFFLE(0, 0, 0, 0)); // [u u t t] R1 = _mm_mul_ps(R1, R2); // 2t R2 = _mm_mul_ps(R2, R2); // t^2 R1 = _mm_mul_ps(R1, R3); // 2tu R3 = _mm_mul_ps(R3, R3); // u^2

R7 = _mm_shuffle_ps(R5, R6, \ _MM_SHUFFLE(1, 0, 3, 2)); // [bc] R6 = _mm_mul_ps(R6, R2); // [cd] * t^2

24

Page 28: Optimizacija De Casteljauovog algoritma vektorizacijom

Dodatak A:Korišteni programski kod

R5 = _mm_mul_ps(R5, R3); // [ab] * u^2 R7 = _mm_mul_ps(R7, R1); // [bc] * 2tu

R6 = _mm_add_ps(R6, R5); // [ab] * u^2 + [cd] * t^2 R6 = _mm_add_ps(R6, R7); // (...) + [bc] * 2tu

R1 = _mm_mul_ps(R6, R4); // (...) * [u u t t] = [x y z w] R2 = _mm_movehl_ps(R1, R1); // [z w z w]

*pResult = _mm_add_ps(R1, R2); // [x+z, ya+w, ...]}

int main (void){ 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

Page 29: Optimizacija De Casteljauovog algoritma vektorizacijom

Dodatak A:Korišteni programski kod

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

A.2. MakefileCC = g++CXX = $(CC)CFLAGS = CXXFLAGS = -msse -O2 -fverbose-asm -ggdb #-cxxlib-iccLDFLAGS =OBJS = test.oSOURCES = test.cppEXECUTABLE = testall: $(SOURCES) $(EXECUTABLE)test: ${OBJS} @${CC} -o ${EXECUTABLE} ${CFLAGS} ${OBJS}clean: @rm -rf ${OBJS} ${EXECUTABLE}

26