135
SVEUČILIŠTE U ZAGREBU FAKULTET ELEKTROTEHNIKE I RAČUNARSTVA DIPLOMSKI RAD br. 1913 OPTIMIZACIJA ROJEM ČESTICA NA PARALELNIM ARHITEKTURAMA Ante Bodić Voditelj: Prof. dr. sc. Domagoj Jakobović Zagreb, rujan 2012.

optimizacija rojem čestica na paralelnim arhitekturama

Embed Size (px)

Citation preview

Page 1: optimizacija rojem čestica na paralelnim arhitekturama

SVEUČILIŠTE U ZAGREBU

FAKULTET ELEKTROTEHNIKE I RAČUNARSTVA

DIPLOMSKI RAD br. 1913

OPTIMIZACIJA ROJEM ČESTICA NA PARALELNIM ARHITEKTURAMA

Ante Bodić

Voditelj: Prof. dr. sc. Domagoj Jakobović

Zagreb, rujan 2012.

Page 2: optimizacija rojem čestica na paralelnim arhitekturama

Zahvaljujem mentoru Prof. dr. sc. Domagoju Jakoboviću na strpljenju, pomoći i vodstvu. Bez njegovih savjeta ovaj rad sigurno ne bi mogao biti kvalitetno i pravovremeno završen.

Također zahvaljujem roditeljima i cijeloj obitelji na moralnoj i financijskoj podršci potrebnoj za studiranje i pisanje rada.

Page 3: optimizacija rojem čestica na paralelnim arhitekturama

SADRŽAJ 1. Uvod.....................................................................................................................1

2. Paralelizam i paralelna računala ..........................................................................2

2.1 Performanse paralelnih sustava....................................................................6

2.2 Sklopovlje sposobno za paralelizam ...........................................................10

2.2.1 Stream procesori .......................................................................................... 10

2.2.2 Glavni računalni procesori ............................................................................ 11

2.2.3 Grafički procesori.......................................................................................... 12

2.2.4 Procesori za digitalnu obradu signala ........................................................... 13

2.2.5 Programiljivi logički uređaji............................................................................ 13

2.2.6 Cell BE i NEC Sx serije procesora ................................................................ 14

2.2.7 APU.............................................................................................................. 15

2.2.8 Sustavi na čipu ............................................................................................. 16

2.2.9 ASIC............................................................................................................. 17

2.3 Paralelizacija algoritama i programiranje paralelnih sustava.......................17

2.3.1 Paralelni algoritmi ......................................................................................... 17

2.3.2 Programiranje paralelnih sustava.................................................................. 21

2.3.3 Programska podrška za paralelne sustave ................................................... 23

3. Prethodna istraživanja........................................................................................25

3.1 RapidMind platforma...................................................................................25

3.1.1 Općenito ....................................................................................................... 25

3.1.2 Način funkcioniranja ..................................................................................... 25

3.1.3 Elementi RapidMind-a .................................................................................. 25

3.1.3.1 Vektori.......................................................................................... 25

3.1.3.2 Nizovi i pogledi............................................................................. 27

3.1.3.3 Programi ...................................................................................... 30

3.2 Računanje π ...............................................................................................33

3.2.1 Implementacija ............................................................................................. 34

3.2.2 Rezultati ....................................................................................................... 34

3.3 Generiranje pseudo-slučajnih nizova brojeva .............................................35

3.3.1 Implementacija ............................................................................................. 36

3.3.2 Rezultati ....................................................................................................... 38

3.4 Algoritam k srednjih vrijednost ....................................................................39

3.4.1 Implementacija ............................................................................................. 40

3.4.2 Rezultati ....................................................................................................... 42

3.5 Paralelna implementacija dijelova postupka sažimanja digitalne slike po JPEG normi ...........................................................................................................43

Page 4: optimizacija rojem čestica na paralelnim arhitekturama

3.5.1 Pretvorba prostora boja i smanjenje prostorne razlučivosti ........................... 44

3.5.2 Diskretna kosinusna transformacija i kvantizacija ......................................... 45

3.5.3 Preslaganje elementa i entropijsko kodiranje ................................................ 46

3.5.4 Implementacija ............................................................................................. 47

3.5.5 Rezultati ....................................................................................................... 49

3.6 Marching cubes algoritam ...........................................................................51

3.6.1 Implementacija ............................................................................................. 53

3.6.2 Rezultati ....................................................................................................... 55

4. Norma OpenCL ..................................................................................................58

4.1 Sklopovski model ........................................................................................58

4.2 Model izvršavanja .......................................................................................59

4.3 Memorijski model ........................................................................................60

4.4 Programski model .......................................................................................62

4.5 Sinkronizacija..............................................................................................62

4.6 Povezivanje modela i preslikavanje na sklopovlje.......................................63

4.7 OpenCL programsko okruženje ..................................................................65

4.8 OpenCL C ...................................................................................................66

4.9 Ostale karakteristike OpenCL norme ..........................................................68

4.10 Druga rješenja za programiranje masovno paralelnih sustava....................69

5. Karakteristike korištenog sustava.......................................................................71

5.1 Glavni procesor...........................................................................................71

5.2 Grafički procesor.........................................................................................72

5.2.1 Naputci za programiranje AMD Radeon HD 5850 grafičkog procesora......... 74

5.2.1.1 Interna arhitektura AMD Radeon HD 5850 grafičkog procesora...76

5.2.1.2 Valovi ............................................................................................79

5.2.1.3 Globalna memorija........................................................................80

5.2.1.4 Lokalna memorija .........................................................................82

5.2.1.5 Privatna memorija.........................................................................84

5.2.1.6 Memorija za konstante..................................................................85

5.3 Korišteni programski alati............................................................................85

5.4 Sklopovlje i razvojno okruženje za RapiMind platformu ..............................86

5.5 Uvjeti tijekom testiranja ...............................................................................87

6. Optimizacija rojem čestica..................................................................................88

7. Implementacija ...................................................................................................92

7.1 Operacija redukcije .....................................................................................93

7.2 Generator pseudo-slučajnih brojeva ...........................................................96

Page 5: optimizacija rojem čestica na paralelnim arhitekturama

7.3 Osnovne matrične operacije .......................................................................96

7.4 Paralelizacija metode optimizacije rojem čestica ........................................98

7.5 Implementacija ostvarenih RapidMind algoritama u OpenCL-u ................105

7.5.1 Računanje π ............................................................................................... 105

7.5.2 Algoritam k srednjih vrijednost .................................................................... 105

7.5.3 Sažimanja digitalne slike po JPEG normi.................................................... 106

7.5.4 Marching cubes algoritam........................................................................... 107

8. Interpretacija rezultata......................................................................................109

8.1 Generiranje nizova pseudo-slučajnih brojeva ...........................................109

8.2 Matrične operacije.....................................................................................115

8.3 Operacije redukcije ...................................................................................117

8.4 Optimizacija rojem čestica ........................................................................120

9. Zaključak..........................................................................................................125

10. Literatura ..........................................................................................................126

11. Sažetak ............................................................................................................129

12. Particle swarm optimization on parallel processor architectures ......................130

Page 6: optimizacija rojem čestica na paralelnim arhitekturama

1

1. Uvod

Paralelne arhitekture sve se više nameću kao glavni način ubrzanja izvođenja algoritama na računalu. U arhitekturama glavnih procesora (eng. Central Processing Unit - CPU) pojavljuju se elementi masovnog paralelizma, a s druge strane, grafički procesori (eng. Graphics Processing Unit - GPU), koji su zbog same namjene oduvijek bili izrazito paralelne arhitekture, tijekom vremena su postali visoko programabilni kako bi omogućili što veću fleksibilnost u prikazu različitih vizualnih efekata. Zbog sve zahtjevnijih aplikacija, novih područja primjene (primjerice inteligentni mobiteli novijih generacija) i dostizanja granica drugih mogućnosti poboljšanja performansi procesora, sve se više arhitektura okreće paralelizmu, kako radi ubrzanja izvođenja algoritama tako i zbog sve važnije energetske učinkovitosti.

Ipak, mnogi algoritmi ostvareni su za slijedno izvođenje zbog čega se javlja pitanje neiskorištenosti takvih paralelnih procesora, a samim time i njihove svrhe. Potrebno je pronaći što veći broj novih paralelnih algoritama kojima bi se iskoristio sav potencijal paralelizacije.

Također, opisane pojave masovne paralelizacije raznih sklopovskih arhitektura zahtijevaju i novi, drugačiji programski model koji bi omogućio jednostavno i brzo iskorištavanje sklopova.

Tematika ovog diplomskog rada je osvrt na masovno paralelne sklopovske arhitekture i načine njihovog korištenja. Pregled tehnologija prikazan je kroz paralelizaciju metode optimizacije rojem čestica. Metoda je prilagođena izvođenju na novijim grafičkim procesorima ili, u manjem dijelu, višejezgrenim glavnim procesorima.

U drugom poglavlju opisani su osnovni koncepti vezani uz paralelizam, njegov povijesni razvoj, smjerovi u kojima se trenutno razvija te njegove najbitnije sklopovske i programske aspekte. Autorova istraživanja masovno paralelnih tehnologija prethodne generacije i eksperimentalne primjene istih prikazane su u trećem poglavlju. Četvrto poglavlje daje kratak pregled norme OpenCL. Peto poglavlje objašnjava metodu optimizacije pomoću roja čestica. Pristup paralelizaciji i ostali aspekti implementacije koncepata opisanih u petom poglavlju objašnjeni su u šestom poglavlju. Sedmo poglavlje daje prikaz dobivenih rezultata i njihovu analizu, interpretaciju i objašnjenje. Zaključna razmatranja o primjenjivosti korištenih tehnologija i implementiranih algoritama, te potencijalnom daljnjem smjeru razvoja paralelizma izneseni su u posljednjem, osmom poglavlju.

Page 7: optimizacija rojem čestica na paralelnim arhitekturama

2. Paralelizam i paralelna računala

Paralelizam u računalnoj znanosti podrazumijeva istodobno obavljanje više operacija radi smanjivanja vremena potrebnog za njihovo obavljanje. Ostvariv je na više razina: umrežavanjem računala, korištenjem višeprocesorskih sustava ili na razini samog procesora.

Jedna od osnovnih podjela računala jest podjela prema odnosu procesorskih instrukcija i načina na koji se prema tim instrukcijama obrađuju podatci (Flynnova taksonomija [1]). Ova se klasifikacija temelji na arhitekturi samih procesora:

• SISD (eng. Single Instruction Single Data) – svaka instrukcija se primjenjuje na jedan (skalarni) podatak

• SIMD (eng. Single Instruction Multiple Data) – instrukcija se primjenjuje na više skalarnih podataka (koji sačinjavaju vektor) istodobno (npr. množenje svih elemenata vektora s konstantom)

• MISD (eng. Multiple Instructions Single Data) – više instrukcija primijenjeno na isti podatak

• MIMD (eng. Multiple Instruction Multiple Data) – više instrukcija se izvršava nad više podatak istovremeno (svestraniji, ali i složeniji pristup)

Na malo višoj razini apstrakcije, razlikuju se četiri vrste paralelizma: paralelizam na razini bitova (eng. bit-level parallelism), paralelizam na razini instrukcija (eng. instruction-level parallelism), podatkovnog paralelizam (eng. data-parallel) i paralelizma na razini zadataka (eng. task-parallel).

Paralelizam na razini bitova relativno je česta pojava kod modernih procesora. U osnovi, ova vrsta paralelizma temelji se na istovremenoj obradi više bitova. Tipičan primjer ovakvog paralelizma bilo bi izvođenje logičke funkcije negacije (eng. not) nad nekim podatkom zapisanim u određenom procesorskom registru. Funkcija će se u većini modernih procesora obaviti istovremeno nad svim bitovima u registru. U sedamdesetima i prvoj polovici osamdesetih godina 20 stoljeća, ubrzanje kod većine procesora ostvarivano je velikim dijelom pomoću paralelizacije na razini bitova.

Moderni procesori koji se temelje na složenijim cjevovodnim arhitekturama obično podržavaju i paralelizam na razini instrukcija. Kako se svaka procesorska instrukcija unutar procesora provodi u više faza (npr. dohvaćanje instrukcije, dekodiranje instrukcije, računanje memorijskih adresa, dohvaćanje podataka iz memorije, izvršavanje operacije nad podatcima, spremanje rezultata operacije nazad u memoriju), cjevovodne arhitekture svaku fazu izvršavaju istovremeno s drugim fazama preostalih instrukcija, pa će tako procesor s N-faznim cjevovodom imati N instrukcija u raznim fazama zgotovljenosti (Slika 2.1). Primjera radi, Intelov Pentium 4 procesor koncipiran je oko cjevovoda s 35 faza. Velika problematika ovakvog pristupa su instrukcije grananja.

Kod podatkovnog paralelizma paralelno se obrađuju podatci, ali svaki procesor koji sačinjava paralelno računalo odnosno sustav (paralelni procesor) obavlja otprilike iste operacije nad podacima iz istog skupa. Tipičan primjer podatkovnog paralelizma bilo bi množenje matrica gdje bi svaki paralelni procesor računao vrijednost jednog elementa matrice koja predstavlja rezultat množenja.

Page 8: optimizacija rojem čestica na paralelnim arhitekturama

Slika 2.1 5-fazni procesorski cjevovod – princip rada

Paralelizam na razini zadataka podrazumijeva da se svakom paralelnom procesoru zadaju sasvim neovisni zadatci, kao što je, primjerice slučaj s modernim operacijskim sustavima i višejezgrenim procesorima: na svakoj jezgri će se izvršavati pojedini proces ili dretva, a ti procesi mogu biti sasvim različiti (dok, na primjer, jedna procesorska jezgra vrše dekompresiju sažetog digitalnog video materijala, druga obrađuje sustavske pozive).

Još jedan termin često vezan uz paralelizam je naziv stream (eng. stream = tok) računarstvo koje se temelji na paralelnom izvršavanju određenih nizova instrukcija nad nekim skupom podataka. Takvi nizovi instrukcija onda se nazivaju jezgrama ili jezgrenim funkcijama (eng. kernel). Nad istim skupom podataka često se izvršava više jezgri, slijedno ili konkurentno. Terminološki, kaže se da se za svaki element iz skupa pokreće nova instanca jezgre odnosno jezgrene funkcije (Slika 2.2). Zbog ovakvog načina rada stream procesori su SIMD arhitekture po Flynnovoj taksonomiji, ali ponekad se klasificiraju i kao SPMD procesori (eng. Single Program Multiple Data – Slika 2.3).

U novijoj povijesti paralelnog računarstva sve je prisutniji i termin masovni paralelizam, kojim se pokušava naznačiti izrazito velik broj paralelnih procesora koji su sposobni obraditi velike količine podataka istovremeno. Distinkcija navedenih sustava u odnosu na ostale vidove paralelizma obično se radi zato što takvi sustavi za sobom povlače neke posebnosti i problematike vezane uz komunikaciju i skaliranje algoritama. Radi što jeftinije i energetski što učinkovitije izvedbe, ovdje je često riječ o sustavima gdje su svi paralelni procesori smješteni unutar istog računala ili čak na isti računalni čip, a često imaju ograničene mogućnosti sinkronizacije i restrikcije na memorijske kapacitete.

Paralelni procesori mogu izvoditi operacije međusobno se sinkronizirajući nakon svakog koraka (eng. lock-step) ili je moguće da su sasvim neovisni. Primjer prvih bili bi grafički procesori za koji su tipične SIMD arhitekture i sinkronizacija nakon svake instrukcije ili svakog procesorskog takta. Grozdovi računala koji se sastoje od više neovisnih računala povezanih putem računalne mreže demonstriraju, s druge strane, paralelizam kod kojeg procesori nisu u lock-step načinu rada.

Page 9: optimizacija rojem čestica na paralelnim arhitekturama

Slika 2.2 Princip izvršavanja jezgri u stream procesoru

Sama sinkronizacija i komunikacija između paralelnih procesora može se obavljati na različite načine (putem zajedničke memorije, računalne mreže, posebnim sklopovskim sabirnicama…). Pri komunikaciji bitnu ulogu ima i topologija komunikacijskih kanala kojima su paralelni procesori povezani: zbog previsokog stupnja kompleksnosti kod velikih paralelnih računalnih sustava, paralelni procesori često nisu povezani tako da svaki procesor ima mogućnost direktne komunikacije s bilo kojim drugim paralelnim procesorom u sustavu.

Posebno je moguće govoriti o konceptu trivijalno paralelnih zadataka (eng. embarrassing parallel), gdje svaka procesorska jedinica može obaviti zadane poslove bez ikakve potrebe za komunikacijom ili sinkronizacijom s ostalim procesorima koji sačinjavaju paralelni sustav. Jednostavan primjer trivijalnog paralelizma bio bi zbrajanje dvaju vektora: svaki paralelni procesor će učitati po

Page 10: optimizacija rojem čestica na paralelnim arhitekturama

jednu komponentu iz svakog vektora-pribrojnika i zapisati rezultat zbroja učitanih komponenti na odgovarajuće mjesto u vektoru koji predstavlja zbroj. U tom postupku neće biti potrebno razmjenjivati podatke ili bilo koju vrstu komunikacijskih poruka s preostalim procesorima. Ovakva potpuna neovisnost zadataka često je prisutna u računalnoj grafici gdje se obrada svakog elemenata slike ili scene može obrađivat neovisno o ostalim elementima. Sukladno tome, i procesorske arhitekture namijenjene računalnoj grafici biti će u velikoj mjeri orijentirane na trivijalno paralelne zadatka zbog čega će ponekad biti otežano implementirati algoritme koji zahtijevaju međuprocesorsku komunikaciju i sinkronizaciju.

Slika 2.3 Princip funkcioniranja stream procesora

U kontekstu samog paralelizma važno je objasniti i pojam zrnatosti. Termin zrnatost (eng. granularity) označava količinu proračuna koja se obavlja lokalno na nekom od procesora u odnosu na količinu komunikacije među svim procesorima koji rade paralelno. U vidu trivijalnog paralelizma zrnatost označava količinu podataka ili proračuna dodijeljenih svakom procesoru.

Osim samih procesora, važna komponenta paralelnih računala je i računalna memorija, prvenstveno razni oblici priručne memorije (cache memorija) i memorije s nasumičnim pristupom (Random Access Memory - RAM). Razne računalne arhitekture podrazumijevaju različiti način pristupanja takvim memorijama - npr. nekom dijelu memorije može pristupati samo jedan paralelni procesor (Masovno Paralelni Procesori – MMP, najčešće ostvareni kao skupine neovisnih računala koja se sinkroniziraju i koordiniraju putem mreže ), samo određena skupina paralelnih procesora (primjerice svi procesori jednog računala koje je dio nekog

Page 11: optimizacija rojem čestica na paralelnim arhitekturama

složenijeg (super)računala) ili svi paralelni procesori u sustavu (Simetrično MultiProcesiranje – SMP). Dijeljenje memorije između više paralelnih procesora olakšava komunikaciju i razmjenu podataka među njima, ali istovremeno povlači problematiku prava pristupa nekom podatku koji se možda ne smije mijenjati jer je potreban nekom drugom procesoru. Ovakvi sinkronizacijski i komunikacijski problemi dodatno povećavaju složenost paralelnih sustava.

Još jedan važan aspekt paralelnih sustava je i način komunikacije među pojedinim (paralelnim) procesorima u sustavu. Prvi parametar komunikacije je metoda komunikacije. Na sklopovskoj razini postoji više načina komuniciranja između paralelnih procesora, od posebnih brzih sabirnica, preko komuniciranja putem zajedničke memorije, pa sve do komunikacije preko računalnih mreža poput lokalnih mreža ili čak interneta. Različiti načini komunikacije imaju različite propusnosti podataka i kašnjenja u odzivu te tako, u ovisnosti o potrebama problema koji se rješava, određuju primjenjivost nekog paralelnog sustava u rješavanju neke problematike.

Na programskoj razini, način komunikacije najčešće će biti definiran paralelnim sklopovljem na kojem se sustav temelji. Sustavi s dijeljenom memorijom (SMP) komunicirat će modifikacijom varijabli u dijeljenoj memoriji.

Drugi važan koncept vezan za međuprocesorsku komunikaciju je topologija komunikacijske mreže. Kod određenih paralelnih sustava, postoji mogućnost proizvoljne međusobne komunikacije između pojedinih dijelova sustava, ali neki, prvenstveno veći, sustavi imaju određenu topologiju koja im omogućava da komuniciraju samo s određenim, susjednim procesorima u sustavu. Dotična ograničenja najčešće proizlaze iz ograničenja tehnologija koje se koriste za međuprocesorsku komunikaciju: istovremena međusobna komunikacija između svih procesora u N-procesorskom sustavu zahtjeva N2 veza. Sustavi sa specifičnim topologijama često pogoduju nekim algoritmima više nego drugim. Općenito se prilikom razvoja paralelnog algoritma velika važnost mora pridavati međuprocesorskoj komunikaciji. Tako će komunikacijska topologija u kojoj svaki procesor može stupit u komunikaciju samo s procesorima koji su tik do njega ((hiper)kocka) pogodovati primjerice simulacijama fluida metodom konačnih volumena ili modeliranju staničnih automata. S druge strane, topologija u kojoj skupina čvorova ima nadređeni čvor sa kojim komunicira biti će vrlo pogodna za redukcijske probleme.

Naravno, očito je da će neke forme paralelizma i određene računalne arhitekture automatski isključivati neke druge mogućnosti ili ih činiti neizbježnima, pa će tako paralelno izvršavanje različitih zadataka biti nemoguće na SIMD arhitekturama ako koriste lock-step način rada. U stvarnosti, različite forme paralelizma često se preklapaju ili nadopunjavaju pa se tako, primjerice, često više računala temeljenih na višejezgrenim procesorima povezuje u mrežu kako bi se dobila još veće brzina proračuna.

2.1 Performanse paralelnih sustava

Najvažniji razlog paralelizacije jest povećanje performansi. Performanse paralelnih sustava često se mjere pomoću vremena potrebnog za izvršavanje određenih (općepoznatih) paralelnih algoritama (eng. benchmark). Još jedna često korištena mjera za računsku snagu nekog sustava je broj operacija s pomičnim zarezom

Page 12: optimizacija rojem čestica na paralelnim arhitekturama

koje je sustav sposoban obaviti u sekundi (eng. FLoating point OPerations per Second - FLOPS).

Paralelizacija nekog složenijeg algoritma često nije moguća ili nije u potpunosti moguća. Radi lakšeg određivanja dobitaka koje donosi paralelizacije koristi se nekoliko zakonitosti. Najpoznatiji među njima je Amdahlov zakon [2] formuliran od strane računalnog arhitekta Genea Amdahla. Ako se pretpostavi da se nad nekim algoritmom može obaviti dekompozicija na njegovu slijednu komponentu i komponentu koju je moguće paralelizirati navedena zakonitost omogućava procjenu najvećeg mogućeg dobitka u vidu brzine izvođenja, ako određeni dio algoritma označen s P (P predstavlja udio u cjelokupnom algoritmu i zato je iz intervala [0, 1]) poveća brzinu izvođenja za S puta - jednadžba (2.1).

( )S

PP

ubrzanje

+−

1

1

(2.1)

( )N

PP

ubrzanje

+−

1

1

(2.2)

Iz perspektive paralelizma obično se pretpostavlja da će se s primjenom N paralelnih procesora, dio algoritma koji je paraleliziran ubrzati N puta, odnosno da je S funkcija od N (jednadžba (2.2)). Oznaka ≤ naglašava da je to teoretsko maksimalno moguće ubrzanje i u praksi najčešće nije ostvarivo.

( ) P

S

PPS −

=

+−∞→ 1

1

1

1lim

(2.3)

Iz jednadžbe (2.3) vidljivo je da će ubrzanje težiti nekom konačnom broju odnosno da je učinkovitost paralelizacije zavisna prvenstveno o dijelu algoritma koji se ne može paralelno implementirati. Kako parametar P raste od nula do jedan ubrzanje će se kretati od jedan (slijedni algoritam – nema ubrzanja) do beskonačno, uz pretpostavku dostupnosti neograničenog broja procesora. Vidljivo je da ubrzanje asimptotski teži nekoj vrijednosti i da nakon neke točke dodavanje procesorskih jedinica zanemarivo utječe na brzinu izvođenja odnosno da slijedni dio algoritma počinje biti dominantan faktor (usko grlo) u brzini izvođenja jer je pribrojnik P/N pao na jako male vrijednosti (Slika 2.4). Idealan broj procesora za neki paralelni algoritam mora se odabrati iz područja prije nego što funkcija ubrzanja uđe i područje zasičenja.

Page 13: optimizacija rojem čestica na paralelnim arhitekturama

Slika 2.4 Ovisnost mogućeg ubrzanja o parametrima P i N

Amdahlov zakon zasniva se na nekoliko pretpostavki, koje njegovu primjenu obilježavaju više kao indikativnu smjernicu nego kako konkretan parametar prema kojem se projektira sustav ili razrađuje algoritam. Među ostalim Amdahlov zakon kreće od pretpostavke da se veličina problema ne mijenja. Proturječan pristup opisan je Gustafosonvim zakonom [3] (jednadžba (2.4)) kojeg su 1988. godine formulirali John Gustafson i Edwin Barsis. Ova zakonitost usmjerena je povećanju obujma problema kako broj paralelnih procesora raste. Ovakvim pristupom omogućeno je dobivanje veće preciznosti ili veće količine ili broja podataka s porastom broja paralelnih procesora u jednakom vremenskom trajanju. Za razliku od Amdahlovog zakona koji se više koristi za prilagođavanje sklopovlja određenom problemu, Gustafsonov zakon prvenstveno je usmjeren na redizajn pristupa paralelizaciji sa strane problematike koja se rješava kako bi se implementacijom na velikim paralelnim sustavim dobilo više u istom vremenskom intervalu. Naravno, već sama polazna premisa Gustafsonovg zakona onemogućava mu svrsishodnu interpretaciju učinaka paralelizacije na probleme fiksne veličine.

( ) ( )11 −⋅−+≤ NPNubrzanje (2.4)

Daljnja generalizacija Amdahlovog i Gustafsonovog zakona ostvarena ja u Sun-Ni zakonu [4][5] formuliranom od Xian-He Suna i Lionela Nia (jednadžba (2.5); F(N) predstavlja koliko se puta može povećati paralelni dio algoritma ako se koristi N procesora). Sun-Ni zakon nameće dodatno ograničenje na memorijski kapacitet koji je dostupan pojedinom paralelnom procesoru i koristi Amdahlov i Gustafsonov zakon kako bi maksimirao procesorske i memorijske kapacitete, pa se može

Page 14: optimizacija rojem čestica na paralelnim arhitekturama

interpretirati da dok Amdahlov zakon pokušava održati konstantnu učinkovitost, a Gustafsonov zakon konstantnu brzinu, Sun-Ni zakon teži održavanju stalne iskoristivosti.

( ) ( )

( )( )N

NFPP

NFPPubrzanje

⋅+−

⋅+−≤

1

1

(2.5)

U dosadašnjima razmatranjima nije pretjerano vođeno računa o samoj pogodnosti algoritma za paralelizaciju. Tijekom paralelizacije nekog algoritma sama njegova priroda često onemogućava skaliranje na proizvoljan broj procesora. Navedena pojava često je uzrokovana potrebom za međuprocesorskom komunikacijom i sinkronizacijom. U praksi se pokazalo da kako broj paralelnih procesora raste, međuprocesorska komunikacija će u nekoj točki najčešće postati usko grlo sustava i dobitak performansi s povećanjem broja paralelnih procesora će opadati upravo zbog te međuprocesorske komunikaciji. Zato ubrzanje algoritma s porastom broja dostupnih (paralelnih) procesora najčešće ne pokazuje linearno nego sublinearno ponašanje. Međuprocesorska komunikacija obično ima složenost O(N) ili O(log N). Troškovi međuprocesorske komunikacije u pravilu su korelirani sa zrnatošću: što je viši stupanj zrnatosti, već je mogućnost učinkovite raspodjela posla između paralelnih procesora, ali previsoki stupanj zrnatosti povlači sa sobom i velike vremenske troškove zbog povećane potrebe za međuprocesorskom komunikacijom.

Postoji još nekoliko parametara koji se obično proučavaju u paralelnom sustavu. Jedan od najjednostavnijih je učinkovitost, koja pokazuje imaju li svi paralelni procesori potpunu iskoristivost ili neki od njih nisu iskorišteni bar dio vremena. Izraz za učinkovitost prikaza je u jednadžbi (2.6), gdje T(N) predstavlja vrijeme izvođenja s N procesora.

( )( )NTN

TE

*

1= (2.6)

Karp-Flatt metrika [6] predstavlja mjeru za paraleliziranost programskog koda. Ova metrika opisana je jednadžbom (2.7) i oslanja se na eksperimentalno određeno ubrzanje ψ kako bi odredili koliki je slijedni udio u algoritmu. Upravo činjenica da se oslanja na izmjerene vrijednosti omogućava Karp-Flatt metrici da nadiđe poteškoće koje Amdahlov i Gustafsonov zakon imaju jer ne uzimaju u obzir međuprocesorsku komunikaciju. Niske vrijednosti označavaju učinkovitiji postupak paralelizacije algoritma.

( )111

11

−=

=N

N

N

Ne

ψ

ψψ (2.7)

Izoučinkovitost (eng. isoefficiency) je mjera koja opisuje koliko veličina problema koji paralelni sustav rješava mora rasti kako bi s promjenom broja paralelnih procesora u sustavu učinkovitost toga sustava ostala ne promijenjena. Uz isoučinkovitost veže se pojam funkcije skalabilnosti koja pokazuje koliko moraju

Page 15: optimizacija rojem čestica na paralelnim arhitekturama

rasti memorijski zahtjevi pojedinog paralelnog procesora kako bi učinkovitost ostala nepromijenjena s porastom problema. Suprotan koncept predstavljen je u poju izomemorije – mjeri koja određuje koliko mora rasti veličine problema koji paralelni sustav rješava kako bi količina memorije koju koristi pojedini procesor ostala nepromijenjena. Analogno, izovrijeme izvođenja određuje vezu između veličine problema koji paralelni sustav rješava i broja procesora, tako da vrijeme izvođenja ostaje konstantno, dok izozrnastost zahtjeva konstantnu količinu podataka po pojedinom paralelnom procesoru.

2.2 Sklopovlje sposobno za paralelizam Današnji moderni procesori sve češće se okreću paralelizmu umjesto povećanju radnog takta kao izvoru ubrzanja. Procesori različite namjene tako postaju sve sličniji po arhitekturi, načinu programiranja i primjene. Ipak različiti tipovi procesora zbog svoje namjene ili iz povijesnih razloga implementiraju različite vidove paralelizma. Jedino je paralelizam na razini bitova u velikoj je mjeri prisutan u svim vrstama procesora već dulje vrijeme. Mnogi paralelni procesori (primjerice grafički procesori, procesori za digitalnu obradu signala, programirljivi logički uređaji) često se koriste kao pomoćni procesori kojima i dalje upravlja glavni procesor u računalu.

Za tematiku ovog rada najvažniji su komercijalno dostupni glavni procesori i grafički procesori.

2.2.1 Stream procesori

Naziv stream procesori ne odnosi se na neki konkretni model ili seriju računalnih procesora, ali predstavlja relativno karakterističan koncept koji je potrebno opisati u kontekstu tematike računalnog paralelizama. Iako se mnogi procesori sa svojstvima paralelizma mogu promatrati ili koristiti kao stream procesori, u nastavku poglavlja navedena su svojstva koja stream procesori najčešće imaju.

Stream procesori često pokazuju svojstva paralelizma na nekoliko razina i obično su masovno paralelnog tipa. Građeni su od velikog broja ALU kojima se upravlja pomoću jedne ili manjeg broja jedinica za dohvaćanje i interpretaciju procesorskih instrukcija.

Iz perspektive programiranja stream procesora, jezgrena funkcija obavlja se istovremeno nad čitavim skupom podataka, ali je moguće da će se u pozadini, zbog ograničene memorijske propusnosti i broja ALU-a, jezgrena funkcija u nekoliko iteracija odvijati nad različitim dijelovima skupa podataka. U ovakvim situacijama će do izražaja doći prednosti pohranjivanja instrukcija jezgrene funkcije u posbenu memoriju na samom stream procesoru. ALU su relativno jednostavne građe i bez pretjerano složenih memorijskih struktura, te kao takve nemaju velike mogućnosti međusobne komunikacije ili sinkronizacije.

Podatci koji se obrađuju mogu biti skalarnog, ali i vektorskog tipa. Unutar same instance jezgrene funkcije pojedine komponente vektora mogu biti obrađivane relativno nezavisno, pa se u nekim izvorima stream procesori klasificiraju kao MIMD arhitekture.

Tradicionalno, koncept stream procesora uključuje model programiranja koji podrazumijeva neka ograničenja kao što su primjerice načini pristupa podacima ili lock-step način rada (način rada u kojem svu paralelni procesori izvršavaju istu

Page 16: optimizacija rojem čestica na paralelnim arhitekturama

instrukciju istovremeno). Međutim, kod novijih generacija stream procesora, granice između stream procesora i ostalih vrsta paralelnih procesora postaju nejasne pa tako ALU bivaju grupirani u skupine koje imaju svoju vlastitu priručnu memoriju, upravljačke jedinice koje su neovisne o u odnosu na upravljačke jedinice ostalih skupina i mogućnost međusobne sinkronizacije.

Iako se stream procesori koriste više kao model, postoje eksperimentalni pokušaji ostvarivanja takvih procesora – primjerice Imagine sa Stanfordskog sveučilišta.

2.2.2 Glavni računalni procesori

Glavni procesori (eng. Central Processing Unit - CPU) u pravilu su tijekom svog povijesnog razvoja usvojili relativno složene procesorske arhitekture koje se temelje na složenim podatkovnim cjevovodima s velikim brojem razina. Istovremeno s razvojem cjevovodne građe razvijani su i napredni sustavi predikcije grananja programskog toka. Kasnije, prije intenzivnijeg razvoja grafičkih procesora, glavnim procesorima dodane su i instrukcije koje se izvršavaju nad vektorskim umjesto nad skalarnim vrijednostima (npr. MMX, SSE ili 3DNow! instrukcije). Ovakva pozadina omogućila je glavnim procesorima dobre preduvjete da postanu učinkoviti u izvršavanju masovno paralelnih zadataka.

Kada su proizvođači računalnih procesora (npr. AMD i Intel) počeli masovno plasirati na tržište višejezgrene procesore za komercijalne, široko dostupne sustave ovi procesori već su imali sposobnost nekoliko razina paralelizma koje su uključivale paralelizam na razini bitova, na razini instrukcija i SIMD komponente (vektorske instrukcije). Povećanjem broja takvih jezgri dobiven je i paralelizam na razini zadataka jer je svaka procesorska jezgra u biti bila neovisna. U odnosu na višeprocesorske sustave, višejezgreni procesori imaju manju potrošnju i jednostavniju i bržu komunikaciju između pojedinih paralelnih procesora.

Tipičan primjer takvog procesora bio bi neki procesor iz Core i7 serije procesora koje proizvodi tvrtka Intel. Ovi procesori mogu imati i do šest fizičkih jezgri, ali se zbog velike dubine cjevovoda prividno čini da ih imaju dvostruku više (dvanaest). Takvi procesori postižu performanse i preko 100 GFLOPS-a koristeći brojeve s pomičnim zarezom u dvostrukoj preciznosti (double).

Mana glavnih procesora jest njihova memorijska propusnost koja je često ograničena, prvenstveno širinom sabirnice. Kako bi zaobišlo ovo usko grlo, velik dio procesorskog sklopovlja sačinjava priručne (eng. cache) memorije različitih razina i namjena (za instrukcije i za podatke). Još jedan način povećanja memorijske propusnosti je podizanje radnog takta na kojem memorijska sabirnica komunicira s procesorom, ali ovakav pristup vrlo brzo doseže svoja ograničenja. Zbog velike količine priručne memorije i složene cjevovodne arhitekture i sustava za predviđanje grananja, glavni procesori ne mogu imati velik broj aritmetičko-logička jedinica (eng. Arithmetic and Logic Unit - ALU). Glavni procesori imaju relativno kompleksne jezgre koje bi trebale biti što svestranije jer su opće namjene. Radni taktovi glavnih procesora relativno visoki, a jedna od najvažnijih prednosti im je što su jezgre u potpunosti neovisne (na glavnim modernim procesorima čak se i radni takt pojedinih jezgri dinamički prilagođava trenutnom opterećenju).

Page 17: optimizacija rojem čestica na paralelnim arhitekturama

2.2.3 Grafički procesori

Povijesno gledano, razvojem računala i njihove procesorske moći, sučelje prema korisniku zauzima sve važnije mjesto. Kako se procesorsko vrijeme ne bi trošilo na iscrtavanje prikaza na računalnom monitoru, pojavljuju se zasebna sklopovska proširenja računalnih sustava namijenjenih prvenstveno računalnoj grafici. Sedamdesetih i osamdesetih godina dvadesetog stoljeća, grafički podsustavi sposobni su na sebe preuzeti samo manji dio zadaća iscrtavanja i to samo u dvodimenzionalnoj (2D) grafici S vremenom su grafički procesori postajali sve moćniji, i glavni fokus prešao je na brzo iscrtavanje trodimenzionalne (3D) grafike. Glavni pokretač ove pojave je masovna popularnost grafički sve zahtjevnijih računalnih igara.

3D računalna grafika u realnom vremenu većinom se temelji na približnom opisivanju oblika stvarnih objekata kao skupova neke vrste poligona, najčešće trokuta. U procesu iscrtavanja – grafičkom cjevovodu – pojednostavljeno gledano, za svaki poligon određuje se kako je osvijetljen. Potom se na njega primjenjuju neki modeli materijala (teksture) koji bi trebali simulirati materijale u stvarnom svijetu i na temelju prethodno izračunatog osvjetljenja interpolira se boja svake točke unutar poligona. Vidljivo je kako se u ovakvom modelu svaki poligon ili, kasnije u grafičkom cjevovodu, svaka točka može obrađivati neovisno. Zbog kompleksnosti 3D scena, velikog broja točaka na prosječnom računalnom monitoru i zahtjeva za obradom tako velike količine podataka u kratkom (realnom) vremenu, masovno paralelne arhitekture nameću se kao idealno rješenje.

Kako su zahtjevi stavljani pred računalnu grafiku postajali sve veći, grafički je cjevovod dobivao sve više sklopovski implementiranih mogućnosti i sve veću programirljivost. Inicijalno, početkom devedesetih godina prošlog stoljeća, grafički procesor služio je samo za rasterizaciju, a kasnije su sve geometrijske transformacije i proračun osvjetljenja (eng. Transform and Lighting – T&L) sklopovski implementirani. Kako bi se postigli proizvoljni vizualni efekti polako je rasla i mogućnost programiranja raznih dijelova grafičkog cjevovoda, gdje je fokus bio prvenstveno na T&L jedinicama. Dijelovi grafičkog cjevovoda više nisu sakriveni iza programskih sučelja (eng .Application Programming Interface - API), nego programeri mogu pisati vlastite kratke programe za sjenčanje (eng. shader). S vremenom dužina i mogućnosti programiranja spomenutih programa za sjenčanje će rasti. Ovakvi grafički procesori sada imaju veliku složenost i računalnu moć.

Paralelno s mogućnostima grafičkog sklopovlja raste i programska podrška. Uz postojeća programska sučelja poput OpenGL (Khronos grupacija) i DirectX (Microsoft), javljaju se i novi jezici za sjenčanje (Nvidia Cg ili High Level Shading Language – HLSL). Uskoro takvi programski jezici i počinju diktirati razvoj sklopovlja, pa tako primjerice, DirectX u verziji 9 uvodi u programe za sjenčanje mogućnost kontrole toka, dok verzija 11 predviđa mogućnost teselacije (proceduralnog generiranja poligona na samom grafičkom procesoru), a proizvođači grafičkog sklopovlja moraju dizajnirati procesore koji zadovoljavaju postavljene specifikacije.

Ovakav brzi razvoj uzrokovao je veoma natjecateljske uvjete među proizvođačima grafičkih procesora tako, da danas samo dvije tvrtke – AMD/ATI i nVidia – drže gotovo cjelokupno tržište, dok su ostale propale (primjerice SGI) ili se preorijentirale (3D Labs) na neku drugu vrstu proizvoda.

Page 18: optimizacija rojem čestica na paralelnim arhitekturama

Pred početak 21. stoljeća počinju se javljati ideje da procesori s tolikom brzinom obrade podataka mogu biti korišteni i za uobičajenu obradu podataka, a ne isključivo za obradu 3D grafike – GPGPU (eng. General Purpose computing on Graphic Processing Units). Prvi pokušaji uključivali su tek jednostavno izmjenjivanje prikazane slike, primjerice propuštanje slike kroz filtar za detekciju rubova. Kasnije se javljaju rudimentarne matematičke simulacije fluida. Sami proizvođači grafičkog sklopovlja polako počinju uviđati da je GPGPU novo potencijalno tržište i najavljuju podršku za GPGPU. Isprva je podrška stizala od vanjskih (“3rd party“) proizvođača, ali kasnije i sami ATI i Nvidia predstavljaju svoja rješenja koja kvalitetno surađuju s njihovim sklopovljem.

U vrijeme pisanja ovog rada GPGPU je već postao koncept koji je prilično raširen, iako još vjerojatno nije ostvario svoj puni potencijal niti razinu raširenosti usporedivu s primjenom grafičkih kartica u računalnim igrama. Razne ugledne tvrtke nude svoja programska i sklopovska rješenja bazirana na GPGPU ideji, a održavaju se i mnogobrojne konferencije o primjeni grafičkih procesora u raznim područjima. Moguće je i unajmiti, programirati i koristiti grafičke procesore preko interneta. Među najbržim superračunalima na svijetu sve je više onih koja su bazirana na grafičkim procesorima. Moguće zaključiti kako ova tehnologije tek treba doseći svoj puni utjecaj na tržištu.

2.2.4 Procesori za digitalnu obradu signala

Procesori za digitalnu obradu signala (eng. Digital Signal Processor - DSP) su specijalizirane procesorske jedinice, najčešće SIMD arhitekture. Optimiranisu za brzo i (energetski) učinkovito izvođenje operacija vezanih za obradu signala. Ovakve procesore karakterizira sposobnost učinkovitog izvršavanja petlji s malim brojem naredbi, memorijske sabirnice relativno visoke propusnosti i različiti specifični načini adresiranja memorije i pojedinih bitova. Iako moderni procesori za digitalnu obradu signala dosežu za današnje pojmove relativno niske performanse reda veličine GFLOPS-a, neki modeli sposobni su konkurirat modernim glavnim procesorima. Primjerice procesori za digitalnu obradu signala iz serije TMS320C66x proizvođača Texas Instruments u određenim konfiguracijama postižu i do 160 GFLOPS-a odnosno 320 GMACS-a (eng. Giga Multiply-Accumulate Operations per Second – operacija prikazana u jednadžbi (2.8); često se koristi pri digitalnoj obradi signala).

( )cbaa ⋅+← (2.8)

2.2.5 Programiljivi logički uređaji

Važnu ulogu u kontekstu paralelnih procesora imaju i programirljivi logički uređaji (end. Programmable Logic Device - PLD). To su integrirani sklopovi (eng. integrated circuit) koji u trenutku proizvodnje nemaju definiranu funkcionalnost. Funkcionalnost sklopa biva definirana kasnije od strane dizajnera ili korisnika. Osnovna svrha ovakvih uređaja je razvoj sklopovlja sa specifičnom, visoko specijaliziranom funkcionalnošću. Neki programirljivi logički uređaji mogu se programirati samo jednom ili mali broj puta, druge vrste se mogu programirati često i bez velikih poteškoća, jednostavnom izmjenom sadržaja pojedinih memorijskih ćelija. Interno se uređaji sastoje od većeg broja najčešće jednostavnih logičkih funkcija koje se mogu proizvoljno povezivati, a često su uparene s

Page 19: optimizacija rojem čestica na paralelnim arhitekturama

memorijskim elementima. Uzevši, k tome, u obzir da ovakvi čipovi u pravilu imaju velik broj ulaznih i izlaznih linija, moguće je ostvariti visok stupanj paralelizma. Pri tome se postiže solidna fleksibilnost i relativno visoka energetska učinkovitost.

FPGA (eng. Field-Programmable Gate Array) su vrsta programiljivih logičkih uređaja složenije građe i relativno velikih sposobnosti. Mogu postići performanse reda veličine 102 do 103 GFLOPS-a. Primjerice, FPGA proizvođač Altera je na Stratix IV FPGA EP4SGX530 uređaju u Monte Carlo Black-Scholes simulaciji uspio postiči 200 GFLOPS-a (naprema 160 GFLOPS-a koje je dosegao Intelov Xenon procesor s četiri jezgre). U nekim ispitivanjima [15] računalni sustavi s FPGA elementima postigli performanse za faktor 100 puta veće u odnosu na klasične računalne sustave bazirane isključivo na računalnim procesorima.

Iako je samo programiranje takvih uređaja relativno složeno, neki proizvođači počeli su uključivati napredne sintagme za paralelno programiranje u svoja istraživanja (npr. kod proizvođača Altera postoji podrška za OpenCL normu objašnjen u poglavlju 4).

Moderni trendovi u razvoju FPGA sklopovlja uključuju integriranje procesorskih jezgri (npr. PowerPC jezgra u Xilinx Virtex-5 porodici procesora). Kada su računalni sustavi u pitanju u pravilu se koriste arhitekture gdje glavni procesori šalju podatke programirljivim logičkim uređajima na obradu.

2.2.6 Cell BE i NEC Sx serije procesora

Porodice procesora kako što su Cell BE ili NEC SX predstavljaju spoj procesorske jezgre s jedinicama za izvođenje vektorskih instrukcija.

Cell BE (Cell Broadband Engine) procesor (Slika 2.5) zajednički su razvili i proizvode Sony, Sony Computer Entertainment, Toshiba i IBM (udruženje poznato kao STI). Ovaj procesor karakterizira jedna jezgra temeljena na IBM-ovoj Power procesorskoj arhitekturi koja je zadužena za sistemske operacije i kontrolu nad ostatkom procesora (Power Processor Element - PPE). Glavnina matematički intenzivnih proračuna odvija se na pomoćnim jedinicama (Synergistic Processing Elements - SPE) koje su u biti RISC procesori (eng. Reduced Instruction Set Computer - Procesori sa smanjeni brojem instrukcija) sposobni obrađivati više podataka po jednom procesorskom taktu (četiri broja s pomičnim zarezom ili 32-bitna cjelobrojna podatka; osam 16-bitnih cjelobrojnih podataka i šesnaest 8-bitnih cjelobrojnih podatak). SPE jedinice imaju svoju vlastitu memoriju (SDRAM) integriranu na procesor koja je namijenjena pohranjivanju instrukcija jezgrene funkcije, tako da jednom kada je programirana jedinica može djelovati i samostalno. Ovakav pristup omogućava proizvoljno serijsko i paralelno povezivanje SPE jedinica. Kako Cell BE nema sposobnost previđanja ganjanja, ova problematika koja je čest problem procesorskih arhitektura sa “stream” svojstvima, riješena je pomoću prevoditelja koji umeću posebne instrukcije koje upozoravaju procesor na nadolazeće grananje u programskom toku (eng. prepare-to-branch instructions). Cell BE proizvodi se u raznim varijantama koje se razlikuju prvenstveno po broju SPE jedinica i radnom taktu. Različite verzije procesora prilagođene su širokom spektru upotrebe od jednostavne obrade signala u potrošačkoj elektronici do izgradnje super-računala (dostupni kao PCI kartice kojima se čvorovi super-računala lagano nadograđuju). Cell BE na radnom taktu od 3.2 GHz opremljen s 8 SPE jedinica postiže teoretske performanse od 230 GFLOPS-a kod brojeva s pomičnim zarezom u jednostrukoj preciznosti što ga čini

Page 20: optimizacija rojem čestica na paralelnim arhitekturama

gotovo dvostruko bržim od najbržih Intelovih procesora, a istovremeno i osjetno energetski učinkovitijim.

Slika 2.5 Građa Cell BE procesora

2.2.7 APU

APU (eng. Accelerated Processing Unit – akcelerirana procesorska jedinica) naziv je za centralne procesore s proširenim mogućnostima procesiranja podatka koje je najčešće ostvareno tako da se glavni procesor integrira s grafičkim procesorom. Ovakav pristup sličan je procesorima opisanima u poglavlju 2.2.6. Glavni cilj pri stvaranju i dizajniranju ovakvih procesora nisu bile performanse nego komercijalna isplativost – APU jedinica najčešće su relativno jeftine i namijenjene osobnoj uporabi, a s druge strane u jednom računalno čipu nude i dodatnu grafičku funkcionalnost eliminirajući potrebu za nabavkom dodatne grafičke kartice. Kasnije su neki proizvođači izdali i verzije namijenjene profesionalnoj primjeni u radnim stanicama. U trenutku pisanja ovog teksta AMD prednjači u proizvodnji ovakvih

Page 21: optimizacija rojem čestica na paralelnim arhitekturama

procesora. Njegovi najjači modeli čiji bi tipični predstavnik bio AMD FirePro A320 APU imaju četiri jezgre (s dodatnim vektorskim instrukcijama) koje rade na taktu od 3.8 GHz, a pored toga posjeduju i grafički podsustav na radnu taktu od 800 MHz s 384 procesorska elementa. Ovakvi sustavi postižu i preko 730 (jednostruka preciznost) odnosno 180 (dvostruka preciznost) GFLOPS-a. Na suprotnom kraju spektra nalaze se APU koji u sposobni postići odličan omjer GFLOPS-a po Watt-u snage i kao takvi su idealni za dostavljanje velike procesorske moći u ugradbene i prijenosne uređaje (npr. AMD Z-Series procesor troši samo 5.9 W, a ima 2 jezgre na radnom taktu od 1 GHz i 80 procesorskih elemenata u grafičkom podsustavu koji funkcionira na radnom taktu od 267 MHz). Dodatna prednost ovakvih procesora je mogućnost brze komunikacije između glavnog grafičkog procesora.

2.2.8 Sustavi na čipu

Sustav na čipu (SoC – System on a Chip) predstavlja vrstu procesora kojima su mogućnost proširene kako bi mogli zamijenit cjelokupni računalni sustav. Ovakvi procesori obično su opremljeni dodatnim kontrolerima i sučeljima prema raznim uređajima. Razlog takvog visokog stupnja integracije su uštede u novcu, prostoru i, najvažnije, potrošnji energije. SoC se obično ugrađuju u razne ugradbene sustave i izrazito kompaktne uređaje kao što su pametni telefoni ili prenosi uređaji za reprodukciju digitalno pohranjene multimedije.

Iako su po karakteristikama i namjeni, naizgled slični klasičnim mikrokontrolerima, SoC u pravilu imaju osjetno više procesorske snage i predviđeno je da se uparuju s više radne memorije, koja ne mora nužno biti integrirana na sam procesor. Ovakvi sustavi često se koriste sa složenijim operacijskim sustavima kao što je Linux i nerijetko rade s naprednim korisničkim sučeljima. Tako mnogi današnji sustavni na čipu imaju ugrađenu podršku za ekrane osjetljive na dodir ili integrirane komponente za komunikaciju s raznim mrežama od Etherneta do 3G mreža. Osim glavnog procesora koji može imati više jezgri, na čipu se često mogu nalaziti i procesori za digitalnu obradu signala i grafički podsustav. Svi ovi sustavi mogu se, teoretski, tretirati kao sklopovlje sposobno za paralelizam, pa se tako na jednom čipu dobiva heterogeno, masovno paralelno okruženje. Jezgre koje sačinjavaju glavni procesor vrlo su često ARM arhitekture, a kako postoje velika ograničenja na procesorsku potrošnju, ponekad se koristi kombinacija više različitih jezgri tako da su jezgre složenije arhitekture i višeg radnog takta isključene dok nema potrebe za procesorski intenzivnim operacijama (kao što je dekodiranje multimedijalnog sadržaja).

Primjer relativno modernog i naprednog sustava na čipu bio bi OMPA 44x0 serija procesora proizvođača Texas Instruments. Ovi procesori imaju dvije napredne ARM Cortex A9 jezgre na radnom taktu između 1 i 1.8 GHz, a također posjeduju i dvije ARM Cortex-M3 jezgre koje se koriste, kao što je opisano, radi redukcije potrošnje električne energije u sustavu. Nadalje, sustav sadrži SGX54x GFX grafički procesor koji radi na taktu od 304-384 MHz (kada radni takt grafičkog procesora SGX544 GFX iznosi 300 MHz, performanse istog dostižu 9.6 GFLOPS-a). Sustav sadrži posebnu jezgru niske potrošnje namijenjenu iscrtavanju 2D grafike. Sklopovlje za dekompresiju digitalnog multimedijskog sadržaja (IVA 3) osim sklopovske akceleracije najčešćih normi za kompresiju, sadrži i programabilni procesor za digitalnu obradu signala predviđen za kompatibilnost s nekim budućim normama, a u sustav je ugrađen i procesor za digitalnu obradu slike.

Page 22: optimizacija rojem čestica na paralelnim arhitekturama

Iako sustavi na čipu po procesorskoj snazi nisu sposobni konkurirati modernim računalnim ili grafičkim procesorima namijenim stolnim računalima (ili radnima stanicama ili računalnim serverima), mogu pružiti visoke performanse, sumjerljive onima u stolnih računala od prije nekoliko godina, a pritom imaju i do 3 reda veličine manju potrošnju. Tako omogućuju naprednu funkcionalnost na uređajima kao što su pametni telefoni ili tablet-računala.

2.2.9 ASIC

ASIC je akronim za Application-Specific Integrated Circuit (integrirano sklopovlje specifične namjene). Ovakvi sklopovi dizajnirani su do ostvaruju neku specifičnu funkcionalnost. Po ovoj karakteristici slični su programirljivim logičkim uređajima, ali za razliku od njih njihova funkcionalnost biva definirana već tijekom proizvodnje. Ovakvi uređaji u pravilu nisu programirljivi, ali se u kontekstu paralelizma spominju zbog toga što mogu biti dizajnirani (ovisno o namjeni) tako da obavljaju velik broj operacija paralelno, a sama specifičnost njihove funkcionalnosti omogućava optimizacije u dizajnu koje dopuštaju velike brzine rada. Navedeni faktori imaju za posljedicu vrlo visoku sposobnost procesiranja podataka kod AISC komponenti, naravno, samo u određenoj, uskoj domeni.

2.3 Paralelizacija algoritama i programiranje paralelnih sustava

2.3.1 Paralelni algoritmi

Rješavanje nekog problema na paralelnom sustavu može donijeti velike vremenske (i energetske) uštede, ali sama implementacija metode rješavanja nije uvijek trivijalna. Iako postoje algoritmi koji se bez problema mogu prebaciti iz slijedne u paralelnu izvedbu, u većini slučajeva potrebno je uložiti dosta truda u funkcionalnu i podatkovnu dekompoziciju slijednog algoritma kako bi paralelni sustav bio uspješno iskorišten. Često je potrebno koristiti sasvim drugačiji pristup rješavanju problema kada se koristi paralelni algoritam, a ponekad, paralelizacija uopće nije moguća. Ako više istodobnih procesa koristi isti resurs potrebno je paziti na podatkovnu ovisnost i pobrinuti se da ne dođe do potpunog zastoja (eng. deadlock) – situacije u kojoj dva ili više procesa međusobno čekaju jedan drugog da završe.

Proces pisanja paralelnih aplikacija u pravilu se sastoji od četiri osnovna koraka. Prvi korak bila bi podjela problema (eng. partitioning) na dijelove koji se mogu obavljati paralelno, tj. isticanje dijelova algoritma koji se mogu paralelizirat (najčešće je riječ o podatkovnom paralelizmu ili paralelizmu na razini zadataka). Slijedi predikcija potreba za međuprocesorskom komunikacijom (eng. communication). Ova faza je zanemariva kod trivijalno paralelnih algoritama. Treći korak (aglomeracija – eng. agglomeration) je aglomeracija gdje programeri uzimaju u obzir konkretno ustrojstvo paralelnog sustava i donose odluke o mogućnosti smanjivanja zrnatosti problema i potrebama za uvišestručavanjem podataka i/ili zadataka. Na posljetku se obavlja preslikavanje (eng. mapping) do sada apstraktnog paralelnog modela na konkretni paralelni sustav, vodeći pritom obzira, kako o specifičnostima sklopovske građe, tako i o dostupnoj programskoj podršci (paralelni programski jezici, operacijski sustav).

Prilikom osmišljavanja paralelnih algoritama mora se paziti na četiri bitna svojstva svako paralelnog algoritma koja bitno utječu na njihove performanse:

Page 23: optimizacija rojem čestica na paralelnim arhitekturama

• skalabilnost (eng. scalability) – mogućnost skaliranja problematike na proizvoljni broj paralelnih procesora

• istodobnost (eng. concurrency) – sposobnost izvršavanja više zadataka istovremeno

• lokalnost (eng. locality) – odnos pristupa lokalnoj memoriji pridruženoj nekom paralelnom procesoru, u odnosu na pristup globalnoj memoriji (ili eventualno memoriji pridruženoj nekom drugom paralelnom procesoru)

• modularnost (eng. modularity) – mogućnost (ponovne) uporabe dijelova algoritma unutar različitih paralelnih programa

Osim trivijalno paralelnih algoritama, svaki se paralelni zadatak na najnižoj se razini obavlja u ciklusima proračuna i međuprocesorske komunikacije koji se međusobno izmjenjuju jedan ili više puta. Na malo višoj razini apstrakcije paralelna aplikacija može imati više osnovnih vrsta strukture koji je moguće međusobno kombinirat na različita načine.

Jedna od najjednostavnijih takvih struktura je višestruko ponavljanje ciklusa računanja i komunikacije u fazama (eng. phase parallel model). Faza komunikacije u ovom slučaju služi i kao sinkronizacijska barijera. Ovakav pristup, prikazuje Slika 2.6, je jednostavan, a nedostatak mu predstavlja inhibicija konkurentnosti. Ovakva vrsta paralelizma u aplikaciji često je inherentno nametnuta samim karakteristikama sklopovlja, kao što je slučaj kod grafičkih procesora.

Slika 2.6 Izmjenjivanje ciklusa računanja i sinkronizacije

Pristup “podijeli pa vladaj“ koncipirana je kao stablo gdje zadatak roditelj (korijen) ima jedno ili više djece (listova). Tako se proračuni dijele na sve manje i manje dijelove dok se ne postigne odgovarajuća razina zrnatosti. Potom se rezultati spajaju u obrnutom redoslijedu, odnosno vrše se redukcija. Ovaj model lagano je

Page 24: optimizacija rojem čestica na paralelnim arhitekturama

primjenjiv i na podatkovni paralelizam (Slika 2.7 gore) i na paralelizam na razini zadataka (Slika 2.7 dolje)

Slika 2.7 Dijeljenje zadatka na razini podataka (gore) i procesa (dolje)

Cjevovodni pristup kakav je opisan u uvodnim razmatranjima 2. poglavlja kao sklopovski način paralelizacije može se koristiti i na razini aplikacije (Slika 2.8). Unix porodica operacijskih sustava obilato se koristi povezivanjem aplikacija cjevovodima. U ovakvom načinu paralelizacije postoji kontinuirani tok podataka koji se šalje u cjevovod. Različite faze obrade podataka obrađuju se na različitim mjestima u cjevovodu. Nakon što pojedini dio cjevovoda obavi proračune nad podatcima prosljeđuje ih sljedećem djelu cjevovoda, a od svog prethodnika prima ove podatke. Za složene cjevovode, više podataka istovremeno se nalazi u cjevovodu, u različitim fazama obrade i odavde proizlazi paralelizam. Ovakav pristup najlakše se preslikava na sustave gdje se paralelizam ostvaruje na razini zadataka.

Page 25: optimizacija rojem čestica na paralelnim arhitekturama

Slika 2.8 Podatkovni cjevovod

Farma procesa (eng. process farm) naziva se i master-slave paradigma (eng. master – gospodar; slave – rob), a oblikovana je tako da glavni dio programa (master) izvodi slijedni dio programa, i prema potrebi stvara niz radnika (salve) koji izvršavaju dijelove programskog koda koje je moguće paralelizirati (Slika 2.9). Kada pojedini radnik završi sa svojim dijelom proračunima obavještava se glavni program koji tada izvršava neki slijedni dio programskog koda, čeka rezultate drugih radnika ili zadaje novi zadatak besposlenom procesu-radniku. Sva sinkronizacija obavlja se u glavnom procesu.

Slika 2.9 Farma procesa

Skup zadataka (eng. pool of tasks) sadrži više globalni skup zadataka koji je ostvaren kao podatkovna struktura u glavnoj memoriji. Inicijalno može postojati samo jedan zadatak u skupu. Postoji određeni broj elemenata (procesa) koji (neovisno) obavljaju zadatke (izračune). Kada pojedini proces završi sa zadatkom dohvaća novi zadatak iz skupa zadaka koji je zajednički svim procesima (Slika 2.10). Rezultat izvršavanja zadatak može biti neka vrijednost ili skup vrijednost. Uz to moguće je da izvršavanje zadatka rezultira jednim ili više novih zadataka koji se ponovo stavljaju u zajednički skup. Kada je skup zadatka ispražnjen algoritam završava. Ovakav model ima ugrađenu raspodjelu opterećenja među dostupni procesorskim resursima.

Page 26: optimizacija rojem čestica na paralelnim arhitekturama

Slika 2.10 Skup zadataka

U praktičnoj primjeni potpuna distinkcija između navedenih pristupa često nije moguća

2.3.2 Programiranje paralelnih sustava

Programiranje paralelnih sustava dodatno je otežano zbog potrebe za podjelom posla i sinkronizacijom. Sam pristup programiranju takvih sustava odražava pretpostavke o arhitekturi i karakteristikama paralelnog sklopovlja. Postoji više klasa modela paralelno programiranja, svaka sa svojim posebnostima.

Model sudionika (eng. actor model) kreće od pretpostavke “sve je sudionik“ (slično pretpostavci “sve je objekt“ prisutne u objektno orijentiranoj paradigmi). Riječ je o modelu koji se temelji na slanju poruka i konkurentnom pristupu. Sudionik prima poruke na temelju čijeg sadržaja može poslati konačan broj poruka, stvoriti konačan broj sudionika ili odrediti ponašanje prilikom sljedeće poruke koju primi (simulacija automata) – ne nužno u navedenom redoslijedu. Svaki sudionik ima svoju jedinstvenu adresu i može slati poruke samo drugim sudionicima čije adrese posjeduje (bilo da ih je dobio iz poruka koje je primio ili zato što je sam stvorio dotične sudionike). Nekoliko primjera takvih sustava su Erlang, Janus, SALSA i Scala.

Koordinacijski jezici kao što je Linda, dodaju funkcionalnost za obavljanje operacija nad n-torkama (eng. tuple) na neki klasični, sekvencijalni programski jezik (npr. C). Na logičkoj razini postoji globalna memorija u koju su n-torke pohranjene. Ovakvi jezici postižu relativnu odvojenost između koordinacije među procesima i same paralelne obrade podataka. Svi procesi komuniciraju isključivo preko n-torki. Ovakvi jezici lagano se primjenjuju na više tipova sklopovskih arhitektura (npr. jednojezgreni procesori, višejezgreni procesori, više umreženih računala) što im omogućuje veliku fleksibilnost i visok stupanj prenosivosti bez ikakvih potreba za izmjenama programskog koda.

CSP (eng. Communicating Sequential Processes – Komunicirajući slijedni procesi) – je formalni jezik za opisivanje uzoraka interakcije u sustavima s konkurentnim procesima. Dio je teorije algebre procesa. Jezik opisuje sustav koji se sastoji od nezavisnih procesa čije se međusobna interakcija ostvaruje slanjem poruka. Primjeri implementacije ovog formalnog jezika su Aleph, Concurrent Pascal, FortanM i Occam.

Programiranje opisom toka podataka (eng. Dataflow programming) je pristup programiranju u kojem se opisuje kako podatci prolaze kroz sustav od točke do točke. U pojedine točke predstavljaju svojevrsne crne kutije koje mijenjaju stanje podataka. Paralelizam se jednostavno može opisati tako što se tok podatka u

Page 27: optimizacija rojem čestica na paralelnim arhitekturama

pojedinim točkama grana ili se pojedine grane stapaju. Primjer ovakvih jezika su Lustre SISAL i Signal.

Programiranje temeljeno na događajima (eng Event-driven programming ili Event-based programming) predstavlja koncept sličan programiranju opisom toka podataka. Završetak svake radnje ili operacije predstavlja događaj. Zavisnost među operacijama izražava se tako da operacija mora čekati na odgovarajuće događaje. Jedan događaj može potaknut više novih operacije koje se mogu odvijati i paralelno, konkurentno i neovisno jedna od druge. Primjeri takvih jezika su jezici za opis sklopovlja (eng Hardware Description Language – HDL) kao što su VHDL ili Verilog, ali i mnogi drugi jezici imaju ovakvu vrstu sinkronizacije implementiranu (primjerice OpenCL programsko okruženje).

Funkcijsko programiranje je programska paradigma koja tretira računanje kao evaluaciju matematičkih funkcija. To je vrsta deklarativnog programiranja gdje se izražava što se treba postići, ali ne i kako to učiniti. Za razliku od možda i najrasprostranjenije paradigme imperativnog programiranja gdje je temelj izdavanje naredbi na osnovi kojih se mijenja stanje, funkcijsko programiranje izbjegava korištenje stanja i izmjenu stanja objekata (side effect free). Zato se mnogi elementi funkcijskog programiranja mogu izvršavati paralelno, neovisno jedni o drugima. Neki primjeri funkcijski temeljenih jezika bili bi Concurrent Haskel, ld i Erlang (također spada u jezike temeljene na modelu sudionika).

Jezici za logičko programiranje poput Paraloga sposobni su izraziti paralelne elemente kao što su komunikacija, sinkronizacija i konkurentnost procesa pomoću deklarativnih jezika koji koriste predikatnu logiku prvog stupnja (eng. Firs-Order Predicate Logic – FOPL).

PGAS (eng. Partitioned Global Address Space – Razdijeljeni Globalni Adresni Prostor) programski model koristi se zajedničkim memorijskim prostorom koje je podijeljen na dijelove koji imaju afinitet prema određenom procesoru u sustavu. Ovime je omogućeno bolje iskorištavanje vremenske i prostorne lokalnosti podataka. Primjer jezika temeljenih na PGAS modelu su Chapel, Co-array Fortran, Unified Parallel C, X10 i ZPL.

Grafički procesori zbog svoje specifične arhitekture koriste posebne vrste programskih jezika kao što u CUDA, OpenCL C ili OpenHMPP koji omogućavaju iskorištavanje takvog procesorskog ustrojstva. Tako zvani GPGPU programski jezici biti će detaljnije opisani u ostatku ovog diplomskog rada, prvenstveno u poglavljima 3.1 i 4.

Mnoge od navedenih vrsta pristupa paralelnom programiranju ne nisu striktno definirani tako da se neki programski jezici mogu klasificirati u nekoliko navedenih skupina.

Osim navedenih pristupa paralelnom programiranju, postoje i mnoge druge vrste programskih modela. Postoje programski jezici u čijoj osnovi leži jaka podrška za višedretveno programiranje. Primjer takvog jezika je Click koji se temelji na tome da programer identificira elemente koji se mogu izvršavati paralelno, a potom se automatski, tijekom izvršavanja obavlja podjela pola na dostupne procesorske resurse. Mnogi objektno orijentirani jezici postoje u “paralelnoj“ inačici prilagođenoj određenim arhitekturama (primjerice C++ AMP je verzija C++ programskog jezika bazirana na DirectX 11 programskom sučelju i namijenjena programiranju

Page 28: optimizacija rojem čestica na paralelnim arhitekturama

grafičkog sklopovlja). Neki programski jezici kao što je NESL, imaju ugrađene mehanizme za procjenu učinkovitosti napisanog paralelnog algoritma.

2.3.3 Programska podrška za paralelne sustave

Programska podrška za paralelne sustave može biti ostvarena na nekoliko načina. Razna programska okruženja (eng. framework) omogućuju brži razvoj, a biblioteke često korištenih funkcija ponovno iskorištavanje već razvijenih paralelnih algoritam. Nerijetko uz neki paralelni programski jezik ili platformu dolaze i paralelni algoritmi koji omogućavaju neke osnovnu funkcionalnost kao toe su redukcije, prosljeđivanje nekog podatka svim ostalim paralelnim procesima (eng. broadcast), ili čak neke složenije funkcionalnosti kao što je podrška za linearnu algebru. Ako se opisana proširenja i ne isporučuju od strane originalno proizvođača, često će se na tržištu vrlo brzo pojaviti komercijalne, ili čak besplatne (Open Source) skupine biblioteka s funkcijama koje se smatraju uobičajenom u paralelnom računarstvu ili nekom području koje se paralelnim računarstvom često koristi.

Cijeli operacijski sustavi mogu biti posebno prilagođeni da omogućuju lakše iskorištavanje paralelizma prisutnog u sklopovlju na kojem se izvršavaju. Primjer takvih operacijski sustava su takozvani SSI (eng. Single-System Image) koa što su OpenSSI i MOSIX (oba temeljena na Linux operacijskom sustavu). Ovakvi sustavi primarno su namijenjeni grozdovima (eng. cluster – manji skup više brzih računala povezanih računalnom mrežom visoke podatkovne propusnosti) i spletovima (eng. grid – veći broj računala, nerijetko smještenih na više lokacija, povezanih računalnom mrežom nešto niže podatkovne propusnosti) računala. SSI operacijski sustavi prikazat će korisniku sve čvorove (računala u grozdu ili spletu računala) u paralelnom sustavu kao jedinstveni sustav. Svi resursi programeru će biti vidljivi kao da su ugrađeni u isto računalo, dok će sam sustav u pozadini obavljati migraciju procesa, brinuti o raspodjeli opterećenja pojedinih čvorova, automatski detektirati novododane resurse u sustavu i raditi s udaljenim datotečnim sustavima kako bi stvorio privid jedinstvenog datotečnog sustava. Ovako modificirani operacijski sustavi često dolaze i naprednim alatima za upravljanje resursima, zadatcima, sklopovljem i instalacijom pojedinih programskih i sklopovskih komponenti. Kako se u sklopu ovog rada ekstenzivno oslanja na OpenCL norma, važno bi bilo istaknuti VCL (eng. Virtual OpenCL) dodatak operacijskim sustavima koji su napravili proizvođači MOSIX-a. VCL omogućuje da svi OpenCL-kompatibilni procesori budu vidljivi kao da su ugrađeni u jedno računalo. Opis operacijskih sustava prilagođenih paralelno računarstvu dan u ovom odlomku stavlja fokus prvenstveno na operacijske sustave namijenjene za HPC (eng. High-performance computing – računarstvo visokih performansi), ali postoji niz operacijskih sustava namijenjenih instalaciji na mreže računala, koji su prilagođeni postizanju visoke pouzdanosti i redundancije.

float numbers = [0.9, 0.7, 0.5, 0.3, 0.1]

float results = log(numbers)

Pseudokod 2.1 Implicitni paralelizam

Još jedan vid programske podrške paralelnom programiranju mogli bi se smatrati sustavi za automatsku paralelizaciju. U takvim slučajevima, sam sustav mora donijeti odluke kako će se prilikom prevođenja ili izvođenja algoritam paralelizirati.

Page 29: optimizacija rojem čestica na paralelnim arhitekturama

Najčešće je riječ o paralelnom izvođenju iteracija neke programske petlje. U nekim programskim jezicima, paralelizam nije eksplicitno izražen, ali postoji implicitno. To se najčešće odnosi na obrađivanje nizova podataka. Pseudokod 2.1 prikazuje situaciju u kojoj se podrazumijeva da, ako se neka operacija zada na nizom brojeva (konkretno operacija logaritmiranja nad varijablom nazvanom numbers), ta operacija može izvršavati istovremeno nad svim elementima niza, odnosno postoji mogućnost da prevoditelj paralelizira taj dio programskog koda. Problem kod ovakvog pristupa je u tome što prevoditelji nisu sposobni analizirati programski kod na razini na kojoj to može obavljati iskusni programer (posljedica Bernstienovog teorema koji kaže da je teško ocijeniti mogu li dvije operacije u sekvencijalnom slijednom programu biti izvedene paralelno) i zbog toga ne mogu paralelizirati kod u svim osim najjednostavnijih situacija. Zato kod takvih sustava često postoji mogućnost da programer natukne prevoditelju koji bi se dijelova programskog koda mogli izvršavati paralelno. Ovakvi sustavi omogućuju programeru da se koncentrira na problem, a ne na proces paralelizacije i karakteristike paralelnog sustava što omogućava i viši stupanj prenosivosti programskog koda. S druge strane uočavanje i uklanjanje grešaka je otežano (otklanjanje grešaka je općenito teško u paralelnim sustavima), a programer nema nikakvu kontrolu nad samim procesom paralelizacije. Većina sustava za automatsku paralelizaciju nije nikad u potpunosti zaživjela ili postigla veći uspjeh.

Page 30: optimizacija rojem čestica na paralelnim arhitekturama

3. Prethodna istraživanja

Radi boljeg uvida u trenutni stupanj razvoja vezan uz masovni paralelizam u GPGPU koncept, u ovom poglavlju, kao svojevrsni kontekst, prikazan je rad vezan uz nešto stariju generaciju grafičkih procesora i programsku podršku prethodne generacije namijenjenu programiranje istih. Sklopovlje korišteno tijekom ovog testiranja opisano je u poglavlju 5.4. U nastavku ovog poglavlja govori isključivo o sklopovlju starijih generacija (približno razdoblje između 2006. i 2009. godine)

3.1 RapidMind platforma

3.1.1 Općenito

RapidMind je platforma za programiranje stream procesora i razvoj masivno paralelnih aplikacija u C/C++ programskom jeziku. Nastalo je komercijalizacijom istraživanja sličnog razvojnog okruženja Sh, koje je bilo razvijano na kanadskom sveučilištu University of Waterloo. Deklarirana ubrzanja aplikacija razvijenih u RapidMind-u odnosu na klasične aplikacije koje se izvode na glavnom procesoru iznosi 3x do 30x. U 2009. godini proizvođača RapidMind platforme, RapidMind Inc. kupuje Intel i nakon godine dana obustavlja prodaju svih RapidMind proizvoda, i iskorištava razvijene tehnologije u vlastitim proizvodima kao što su Intel Array Building Blocks (ArBB).

3.1.2 Način funkcioniranja

RapidMind okruženje dizajnirano je tako da skriva od korisnika specifičnosti sklopovlja i omogućava programiranje na visokoj razini, predstavljajući elemente potrebne za stream paralelizaciju kroz biblioteke (libraries) C++ programskog jezika. Jezgra se prevodi tijekom izvođenja C/C++ programskog koda (“runtime compiling“) i optimizira za stream procesor koji je najpogodniji za zadanu jezgru i količinu podataka (u slučaju da je više stream procesora dostupno više istovremeno). Prilikom prvog poziva određene jezgre potrebno čekati određeno vrijeme upravo radi prevođenja jezgre. Same stream naredbe RapidMind platforme zapravo su iz C++ programskog jezika vidljive kao objekti smješteni u biblioteke. Elementi se za vrijeme izvođenja sakupljaju i prevode.

Korištena verzija RapidMind platforme, deklarativno podržava grafičke procesore s modelom sjenčanja 3.0 ili novijim (ATI x1X00 ili noviji i NVIDIA GeForce® 6000 odnosno Quadro FX5500 ili noviji), Cell BE procesori (i simulatori istih) i višejezgreni x86 kompatibilni procesori u kojima se zapravo prvenstveno koriste “multimedijalne“ ekstenzije osnovne x86 arhitekture (poput SSE4) koje su u prilagođene radu sa (četverodimenzionalnim) vektorima. Grafičkim procesorima, RapidMind pristupa putem OpenGL programskog sučelja.

3.1.3 Elementi RapidMind-a

3.1.3.1 Vektori

Vektori su osnovni tipovi podataka u RapidMind okruženju. Oni predstavljaju ulaze i izlaze u instance jezgre. Teoretski, moguća je proizvoljna dimenzionalnost, ali kada su u pitanju grafički procesori, ona je ograničena na najviše četiri dimenzije.

Page 31: optimizacija rojem čestica na paralelnim arhitekturama

Vektori mogu biti sačinjeni od uobičajenih tipova koji se nalaze i u C programskom jeziku (bool, (unsigned) char/short/int/long, float i double), a također su podržani brojevi s pomičnim zarezom u polovičnoj preciznosti (half) i fraction tip koji se primarno koristi za normalizirane koordinate a zapravo prikazuje brojeve iz intervala [0, 1] (unsigned) odnosno [-1, 1] (signed). Na grafičkom procesoru dva osnovna tipa su float i fraction, dok su ostali tipovi interno svedeni na njih. Primjerice, svi cjelobrojni tipovi zapravo su predstavljeni tipovima uz pomoću float tipa i niza dodatnih instrukcija koje RapidMind ubacuje prilikom prevođenja kako bi se postiglo ponašanje sukladno s cjelobrojnim tipom. Unatoč ovim dodatnim instrukcijama, često se dobiva neočekivano ponašanje i potrebno je neprekidno imati na umu da se radi o brojevima s pomičnim zarezom. Tako se primjerice dijeljenjem broja jedan brojem dva dobiva rezultat nula, ali interno GPU pamti taj broj kao jednu polovinu. Zato se u takvoj situaciji prilikom usporedbe dobivenog rezultata s nulom može dobiti rezultat false odnosno nejednakost s nulom (ovaj konkretni problem, lagano se rješava funkcijom za zaokruživanje na niže). Ovakva reprezentacija cjelobrojnih tipova, također bitno utječe na preciznost operacija nad njima, kao i maksimalni raspon vrijednosti cjelobrojnog tipa. Sam float tip, nije na dostupnom sklopovlju bio ekvivalentan klasičnom C/C++ float tipu u pogledu preciznosti, što se često vidjelo i na diskrepanciji između rezultata vraćenih od strane RapidMind-a odnosno grafičkog procesora u odnosu na rezultate dobivene proračunima na glavnom proesoru. Također, brojevi s pomičnim zarezom u dvostrukoj preciznosti (double-precision float) nisu interno podržan na dostupnom grafičkom procesoru i biti će pretvarani u broj jednostruke preciznosti (single-precision float tip) prilikom prosljeđivanja podataka memoriji grafičkog procesora. Ova činjenica će imati negativne posljedice na brzinu izvođenja. Tip half biti će automatski proširen na najveću dostupnu preciznost (float) bez ikakvog brzinskog gubitka. Valja napomenuti da su na modernijim grafičkim procesorima, kakvi nisu bili dostupni tijekom korištenja RapidMind platforme, uobičajeni tipovi podataka kakvima se barata na glavnom procesoru normalno podržani. Same komponente vektora moguće je proizvoljno ekstrahirati, permutirati duplicirati (eng. swizzling i writemasking).

Unutar jezgre, nad vrijednostima moguće je pozvati različite funkcije integrirane u RapidMind platformu, koje su često relativno slične svojim C/C++ pandanima. Razlika je što se funkcije izvode nad svim elementima vektora istovremeno. Neke funkcije provode redukciju nad komponentama vektora i vračaju jednu vrijednost kao rezultat obrade n komponenti vektora, a postoje i funkcije koje mogu primati više vektora različitih dimenzionalnosti (npr. množenje elemenata četverodimenzionalnog vektora s konstantom, odnosno jednodimenzionalnim vektorom). Broj podržanih funkcija je relativno velik a osim relativno uobičajenih funkcija kao što su aritmetičko-logičke (operatori +, -, / , *, %, ++, --, ==, !, !=, >, >=, >, <<, &&, ||…) i transcendentalne (logaritmiranje, potenciranje, trigonometrijske i hiperbolne funkcije, itd.), unutar RapidMind platforme postoji niz specifičnih funkcija vezanih uz linearnu algebru (normalizacija vektora, duljina vektora, skalarni produkt, kut među vektorima, udaljenosti među točkama u različitim metrikama…), računalnu grafiku (interpolacija određivanje kuta refrakcije, refleksije i totalne refleksije) i neke specifične za vektore kao elemente RapidMind jezgri (multiply-add, redukcije nad vektorima…).

Page 32: optimizacija rojem čestica na paralelnim arhitekturama

3.1.3.2 Nizovi i pogledi

Nizovi (Array) su skupine više vektora. Mogu biti jednodimenzionalni ili dvodimenzionalni, a na nekom sklopovlju (nVidia) podržani su i trodimenzionalni nizovi. Njihova je svrha pohranjivanje većeg broja vektora i njihovo isporučivanje odgovarajućem stream procesoru, odnosno na programskoj razini odgovarajućem RapidMind programu. Nizove se ne može smatrati klasičnim alociranim dijelovima memorije već C++ objektima, koji se brinu o transferu podataka između glavne memorije i memorije stream procesora, odgovarajućim tipovima podataka i njihovoj konverziji, o graničnim uvjetima i mnogim drugim detaljima. U tom aspektu, sličniji su kolekcijama iz STL-a (eng. Standard Template Library) i kao takvi, posjeduju čak i svoje vlastite iteratore. Osim pomoću iteratora, elementima niza moguće je pristupiti i preko indeksa (klasičnih i normiranih – jednadžba (3.1)) O alokaciji i dealokaciji memorije potrebnoj za smještanje vektora brine se sama platforma. Postoji i mogućnost pretvaranja dijela memorije alociranog izvan RapidMind platforme u RapidMind niz, pri čemu je moguće da platforma preuzme brigu o dealokaciji memorije ili da se o memoriji i dalje brine korisnik. Posljednja opcija je pogodna u situacijama u kojima je potrebno dodatno obrađivati podatke glavnim procesorom, nakon što ih obradi neki stream procesor.

( ) [ ] niza elemenata broj;5,0_*_ −−= nindexfracnArrayindexfracArray (3.1)

Osim “običnih“ nizova, nazvanih Array unutar RapidMind nomenklature, postoje i ArrayAccessor objekti. Ova vrsta nizova mogla bi se nazvati pogledom na niz (Array). Oni se definiraju nad Array objektom ili nekim drugim pogledom i koriste se kako bi se pristupilo samo dijelu podataka u njemu. Ovakvi pogledi također ne moraju nužno slijedno pristupati objektima iz niza o čemu će više riječi biti u idućim odlomcima. Nad istim nizom može biti konstruirano proizvoljno mnogo pogleda, koji će bit vezani uz dotični niz i njihov sadržaj će se mijenjati sukladno promjenama u nizu koji je pod njima. Kako pogledi ne sadrže vlastite podatke odnosno vektore, ne zahtijevaju niti memorijski prostor za njih. Pod uvjetom da pogled ne prelazi granice niza, pridruživanje podataka pogledu upisivat će podatke u dio niza nad kojim je pogled konstruiran. Treći tip nizova su ArrayRef objekti – reference na RapidMind nizove koje je moguće usporediti s pokazivačima na nizove ili poglede.

Page 33: optimizacija rojem čestica na paralelnim arhitekturama

Slika 3.1 Različiti načina ponašanja nizova izvan definiranih granica

Važna razlika između RapidMind nizova i nizova u klasičnim programskim jezicima (npr. C/C++) ili kolekcija u STL-u jesu granični uvjeti i načini pristupa elementima. Prvi pojam odnosi se na ponašanje tijekom pristupanja elementima s indeksom koji je izvan opsega, odnosno veći od broja elemenata u nizu umanjenog za jedan (indeksi počinju od nule). Moguće je definirati kako će se niz ponašati ako se pokuša pristupiti elementu izvan opsega i to ponašanje neće nužno uzrokovati pogrešku (Slika 3.1). Ovi različiti načini ponašanja prilikom adresiranja elemenata izvan definiranih granica niza originalno su namijenjeni radu s teksturama kod kojih je često moguće definirati svojstva za mali dio teksture i potom ih kopirati štedeći tako memorijski prostor i količinu podataka koje je potrebno prebaciti. Načini ponašanja prilikom adresiranja elemenata izvan definiranih granica niza su:

• SAFE_UNDEFINED – vraća vrijednost, ali nije definirano koju

• CHECK_UNDEFINED – nedefinirano ponašanje koje će uzrokovati iznimku ako je to moguće

• UNDEFINED – nedefinirano ponašanje koje će uzrokovati rušenje aplikacije

• REPEAT –vrijednosti u niz se periodički ponavljaju i izvan definiranih granica niza

• CLAMP – posljednji element s granice se ponavlja

Page 34: optimizacija rojem čestica na paralelnim arhitekturama

• CONSTANT – prostor izvan opsega popunjava se određenom konstantom (eksperimentalno je ustanovljeno da ovaj način rada ne funkcionira, odnosno da nije moguće postaviti vrijednost konstante)

Kao što je spomenuto, pomoću pogleda (ArrayAccessor), moguće je na različite, neuobičajene načine pristupati elementima. Primjerice, moguće je konstruirati pogled na samo određeni dio niza, ili čak periodički preskakati elemente. Ovakva funkcionalnost bila je inicijalno namijenjena lakšem uzorkovanju tekstura i njihovih dijelova. RapidMind razvojno okruženje posjeduje ugrađenu podršku za pet različitih načina pristupa (Slika 3.2), od kojih su posljednja dva navedena kombinacija prethodnih:

• take – definira koliko će elemenata biti obuhvaćeno počevši od nultoga

• shift – definira se pogled jednakih dimenzija kao i originalni niz ili pogled, samo odmaknut od početka niza za određeni broj elemenata

• stride – definira koliko će elemenata biti preskočeno prije uzorkovanja novog elemenata iz niza ili pogleda

• offset – definira se odmak od elemenata s nultim indeksom kao i kod shift, ali ne prelazi izvan granica niza, odnosno pogleda nad kojim se gradi

• slice – definira početne i završne elemente koji će biti uzeti u obzir.

TAKE (2, 3) SHIFT (2, 1) STRIDE (2, 1)

OFFSET (1, 2) SLICE (1, 2, 5, 3)

Slika 3.2 Vrste pristupa elementima niza

Page 35: optimizacija rojem čestica na paralelnim arhitekturama

Opisana funkcionalnost koju RapidMind nudi u vidu načina pristupa i graničnih uvjeta, na nekom sklopovlju ima poteškoća prilikom izvođenja ili se ne izvodi ispravno. Ovo se pogotovo odnosi na granične uvjete.

U stream aplikacijama pogonjenim RapidMind-om koristi se još jedna vrsta nizova – virtualni nizovi. Riječ je o nizovima koji ne postoje fizički u memoriji, već se vrijednost njihovih elemenata izračunava dinamički, unutar pojedine instance jezgre. Virtualni niz koji će se često koristiti tijekom izrade programskog dijela ovog seminara naziva se grid. Njegovi elementi imaju vrijednost jednaku svojem rednom broju odnosno indeksu u nizu. Kako su podržani i višedimenzionalni nizovi, moguće je da će elementi biti višedimenzionalni vektori.

3.1.3.3 Programi

U RapidMind terminologiji, Programi su naziv za jezgre. Omeđeni su makro naredbama za početak i kraj programa koji označavaju C/C++ prevoditelju da blok naredbi i objekta koji su iz RapidMind domene sakuplja radi prosljeđivanja RapidMind platformi na prevođenje tijekom izvođenja glavnog programa. Upravo radi ovakvog načina sakupljanja naredbi i prevođenja za vrijeme izvođenja moguće je dinamički generirati jezgre: tijekom izvođenja C/C++ programskog koda, kada se dođe do dijela gdje se definira RapidMind program, počinju se sakupljati RapidMind naredbe i od njih slagat programski kod koji će biti prosljeđivan RapidMind prevoditelju. Svi klasični elementi C/C++ programskog jezika u tom trenutku će već imati definirane vrijednosti. Primjerice, C/C++ varijable mogu se koristiti kao vrijednosti za prosljeđivanje operatoru za permutiranje elemenata vektora unutar RapidMind programa. Ovisno o vrijednosti varijabli u trenutku sakupljanja RapidMind naredbi, u jezgri se mogu izvoditi različite permutacije. Proširenjem ovog koncepta na C/C++ naredbe za kontrolu toka, moguće je klasičnim grananjima, ovisnima o nekoj C/C++ varijabli regulirati koji će nizovi RapidMind naredbi ući u sastav jezgre odnosno RapidMind programa. Običnim C/C++ petljama neku RapidMind naredbu ili niz naredbi može se proizvoljno mnogo puta replicirati.

Programi primaju podatke preko ulaza i vračaju ih putem izlaza. U RapidMind razvojnom okruženju ulazi i izlazi deklarirani unutar programa su vektori. Kako je riječ o masivno paralelnim aplikacijama prilikom pozivanja RapidMind programa, kao ulazni i izlazni parametri koriste se nizovi ili pogledi na iste, a potom će svaka instanca programa odnosno jezgre dobiti na raspolaganje po jedan element, odnosno vektor iz svakog ulaznog ili izlaznog niza ili pogleda (Slika 2.2). Vidljivo je da broj elemenata u svim ulaznim i izlaznim nizovima mora biti jednak za neki poziv programa, ali, naravno, dva različita poziva programa mogu obrađivati nizove različitih veličina ili čak različite dimenzionalnosti. Slika 2.2 prikazuje ovakav pristup. Kako su ulazi u program vektori RapidMind platforma mogla bi se klasificirati kao koordinacijski programski jezik (poglavlje 2.3.2). Prilikom prvog pozivanja programa nad nekim određenim podacima, oni će biti prebačeni u memoriju stream procesora, ali za daljnjih pozivanja neće biti potrebe za novim prebacivanjem i program će se izvršavati brže, osim ako u međuvremenu podaci nisu mijenjani od strane glavnog programa. Osim putem deklariranih ulaza i izlaza, RapidMind razvojno okruženje podržava još dva načina unosa podataka u jezgru. Prvi je čitanje s nasumičnim pristupom koje omogućava dohvaćanje vrijednosti iz nizova pohranjenih u memoriji na temelju njihova indeksa. Ova mogućnost se na grafičkim karticama najčešće koristi za dohvaćanje vrijednosti određene teksture u

Page 36: optimizacija rojem čestica na paralelnim arhitekturama

točci koja se trenutno iscrtava. Drugi način unosa podataka u jezgru predstavljaju varijable tipa vektor koje su deklarirane izvan same jezgre, ali se u jezgri referenciraju (programski parametri). Ovakve varijable može se usporediti s globalnim varijablama u C programskom jeziku, s razlikom da se lokalno njihova vrijednost ne može mijenjati. Iako je njihova vrijednost, tijekom izvršavanja jezgre konstantna, između poziva vrijednost se može mijenjati. Sama vrijednost bilo koje vrste ulaznog vektora može se mijenjati unutar pojedine instance jezgre, ali te promjene vrijede lokalno, samo za tu instancu jezgre, upravo zbog činjnice da jezgre ne mogu mijenjati sadržaj memorije, osim putem izlaznih nizova. Općenito gledajući, različiti načini unosa podataka, različito utječu na brzinu izvođenja jezgre. Konstante koje se deklariraju unutar jezgre, funkcioniraju najbrže jer se ugrađuju u sam programski kod jezgre već prilikom prevođenja. Programski parametri daju tek nešto lošije rezultate. Njihova vrijednost obično se pohranjuje u posebnim registrima ili priručnoj memoriji i prilikom svake izmjene potrebno je, prije pozivanja jezgre, samo upisati dotične konstante. Sljedeći po brzini su virtualni nizovi, koji, kao i navedene dvije kategorije, ne zauzimaju memoriju, što predstavlja dodatnu prednost. Nakon virtualnih nizova, po brzinskim rezultatima slijede klasični nizovi koji se unose preko deklariranih ulaza, a na kraju su čitanja s nasumičnim pristupom.

Slika 3.3 Programska algebra

Osim manipulacije RapidMind programa umetanjem C/C++ programskog koda, na njih je moguće utjecati pomoću programske algebre. Ovaj pojam označava mogućnost spajanja različitih jezgri preko ulaza i izlaza paralelno ili serijski (Slika 3.3). Ovakav pristup spadao bi pod programiranje opisom toka podatka (poglavlje 2.3.2) Interno će RapidMind platforma prevesti više programa povezanih s programskom algebrom kao jedan program. Kako su programi u biti C++ objekti, na ovaj su način omogućene daljnje kombinacije i mogućnosti za dinamičko generiranje jezgre tijekom izvođenja. Programi također mogu pozivati druge programe kao funkcije. Rekurzivni pozivi nisu mogući. Ovakvo pozivanje RapidMind programa, također će interno umetnuti kod programa pozvanih u službi

Page 37: optimizacija rojem čestica na paralelnim arhitekturama

funkcije u kod programa iz kojeg su pozvani slično inline funkcijama u C/C++ programskom jeziku.

RapidMind programi podržavaju kontrolu toka, sličnu klasičnoj C/C++ kontroli toka. Podržana su programska grananja (if-then-else), petlje (for, while, do-until), te break, continue i return. Valja napomenuti da, iako je return deklarativno podržan, u praksi ova komanda ne funkcionira.

Slika 3.4 Princip rada redukcije na različitim razinma zrnatosti

Unutar RapidMind razvojnog okruženja implementirana je i mogućnost redukcije. Koncept redukcije u paralelnom računarstvu obično označava provođenje neke operacije između svih elemenata u nekom nizu, tako da se na kraju dobije samo jedan element. Primjer za operacije redukcije nad nekim nizom je zbrajanje svih elemenata dotičnog niza. Moguće je koristiti klasičan RapidMind program kao operator redukcije pod uvjetom da ima samo dva ulaza, samo jedan izlaz i da je predstavlja asocijativan operator, jer je redoslijed izvršavanja redukcije nad pojedinim elementima niza nedeterministički. Također postoje i ugrađene redukcije za sumiranje, produkt te za minimalni i maksimalni element u nizu. Potrebno je paziti na činjenicu da je osnovni tip u RapidMind razvojnom okruženju vektor i da se većina operacija, uključujući i redukcije, provode po komponentama tog vektora, tako da će redukcije, umjesto jedne skalarne vrijednosti, vračati vektor

Page 38: optimizacija rojem čestica na paralelnim arhitekturama

čija dimenzionalnost odgovara dimenzionalnosti vektora iz niza nad kojim se provodi redukcija. RapidMind platforma interno provodi redukciju primjenjujući uzastopce neki program nad nizom podataka (Slika 3.4): niz koji se reducira podijeli se na dva ili više dijelova pomoću pogleda koji onda služe kao ulaz programa za redukciju. Moguće je i direktno reduciranje izlaza iz neke jezgre pri čemu će platforma interno spojiti dijelove koda za redukciju s programskim kodomjezgre i time ubrzati cijeli proces. U razvojnom okruženju postoje i dvije asinkrone redukcije. Ovaj tip redukcija vraća se odmah nakon poziva iz glavnog programa, a izračune obavlja u pozadini, asinkrono. Podržane su redukcije koje rade asinkrono s logičkim tipom (bool) i vraćaju broj istinitih vrijednosti koje je vratio neki program (async_count()), odnosno je li vraćena ijedna istinita vrijednost (async_exists()).

Programi se mogu spajati s kodom za sjenčanje i OpenGL funkcionalnošću.

3.2 Računanje π Broj π je matematička konstanta koja u euklidskom prostoru opisuje omjer polumjera i opsega kruga (jednadžba (3.2)), ili alternativno odnos površine kruga prema kvadratu njegova polumjera (jednadžba (3.3)). To je iracionalan i transcendentalan broj.

R

O

r

O==

2π (3.2)

22

4

R

P

r

P==π

(3.3)

Postoji više načina izračuna vrijednosti π ili neke njegove decimale, ali za potrebe ovog rada većina njih (primjerice one koje su rekurzivne) neće bit razmatrane već će fokus biti stavljen na mnogobrojne metode gdje se vrijednost π računa kao suma reda. Odabrane su sljedeće metode: Gregory-Leibniz red (jednadžba (3.4)), Madhava red (jednadžba (3.5)) te Bailey–Borwein–Plouffe (BBP) formula u regularnom (jednadžba (3.6)) i polinomijalnom obliku (jednadžba (3.7)).

( )( )∑∞

=

−⋅+=

+

⋅−

⋅+

⋅−=

0

1

3231212....

37

1

35

1

33

1112

k

kkπ

(3.4)

( )∑∞

=

−+=+−+−=

0

1124...

7

4

5

4

3

4

1

4

k

(3.5)

∑∞

=

+−

+−

+−

+=

0

68

1

58

1

48

2

18

4

16

1

k

k kkkkπ

(3.6)

Page 39: optimizacija rojem čestica na paralelnim arhitekturama

( )konstanta Gelfondova;1

1

1

4

1

324

142

1 ππ eqqqq

kk

kkk=

−+

−−

−= ∑

=

− (3.7)

3.2.1 Implementacija

Kao što je već navedeno, osnovni princip izračuna pomoću odabranih metoda bio bi da jezgra izračunava paralelno elemente niza koji se potom sumiraju. Kako vrijednost elemenata ovisi samo o položaju u nizu, odnosno o rednom broju elementa u nizu, jedini ulaz u jezgru bio je redni broj elementa, odnosno virtualni niz tipa grid.

Zato što RapidMind platforma tretira elemente grid-a kao brojeve tipa integer, a zapravo ih grafički procesor interno tretira kao brojeve tipa float, dolazilo je do krive interpretacije pojedinih elemenata grida-a, i bilo je potrebno implementirati dodatne postupke zaokruživanja kako bi se ulaz u jezgru mogao pravilno koristiti u ostatku proračuna. Također je, vjerojatno zbog greške u upravljačkim programima (eng. driverima), bilo potrebno proširiti izlazne vektore na četiri dimenzije iako sama funkcionalnost jezgre ne bi bila narušena ni da je izlaz iz jezgre bila obična skalarna vrijednost.

3.2.2 Rezultati

Pokazalo se da je implementacija na grafičkom procesoru jedan do dva reda veličine brža od implementacije na glavnom procesoru (Slika 3.6). Ovakva premoć grafičkog procesora vjerojatno proizlazi iz činjenice da jezgre ne ovise o nikakvim ulaznim podacima nego se koristi samo jedan virtualni niz koji se interno generira unutar same jezgre.

Učinkovitost pri korištenju različitih metoda izračuna očekivano varira, ali te varijacije su manje kod grafičkog procesora. Ovo je vjerojatno posljedica nekog drugog uskog grla u procesu računanja, zbog kojeg razlike u računskoj zahtjevnosti različitih matematičkih formula ne dolaze do izražaja.

0

2

4

6

8

10

12

14

16

18

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21

Zrnatost

Vri

jem

e iz

vođen

ja/m

s

Gregory-Leibniz

Madhava

Bailey-Borwein-Plouffe (regular)

Bailey-Borwein-Plouffe (polinom.)

Slika 3.5 Brzina računanja π na grafičkom procesoru

Page 40: optimizacija rojem čestica na paralelnim arhitekturama

Smanjenje zrnatosti u početku donosi određena poboljšanja, ali kasnije gubi učinak ili čak ima i suprotan efekt: povećanjem količine proračuna po instanci jezgre zapravo se povećava vrijeme izvođenja. Moguće je da je ovo uzrokovano time što dimenzije ulaznog niza više nisu potencije broja dva što bi se pogotovo moglo negativno odraziti na učinkovitost redukcijske operacije sumiranja. Iz priloženog grafa (Slika 3.5) vidljivo je da se prvi slučaj smanjenja učinkovitosti zbog povećanja zrnatosti javlja upravo kada se šest elemenata izračunava u jednoj instanci jezgre. Iako takvo povećanje zrnatosti spušta broj instanci koje se pokreću na redove veličine koji su se u sljedećim ispitivanjima (primjerice u poglavljima 3.3 i 3.5) pokazali najpogodnijim za promatrani grafički procesor, ovdje je očekivani rezultat izostao.

Sama preciznost proračuna je krajnje nezadovoljavajuća. Ovisno o metodi izračuna i zrnatosti, vrijednost π obavezno je veća od stvarne vrijednosti konstante i varira između 3.62 i 87.0622 za slučaj da se izračun obavlja nad nizom s 230 elemenata. Kako je glavni cilj mjerenja bilo istraživanje mogućnosti paralelizacije ova nepreciznost se ne smatra velikim nedostatkom jer je na novijim grafičkim procesorima preciznost proračuna s pomičnim zarezom ionako dovedena na zadovoljavajuću razinu.

0

100

200

300

400

500

600

0 1 2 3 4 5 6 7 8 9 101112131415161718192021Zrnatost

Vri

jem

e iz

vođ

enja

(m

s)

Gregory-LeibnizGPU

Gregory-LeibnizCPU

Madhava CPU

Bailey-Borwein-Plouffe (reg.)CPUBailey-Borwein-

Slika 3.6 Brzina računanja π na glavnom procesoru

3.3 Generiranje pseudo-slučajnih nizova brojeva Slučajni nizovi brojeva okarakterizirani su nedostatkom bilo kakvog uzorka unutar samog niza. Generiranje takvih nizova važno je u mnogim područjima znanosti, a osobito često se koristi u računarstvu. Iz ovih razloga razvijeni su mnoge metode generiranja nizova brojeva koji su nasumični, ili, preciznije rečeno, djeluju nasumično. Naravno, različite metode bitno se razlikuju po brzini i po tome koliko su stvarno nasumični nizovi koje generiraju. Veliku ulogu u ocjeni generatora

Page 41: optimizacija rojem čestica na paralelnim arhitekturama

takvih nizova igraju i metode koje su korištene u evaluaciju kvaliteta nizova koji su generirani.

U računarstvu se često koriste generatori pseudo-slučajnih nizova brojeva. Prefiks „pseudo“ odnosi se na činjenicu da ti brojevi nisu uistinu slučajni, već se, zapravo, uz poznate parametre algoritma za generiranje i poznato početno stanje generatora, može točno odrediti kako će niz brojeva izgledati. Ipak, za većinu primjena, takvi generator mogu polučiti zadovoljavajuće rezultate. Najčešće se koriste generatori temeljeni na modulo operaciji – LCG (eng. Linear Congruential Generator). Osnovni princip generiranja brojeva prikazan je u jednadžbi (3.8).

( ) 01 ,, ;mod ℵ∈ℵ∈+=+ cmamcaXX nn (3.8)

Vidljivo je da je ovaj tip generatora ovisi o prethodno izračunatom rezultatu, odnosno da je potrebno pohranjivati prethodno stanje sustava. Također je potrebno odrediti neko inicijalno stanje sustava X0 (eng. seed). Za takav generator uvodi se nomenklatura prikazana u jednadžbi (3.9).

( )0,,, XcamLCG (3.9)

Kvaliteta LCG-a, ali i generatora općenito, uvelike ovisi o odabranim parametrima a, c i m. Nizovi koje ovakav generator proizvodi neće biti uistinu slučajni, već će imati određeni period nakon kojeg će se niz ponavljati, a taj period može maksimalno iznositi m ili biti manji ako su parametri loše odabrani. Također, ako se LCG koriste za generiranje točaka u n-dimenzionalnom prostoru, točke će biti posložene u maksimalno m1/n ravnina. Postoje i mnogi drugi problemi koje ovaj generator može imati, kao što je, primjerice, činjenica da će manje važni bitovi u binarnom zapisu generiranog broja ponavljati s jako kratkim periodom ako je m potencija broja dva. Unatoč nabrojanim, a i mnogim drugim manama, LCG se još uvijek, uz pravilno odabrane parametre smatra dostatnim za većinu primjena.

Često korištena karakteristika LCG jest da se zbrajanjem rezultata n LCG-a s malim periodom može dobiti LCG s izrazito visokim periodom koji će odgovarati umnošku perioda svih generatora od kojih se sastoji. Ovakvi generatori nazivaju se kombinirani LCG (cLCG – eng. combined Linear Congruential Generator). Očita prednost im je da mogu baratati s parametrima koji imaju za nekoliko redova veličine manje vrijednosti u odnosu na klasične LCG uz postizanje podjednake periodičnosti, odnosno, da mogu postići puno veću periodičnost u odnosu na vrijednost parametara. Mana ovakvih generatora u odnosu na “nekombinirane“ verzije generatora je povećan broj računskih operacija te potreba za pohranom n stanja umjesto samo jednoga. Ovakvi generatori, također su osjetljiviji na pogrešno odabrane parametre.

3.3.1 Implementacija

Prilikom implementacije velike poteškoće zadavala je preciznost prilikom izvršavanja matematičkih operacija na grafičkom procesoru. Kako se kod uobičajenih metoda generiranja nizova pseudo-nasumičnih brojeva u pravilu barata s velikim brojevima, dolazilo je do velikih odstupanja od rezultata izračunatih na glavnom procesoru, pri čemu se ispostavilo da je najveći izvor nepreciznosti operacija modulo dijeljenja. Prvi korak pri poboljšanju preciznosti bio

Page 42: optimizacija rojem čestica na paralelnim arhitekturama

je zamjena klasične operacije modulo dijeljenja koju nudi RapidMind razvojno okruženje s nekom preciznijom metodom računanja. Za ostvarivanje ovog cilja najučinkovitijom pokazala se metoda opisana u jednadžbi(3.10), koja je omogućila preciznost računskih operacija na brojevima koji su, u odnosu na klasičnu operaciju modulo dijeljenja, za jedan do dva reda veličine veći.

MaMama /mod −≡ (3.10)

Ova poboljšanja, naravno, nisu mogla poništiti činjenicu da je sam raspon brojeva s pomičnim zarezom koje grafički procesor može prikazat veoma malen, pa će u određenom trenutku početi dolaziti do preljeva, bez obzira na preciznost koja je postignuta. Zato je umjesto klasičnog LCG-a implementiran generator cLCG tipa prikazan u jednadžbi 3.13. Ovaj generator naziva se Wichmann-Hillov cLCG, i zapravo spada u klasu Parker-Miller generatora, jer mu je vrijednost c parametra jednaka nuli, što predstavlja, u biti, dobru karakteristiku jer smanjuje broj konstanti i količinu proračuna koji su potrebni.

( )( )( )

( )1 0, 4690,1655542526 4309,2781718560

1 ,0 ,170 ,30323

1 ,0 ,172 ,30307

1 ,0 ,171 ,30269

LCG

LCG

LCG

LCG

(3.11)

Prvotno je implementiran cLCG koji prima odnosno vraća četverodimenzionalne vektore kao ulaze odnosno izlaze iz jezgre. Prve tri komponente vektora koristile su se za pohranjivanje stanja cLGC-a, dok je četvrta komponenta predstavljala sumu prve 3 (koristila se RapidMind operacija sum), odnosno slučajni broj koji cLCG generira.

Kasnije je, radi postizanja što boljeg omjera broja računskih operacija unutar jezgre u odnosu na količinu podatka koji moraju biti preneseni u jezgru, napravljena nova verzija generatora koja u jednom pozivu jezgre računa dva nasumična broja, odnosno prolazi kroz dvije iteracije cLCG-a. U ovom slučaju prvo se izračunava prvi slučajni broj i pohranjuje se na treće mjesto u vektoru. Potom se na temelju novoizračunatih stanja izračunava još jedan slučajan broj koji se pohranjuje kao četvrta komponenta vektora. Na prve dvije pozicije pohranjuju se novodobivena stanja prvog i drugog LCG-a koji sačinjavaju Wichmann-Hillov cLCG. Stanje trećeg LCG-a nije nigdje pohranjeno već se prilikom sljedećeg poziva jezgre izračunava oduzimanjem prve dvije komponente ulaznog vektora, odnosno prva dva stanja od četvrte komponente. U daljnjem tekstu ova će verzija biti dodatno označavana s “(3:2)“ jer na temelju tri spremljena stanja računa dva nasumična broja po pozivu jezgre, dok će sukladno tome osnovna verzija biti označavana s “(3:1)“. Prilikom inicijalizacije Wichmann-Hillov cLCG-a verzije (3:2) potrebno je osigurati da vrijednost četvrtog elementa vektora odgovara zbroju sva tri LCG-a koji sačinjavaju Wichmann-Hillov cLCG.

Inicijalizacija niza (seed) obavlja se pomoću samog grafičkog procesora, odnosno pomoću virtualnih nizova. Naime, u [7] je pokazano da već inicijalizacija instanci jezgri sa slijednim vrijednostima daje rezultate dovoljno dobre za mnoge primjene pod uvjetom da se odbaci početnih nekoliko stotina bojeva koje će svaka instanca jezgre generirati i koji imaju gotovo linearni odnos s početnom vrijednosti. Kako se, pomoću različitih načina pristupa nizu koje RapidMind platforma podržava,

Page 43: optimizacija rojem čestica na paralelnim arhitekturama

vrijednosti virtualnog niza mogu izmijeniti bez velikih računskih troškova dodatnim operacijama shift i stride kojima će kao parametri biti proslijeđene nasumične cjelobrojne vrijednosti, moguće je postići da dvije “susjedne“ instance jezgre imaju osjetno različite početne vrijednosti. GPU inicijalizacija odvija se u zasebno napisanim jezgrama.

3.3.2 Rezultati

Prilikom mjerenja brzine u ovisnosti o količini podataka korišteni su osjetno manji koraci u promjeni količine podataka nego što je to slučaj u ostalim mjerenjima opisanim u ovom poglavlju, gdje su bili promatrani rezultati samo za određene redove veličine. Zato se, u ovom slučaju, može dati puno preciznija ocjena u kojoj točci korištenje grafičkog procesora postaje isplativije od korištenja glavnog procesora. Priloženi graf (Slika 3.7) prikazuje odnos brzina (3:1) i (3:2) verzije paralelnog cLCG-a, a radi usporedbe, simbolički su naznačene i brzine ostvarene na glavnom procesoru za ugrađeni LCG iz stdlib.h biblioteke i Wichmann-Hillov cLCG. Vidljivo je da je već za nekoliko tisuća instanci jezgre grafički procesor brži od glavnog: Paralelni Wichmann-Hillov cLCG u verziji (3:1) generira više pseudo-nasumičnih brojeva od njegove slijedne verzije već za 212 paralelnih instanci jezgre, dok će klasični LCG iz stdlib.h biblioteke biti prestignut pri 213 paralelnih instanci. Wichmann-Hillov cLCG (3:1) učinkovitost udvostručava s udvostručavanjem broja instanci jezgre koje se paralelno pozivaju i ova pravilnost vrijedi od minimalnog broja instanci za koje se mjerenje obavljalo (24) pa sve do 215 kada ulazi u područje stagnacije u kojem je broj generiranih pseudo-nasumičnih brojeva u vremenu konstantan ili čak i opada. Samo skaliranje ne utječe previše na glavni procesor, dok na grafičkom procesoru ima u potpunosti zanemarivu ulogu tako da neskalirane verzije jezgre ponekad postižu lošije rezultate, što bi sugeriralo da količina proračuna u jezgri nije usko grlo ovog sustava.

Wichmann-Hillov cLCG (3:2) u većini slučajeva postiže približno dvostruko bolje rezultate nego (3:1) verzija. Ovo se može uzeti kao dodatna potvrda prethodnog zaključka da količina proračuna u jezgri ne predstavlja usko grlo ovog sustava.

Page 44: optimizacija rojem čestica na paralelnim arhitekturama

0,000

50,000

100,000

150,000

200,000

250,000

300,000

350,000

400,000

450,000

10 100 1000 10000 100000 1000000 10000000

B roj ins tanc i jez g re

Ge

ne

rira

nih

ps

eu

do

-na

su

mič

nih

bro

jev

a/s

Ugrađeni C P U R ND

c L C G C P U

G P U c L C G 3:1

G P U c L C G 3:2

Slika 3.7 Brzina generiranja pseudo-nasumičnih brojeva

Sama inicijalizacija putem virtualnog niza pokazala se izrazito učinkovita jer zapravo ne predstavlja nikakav vremenski trošak pošto nema prebacivanja podataka. Za velike brojeve instanci jezgre ovakav način inicijalizacije doseže i preko 109 inicijaliziranih vrijednosti po sekundi.

3.4 Algoritam k srednjih vrijednost Termin “Algoritam k srednjih vrijednost“ (eng. k-means algorithm) prvi put je korišten u [8], a začeci ideje mogu se pronaći u [9] i [10]. To je heuristički algoritam koji se najčešće koristi u statistici i strojnom učenju za automatsko grupiranje podataka (eng. clustering).

∑ ∑= ∈

−k

i Sx

ijS

ij

x1

2

minarg µ (3.12)

Formalno, za neki skup (x1, x2,….xn) d-dimenzionalnih vektora, algoritam pokušava zadovoljiti uvjet prikazan u jednadžbi (3.12), gdje je µi srednja vrijednost elementa u skupu Si koji predstavlja podskupu svih elemenata domene. Drugim riječima, algoritam k srednjih vrijednosti pokušat će pronaći takve podskupove točaka da svaka točka ima minimalnu udaljenost od centra skupa kojem pripada. Sam algoritam je iterativne prirode, a broj podskupova k prosljeđuje se kao parametar algoritmu, tj. mora biti definiran od strane korisnika. Svaka iteracija sastoji se od dva koraka: pridruživanje ili klasifikacija (eng. assignment step) i ažuriranje (eng. update step)

( ) ( ) ( ) { }{ }ilkllmxmxxSt

lj

t

ijj

t

i ≠∧∈∀−≤−= ,...,1:;: (3.13)

Page 45: optimizacija rojem čestica na paralelnim arhitekturama

( )( )

( )∑∈

+ =t

ij Sx

jt

i

t

i xS

m11

(3.14)

U prvom koraku, prikazanom u jednadžbi (3.13), svaki se vektor xj iz skupa svih vektora klasificira kao element podskupa od čijeg je središta mi (ova središta nazivaju se centroidima) vektor najmanje udaljen. U drugoj fazi (jednadžba (3.14)) računaju se nova središta skupova dobivenih u prethodnom koraku iteracije kao srednje vrijednosti (odatle i naziv algoritma). Algoritam se zaustavlja kada se niti jedno središte nije promijenilo u odnosu na prethodnu iteraciju (jednadžba (3.15)). Inicijalno se središta za svaki podskup odabiru nasumično. Slika 3.8 skicira princip rada algoritma k srednjih vrijednosti u dvodimenzionalno prostoru.

( ) ( ) { }kimm t

i

t

i ,...,1;1 ∈∀=+ (3.15)

Algoritam je np-hard za d-dimenzionalan Euklidski prostor i dvije grupe (podskupa) ili za dvodimenzionalan prostor i k grupa, ali u stvarnim primjenama obično konvergira brzo. Osnovna pretpostavka algoritma je da su grupe pravilnog kružnog odnosno (hiper)sfernog oblika i podjednakih veličina.

Slika 3.8 Princip rada algoritma k srednjih vrijednosti

3.4.1 Implementacija

Prilikom ostvarivanja paralelne verzije algoritma, grafički procesor korišten je za ubrzanje pojedinih dijelova oba koraka iteracije algoritma. U prvom koraku (korak klasificiranja) na grafičkom procesoru izvodi se pridruživanje klasifikacije svakom pojedinom vektoru iz skupa svih promatranih vektora. Ulaz u jezgru je promatrani

Page 46: optimizacija rojem čestica na paralelnim arhitekturama

vektor, a izlaz redni broj centroida kojem je vektor najbliži. Interno, svaka će instanca jezgre odredit udaljenost proslijeđenog joj vektora od svih centroida i potom će mu kroz niz uvjetovanih pridruživanja dodijeliti redni broj skupa čijem je centroidu dotični vektor najbliži. Same koordinate centroida prosljeđuju se jezgri kao programski parametri. Iz ovoga je vidljivo da će zasebni RapidMind program morati biti napisan za svaki mogući broj centroida. Također je potrebno napisati zasebnu jezgru za određenu dimenzionalnost problema. Za potrebe eksperimenta, napisane su jezgre za grupiranje u četiri različita skupa za četiri, osam i dvanaest dimenzionalni prostor, te jedna jezgra koja četverodimenzionalne vektore grupira u jedan od osamnaest mogućih skupova. Iako je algoritam k srednjih vrijednosti temeljen isključivo na Euklidskoj udaljenosti među točkama, u jezgrama je ostavljena mogućnost da se na temelju korisnikova odabira dinamički generira jezgra s Manhattan metrikom.

U drugom koraku (korak ažuriranja), grafički procesor koristi se za prebrojavanje vektora u pojedinom podskupu i računanje sume vrijednosti vektora (sumiranje vektora po komponentama odnosno dimenzijama). Prebrojavanje se za slučaj s četiri centroida obavlja tako da se rezultati klasifikacije iz prethodnog koraka prosljeđuju RapidMind jezgri koja generira četverodimenzionalne izlazne vektore kojima jedina ne-nula komponenta (iznos te komponente obavezno je 1) ona čija pozicija u izlaznom vektoru odgovara rednom broju centroida kojem je pridružen vektor iz promatranog skupa. Na primjer, ako neki vektor iz promatranog skupa nakon prvog koraka ima pridruženu klasifikaciju 3, ova će jezgra kao izlaz dati vektor {0,0,1,0}. Sama jezgra ponovno je ostvarena nizom pridruživanja koja su uvjetovana usporedbom klasifikacije vektora s konstantom. Na ovu jezgru primijenjena je direktno redukcijska operacija sumiranja, tako da će se izlazi iz jezgre automatski sumirati prije povratka u glavni program. Rezultat ove redukcije vidljiv u glavnom programu je četverodimenzionalni vektor kojem je na svakoj poziciji zapisan broj elemenata koji pripadaju pojedinom skup. U slučaju s osamnaest mogućih centroida, jezgra ne koristi konstante već se koriste programski parametri kako bi se odredilo s kojim centroidima se klasifikacije vektora uspoređuju (u ovom slučaju naravno jezgra i redukcija nad njome moraju biti pozvane pet puta).

Sumiranje vektora obavlja se za svaki mogući centroid zasebno i za svake četiri dimenzije zasebno. Kako bi iz skupa svih vektora bili izdvojeni oni koji su povezani s određenim centroidom, napisana je RapidMind jezgra kojoj se redni broj centroida prosljeđuje kao programski parametar, a ulazni vektori u jezgru predstavljaju klasifikacije i vrijednosti vektora iz promatranog skupa. Ako klasifikacija vektora ne upućuje na centroid čiji je redni broj proslijeđen jezgri, putem jednostavnog uvjetovanog pridruživanja sve komponente vektora svode se na nulu tako da vektor koji nije vezan uz dani centroid neće niti pridonositi sumi. Na ovu operaciju također je primijenjena redukcija kako bi se dobila konačna suma vektora koji pripadaju pojedinom podskupu.

U glavnom programu se potom računaju centroidi za sljedeću iteraciju i provjeravaju uvjeti zaustavljanja algoritma. Inicijalno algoritam je bio napisan tako da se tijekom procesa sumiranja vektora, u jezgru putem programskih parametara prenosi i kardinalnost (broj elemenata) skupa nad kojim se obavlja sumiranje. Svaka komponenta svakog vektora prije ulaska u sam proces redukcije dijeli s prethodno određenim brojem elemenata skupa kojem pripada. Ovime bi se automatski računao novi centroid unutar jezgre umjesto u glavnom programu, a

Page 47: optimizacija rojem čestica na paralelnim arhitekturama

odnos broja računskih operacija po količini podataka prenesenih jezgri bi se poboljšao. Mjerenjima se pokazalo kako ovaj postupak samo usporava rad algoritma, vjerojatno zato što su svi potrebni podaci već prisutni u memoriji grafičke kartice, tako da jezgre za sumiranje nemaju potrebe za vremenski skupocjenim prebacivanjem podataka iz glavne memorije. Ovaj pristup također je, zbog nepreciznosti proračuna na grafičkom procesoru, unosio velike nepreciznosti u algoritam i onemogućio konvergenciju i ispravljanje pogrešaka tijekom razvoja. Zbog navedenog, ovakav pristup je napušten.

3.4.2 Rezultati

Izmjereno vrijeme obrade pojedine iteracije daje očitu prednost grafičkom procesoru za velike količine podataka. Kao što je vidljivo iz priloženog grafa (Slika 3.9), ako broj elemenata iznosi nekoliko desetaka tisuća ili više, grafički procesor je brži. Za mali broj elemenata grafički procesor je izrazito neučinkovit u odnosu na glavni procesor i može obraditi za dva do tri reda veličine manje iteracija po sekundi. Povećanjem broja elemenata učinkovitost grafičkog procesora puno sporije opada nego što je to slučaj s glavnim procesorom. Samo pozivanje jezgri i djelomična obrada u glavnom programu mogli bi predstavljati usko grlo koje za mali broj elemenata ne dozvoljava paralelnoj implementaciji algoritma da prijeđe nekoliko desetaka ili stotina iteracija u sekundi dok istovremeno slijedna verzija programa obrađuje i po nekoliko desetaka tisuća iteracija po sekundi. Pri obradi velikog broja elemenata (nekoliko stotina tisuća ili milijuna elemenata), grafički procesor i dalje je sposoban obraditi nekoliko ili čak nekoliko desetaka iteracija u sekundi (ovisno o dimenzionalnosti i broju centroida) dok je glavni procesor nekoliko puta sporiji i ponekad mu je potrebno dulje od sekunde za obradu samo jedne iteracije.

Porastom dimenzionalnosti problema (Slika 3.10) ili broja centroida, vrijeme izvođenja produžuje se kao što je i očekivano.

1

10

100

1000

10000

100000

10 100 1000 10000 100000 1000000 10000000

B roja uz oraka u s kupu

Iteracija/s

C P U 4C 4D

G P U 4C 4D

Page 48: optimizacija rojem čestica na paralelnim arhitekturama

Slika 3.9 Brzina izvođenja iteracije algoritma k srednjih vrijednosti

Vremena izvođenja vjerojatno bi se mogla poboljšati kada bi se vremenski zahtjevno računanje drugog korijena izbacilo iz koraka klasifikacije prilikom računanja euklidske udaljenosti. Kako je u ovom slučaju udaljenost potrebna samo kako bi se procijenila klasifikacija, odnosno kako bi se vidjelo kojem je centroidu pojedini element najbliži, u ovom slučaju moguće bez gubitka same funkcionalnosti koristiti kvadrat udaljenosti odnosno izbaciti postupak korjenovanja.

0

10

20

30

40

50

60

4D 8D 12D

Dimenzionalnost

Itre

acija

/s

GPU 65kCPU 65kGPU 262kCPU 262kGPU 1MCPU 1M

Slika 3.10 Zavisnost brzine izvođenja o broju dimenzija vektorskog prostora

3.5 Paralelna implementacija dijelova postupka sažimanja digitalne slike po JPEG normi

JPEG je norma za sažimanje (kompresiju) slika pohranjenih u digitalnom formatu. Kvalitetan opis principa sažimanja dostupan je u [11] i [12]. Sam naziv norme nastao je kao akronim naziva grupe Joint Photographic Experts Group koja ga je sastavila. Po načinu sažimanja podataka, JPEG je moguće kategorizirati kao kompresiju s gubicima što znači da se. tijekom procesa sažimanja dio podataka nepovratno gubi. Postoji nekoliko novijih verzija norme, ali svima je zajednički varijabilan stupanj sažimanja (količina izgubljenih podataka raste sa stupnjem kompresije; moguće je i sažimanje bez gubitaka) i primjenjivost na slike različitih rezolucija i dubina boja. Tipični faktor kompresije iznosi oko deset. Danas je JPEG jedan od najraširenijih normi za sažimanje i pohranu slika, a pogotovo je raširen kao način pohrane slika na web-stranicama i općenito u okruženjima gdje su memorijski kapaciteti ili brzine prijenosa podataka ograničene.

Sam proces sažimanja (Slika 3.11) relevantan za ovaj rad odnosi se na tipičnu sliku u boji i može se podijeliti na nekoliko faza ukratko opisanih u idućim poglavljima ()

Page 49: optimizacija rojem čestica na paralelnim arhitekturama

DCT KvantizacijaEntropojsko

kodiranje

Kvantizacijski

koeficijenit

Tablica

specifikacija

Početna slikaPredobrada(prostor boja,

translacija…)

BLOK

8 x 8

Sažeti podaci

Baseline JPEG sažimanje

Slika 3.11 Dijagram procesa JPEG sažimanja

3.5.1 Pretvorba prostora boja i smanjenje prostorne razlučivosti

JPEG norma koristi Y'CBCR prostor boja koji je zapravo način kodiranja veoma raširenog RGB prostora boja. Y'CBCR koristi se inače i za zapis digitalnog video sadržaja, primjerice kod digitalne televizije ili video DVD formata, a na slični način su boje prikazane u PAL normi kod klasičnog televizijsko prijenosa.

Y' komponenta označava svjetlinu (intenzitet) pojedinog elementa slike koji je nelinearno kodiran (ova činjenica označena je apostrofom iz slova 'Y') pomoću gama korekcije. CB i CR označavaju kroma (eng. chroma) komponente – plavu (B – eng. Blue) i crvenu (R – eng. red).

Prednost ovakve reprezentacije boja je u činjenici da je ljudski sustav vida puno osjetljiviji na razlike u intenzitetu nego na razlike u boji. Slika 3.12 ispod fotografije u boji prikazuje i njezinu Y' komponenta, a desno su CB i CR komponente. Vidljivo je da najveću informacijsku vrijednost nosi Y' komponenta, dok će se degradacijom preciznosti preostale dvije komponente puno manje gubiti na kvaliteti slike. Iz ovog razloga je moguće smanjiti prostornu razlučivost (u horizontalnom i vertikalnom ili samo u horizontalnom smjeru) kroma kanala bez većih gubitaka kvalitete slike i time doprinijeti stupnju sažimanja.

Page 50: optimizacija rojem čestica na paralelnim arhitekturama

Slika 3.12 Y'CBCR komponente slike (preuzeto sa http://en.wikipedia.org/wiki/JPEG [12])

3.5.2 Diskretna kosinusna transformacija i kvantizacija

Ova faza je ključna za sažimanje i za razliku od faza opisanih u prethodnom poglavlju primjenjuje se u svim varijantama JPEG sažimanja (Baseline sažimanje). Prije primjene same transformacije, svaka od komponenti slike dobivenih u prethodnim fazama dijeli se na blokove dimenzija 8 * 8 točaka (pixela). Ovi blokovi su osnovni elementi nad kojima se neovisno provode daljnje faze JPEG sažimanja. Raspon vrijednosti u blokovima najčešće se kreće u intervalu [0, 255] i potrebno ga je translatirati tako da se nula nalazi u sredini intervala [-128, 127].

Slika 3.13 Vizualizacija koeficijenata diskretne kosinusne transformacije (preuzeto sa http://en.wikipedia.org/wiki/JPEG [12])

Diskretna kosinusna transformacija spada u linearne transformacije vezane uz Fourierovu analizu. Primjenjuje se samo na realnim brojevima i svojstveno joj je da neku funkciju ili signal prikazuje kao (beskonačnu) sumu nekih trigonometrijskih baznih funkcija odnosno koeficijenata. Prilikom postupka sažimanja po JPEG normi na elemente blokova primjenjuje se dvodimenzionalna diskretna kosinusna transformacija tipa II (jednadžba (3.16)) kako bi se dobio transformirani blok. U formuli je slovom G označena vrijednost u transformiranom bloku dok g označava vrijednost točke u originalnom bloku. Sukladno tome, slova u i v odnosno x i y označavaju koordinate u transformiranom odnosno originalnom bloku. Vidljivo je da će se za svaku točku transformiranog bloka koristiti sve vrijednosti točaka iz originalnog bloka pomnožene s vrijednostima koje su u biti funkcije pozicije točke u novom bloku. Drugim riječima, svaka se točka prikazuje kao linearna kombinacija 64 bazne funkcije (Slika 3.13) Prednost ove transformacije je da ima tendenciju akumulirati većinu signala u gornju desni kut bloka oko istosmjerne komponente (smještene u samom kutu).

Page 51: optimizacija rojem čestica na paralelnim arhitekturama

( ) ( )

( )

≠⇔

=⇔

=

+⋅⋅

+⋅⋅⋅⋅= ∑∑

= =

08

2

08

1

;2

1

8cos

2

1

8cos

7

0

7

0

,,

n

n

n

vyuxgvuGx y

yxvu

α

ππαα

(3.16)

Nakon diskretne kosinusne transformacije svaki element bloka biti će podijeljen s određenim koeficijentom i zaokružen na najbližu cjelobrojnu vrijednost u postupku kvantizacije. Upravo u tom postupku dolazi do odbacivanja dijela informacija. Odabir koeficijenata ima ponajveći utjecaj na stupanj sažimanja, a biraju se tako da su djelitelji veći što su dalje od gornjeg desnog kuta bloka. Time se postiže da je za dijelove signala koji nose najmanje informacija najveća vjerojatnost da će biti svedeni na nulu.

3.5.3 Preslaganje elementa i entropijsko kodiranje

Kako bi se u postupku sažimanja maksimalno iskoristilo stanje blokova opisano u prethodnom poglavlju 3.5.2, elementi blokova se čitaju na specifičan način, krećući se kroz blok dijagonalno (Slika 3.14). Ovaj način kretanja obično se naziva “cik-cak“ i postiže da su bliske frekvencije smještene blizu jedna drugoj unutar bloka. Tako će se mnogi elementi bloka za koje je velika vjerojatnost da su prethodno bili zaokruženi na nulu smjestiti na sam kraj novonapisanoga bloka (pod pretpostavkom da se blok sekvencijalno čita po recima), odnosno postoji velika vjerojatnost da će se na kraju naći relativno dugačak niz nula. Posebnom riječi “EOB“ algoritam za sažimanje će označiti početak takvog niza nula, nakon čega se ostatak niza može odbaciti smanjujući tako memorijski prostor potreban za zapisivanje bloka.

Na samom kraju postupka primjenjuje se neka od tehnika sažimanja bez gubitaka poput Huffmanova kodiranja ili aritmetičkog kodiranja. Ova su sažimanja temeljena na entropiji, a osnovna zamisao jest da se simbolima na temelju vjerojatnosti pojavljivanja pridruži određeni niz bitova: najčešćim simbolima se pridružuje najkraći niz bitova i time se postiže sažimanje. Ova faza može biti ostvarena na različite načine, ali kako nije pogodna za paralelizaciju na danoj platformi, neće biti detaljnije razmatrana niti implementirana.

Page 52: optimizacija rojem čestica na paralelnim arhitekturama

Slika 3.14 Peslagivanja elemenata u bloku

3.5.4 Implementacija

Inicijalno, podaci su raspoloživi u RGB prostoru boja. Pretvorba u Y'CBCR napravljena je prema jednadžbama (3.17), (3.18) i(3.19). Jezgra napisana u tu svrhu ima tri ulazne vrijednosti koje odgovaraju komponentama RGB prostora boja i 3 izlazne komponente za 3 elementa Y'CBCR prostora boja. Sve ulazne i izlazne komponente su zapravo četvorke tako da svaka instanca jezgre istovremeno obavlja pretvorbu nad četiri elementa slike. Korištene su četvorke zato što je grafička kartica ionako prilagođena radu s četverodimenzionalnim vektorima i da je korištena niža dimenzionalnost (npr. dvojke), interno bi komponente opet bile proširene u četvorke u kojima su preostali elementi popunjeni s nulama, pa bi učinkovitost prilikom prebacivanja podatka između glavne memorije i memorije grafičke kartice bila smanjena, jer bi se prebacivali nepotrebni podaci (nule). Ova dimenzionalnost je optimalna i za samu obradu podataka unutar grafičkog procesora koji interno barata s četvorkama. Sam tip podatka koje sačinjavaju četvorku je float kojim grafički procesor interno i barata.

BGRY ⋅+⋅+⋅= 114.0587.0299.0' (3.17)

BGRCB ⋅+⋅−⋅−= 5.0331264.0168736.0128 (3.18)

BGRCR ⋅−⋅−⋅+= 081312.0418688.05.0128 (3.19)

Nakon transformacije sve dobivene vrijednosti biti će u intervalu [0, 255]. Kako bi se smanjili zahtjevi na dinamički raspon tijekom faze diskretne kosinusne transformacije, brojevi se umanjuju za 128 tako da je novi raspon vrijednosti centriran oko nule.

Postupak diskretne kosinusne transformacije može se implementirati na više načina. Samo računanje po jednadžbi (3.16) nije izvedeno zato što zahtjeva puno složenih matematičkih operacija. Iako je ova opcija naizgled idealna zbog odnosa

Page 53: optimizacija rojem čestica na paralelnim arhitekturama

broja proračuna prema količini podataka, kako je ustanovljeno u poglavljima 3.1.3.1 i 3.2.2, grafički procesor ima velikih problema s preciznošću i rezultati bi bili u potpunosti neupotrebljivi, a eventualne pogreške u kodu tijekom razvoja postale bi teško uočljive. Zato se u implementaciji diskretne kosinusne transformacije koriste već prethodno izračunati koeficijenti (Slika 3.13) koji se množe s vrijednošću elementa slike u pojedinoj točki. Sami koeficijenti nisu učitavani iz tablice (čitanje s nasumičnim pristupom) ili prosljeđivani kao ulazi, već su programirani kao konstante u samu jezgru, jer takav pristup nudi daleko najveću brzinu izvršavanja. Koeficijenti su izračunati na glavnom procesoru i integrirani u programski kod jezgre dinamički, tijekom sakupljanja operacija jezgre u glavnom programu. Kako se za svaki od 64 (8 * 8) elemenata u bloku obavlja 8 zbrajanja u horizontalnom i vertikalnom smjeru vidljivo je da je za obavljanje diskretne kosinusne transformacije jednog bloka podataka potrebno 642 = 4096 koeficijenata. Zbog ograničenja na broj instrukcija u jezgri, napisane su dvije jezgre u koje je odmah uključena i faza kvantizacije. Kvantizacijska matrica također je integrirana u kod jezgre na isti način kao i koeficijenti potrebni za diskretnu kosinusnu transformaciju. Svaka jezgra mora imati sva 64 ulazna elementa predstavljena kao 16 četvorki tipa float. Prva jezgra računa prvih 40 elemenata izlaznog bloka, dok se preostalih 24 računa u drugoj. Preslaganje elemenata ostvaruje se automatski, pridjeljivanjem vrijednosti izračunatih u jezgri odgovarajućim izlazima iz iste. Sam proces diskretne kosinusne transformacije je u jezgri implementiran kao niz mad funkcija. Kako su se tijekom razvoja pojavile nestabilnosti, pogotovo prilikom periodičkog pristupanja ulaznim i izlaznim nizovima podataka vezanim uz jezgre u kojima se obavlja diskretna kosinusna transformacija i kvantizacija, bilo je nužno napisati i pomoćnu RapidMind jezgru koja će ulazni niz podataka dijeliti u 16 nezavisnih nizova koji će dalje biti zasebno obrađivani.

Na posljetku je napisana RapidMind jezgra kojom se detektira i označava položaj posljednjeg ne-nultog elementa u bloku podataka. Kao oznaku pozicije, na prvo nulto mjesto iza detektirane pozicije upisuje se broj -257. Kako unutar jezgre ne postoji mogućnost korištenja nizova, niti indeksiranja pojedinih elemenata n-torki pomoću varijabli, svaki element svake od šesnaest četvorki proslijeđenih jezgri mora biti zasebno provjeren. S provjerom se počinje na šesnaestoj četvorki i kreće prema prvoj. Velika je vjerojatnost da nisu svi elementi u bloku podataka jednaki nuli, a istovremeno, diskretna kosinusna transformacija se provodi upravo zato što će se, nakon postupka kvantizacije, vrlo vjerojatno na kraju bloka pojaviti niz nula koje je moguće odbaciti i tako povisiti stupanj sažimanja. Bilo bi logično da se pomoću elemenata kontrole toka, konkretnije elemenata za grananje i uvjetno izvođenje programskog koda (RM_IF), omogući preskakanje nepotrebnih provjera preostalih četvorki, nakon što je pojavljivanje posljednjeg ne-nula elementa pronađeno i označeno. Problem pri uporabi kontrole toka na grafičkom procesoru je u tome što sve instance jezgre koje se istovremeno izvršavaju na grafičkom procesoru moraju čekati da se završi ona instanca čiji je dijagram toka najduži. Prethodna tvrdnja povlači da će one instance jezgre koje pronađu traženi element prije, i dalje morati čekati na izvršenje instanci jezgre koje obrađuju blokove u kojima se traženi element pojavljuje kasnije. Zato su ostvarene četiri različite verzije ove jezgre koje koriste različit broj RM_IF naredbi u svome tijelu: prva verzija uopće ne koristi uvjetovano izvođenje nekog dijela programskog koda, druga i treća verzija koriste četiri, odnosno osam RM_IF naredbi, dok je posljednjoj verziji provjera svake četvorke uvjetovana neuspješnom provjerom prethodne

Page 54: optimizacija rojem čestica na paralelnim arhitekturama

četvorke. Pretpostavljalo se da će druga ili treća verzija jezgre davati najbolje rezultate jer, statistički, blokovi nemaju zadnji ne-nula element ni na samom početku niti na samom kraju.

3.5.5 Rezultati

Mjerenja pokazuju da je brzinska premoć grafičkog procesora i u ovoj aplikaciji ovisna o količini podataka. Za male količine podataka, tipično od nekoliko blokova pa do nekoliko tisuća blokova grafički procesor je sporiji ali je vidljiva tendencija brzog rasta učinkovitosti s porastom količine podataka. Priloženi gafovi (Slika 3.15, Slika 3.16 i Slika 3.17) prikazuju vremena obrade elementa slike za operacije promjene prostora boja, diskretnu kosinusnu transformaciju s kvantizacijom i cijeli proces JPEG sažimanja (bez entropijskog kodiranja). Obje koordinatne osi su u logaritamskom mjerilu. Na grafovima je vidljivo da na navedenom području svako povećanje količine podataka za red veličine uzrokuje promjenu brzine obrade pojedine točke slike za red veličine. U logaritamskom mjerilu navedena pojava se očituje kao približno linearni dio krivulje interpolirane između točaka mjerenja. S druge strane, brzina obrade pojedine točke u slijednom algoritmu koji se izvodi na glavnom procesoru je konstantna i povećanjem broja točaka linearno će se povećati i vrijeme izvođenja algoritma. Količina proračuna po prenesenom podatku također predstavlja važan faktor. Zato RapidMind jezgra zadužena za pretvorbu iz RGB u Y'CBCR prostor boja i skaliranje prestiže glavni procesor za veće količine podataka nego što je to slučaj s jezgrama u kojima se obavlja diskretna kosinusna transformacija i kvantizacija.

0,001

0,01

0,1

1

10

100 1000 10000 100000 1000000 10000000

Količina podataka

Vri

jem

e o

bra

de

po

točki

sli

ke /

s

CPU prostorboja

GPU prostorboja

Slika 3.15 Vremena obrade točke pri postupku transformacije prostora boja

Dolaskom na područje od nekoliko desetak tisuća do nekoliko milijuna točaka po slici, brzina obrade će doći u svojevrsno zasićenje u smislu da će red veličine vremena izvođenja ostati nepromijenjen iako će određene razlike u učinkovitosti ponovo postojati. U ovom području najbolje se očituju prednosti grafičkog procesora. Važno je istaknuti da prilikom provođenja procesa diskretne kosinusne transformacije i kvantizacije veličina pojedine dimenzije ulaznih podataka nije potencija broja dva: iako su slike odabrane tako da svojom visinom i širinom

Page 55: optimizacija rojem čestica na paralelnim arhitekturama

zadovoljavaju ovaj kriterij, svaka točka sastoji se od tri komponente boje pa se ukupna veličina nizova množi s tri. Za razliku od mjerenja u poglavljima 3.2.2 gdje se pretpostavljalo da upravo zbog neodgovarajućih dimenzija niza dolazi do mjestimičnih pogoršanja učinkovitosti, ovdje takav trend nije zapažen.

0,001

0,01

0,1

1

10

100

100 1000 10000 100000 1000000 10000000

Količina podataka

Vri

jem

e o

bra

de

po

toč

ki s

like

/ s

CPU DCTtablični pristup

GPU DCT

Slika 3.16 Vremena obrade točke pri postupku diskretne kosinusne transformacije

U eksperimentu je također moguće neizravno iščitati važnost načina prijenosa podataka. Činjenica da program na glavnom procesoru ima otprilike 13 puta veću brzinu prilikom korištenja tablica s prethodno proračunatim koeficijentima u odnosu na slučaj kada se cjelokupna diskretna kosinusna transformacija obavlja računski sugerira da je tablični pristup puno bolji i za grafički procesor, pogotovo ako se uzme u obzir da su svi koeficijenti već integrirani u sam programski kod koji se izvodi na grafičkom procesoru, i kao takvi se izvode gotovo bez ikakvih kašnjenja. Nedostatak ovakvog pristupa je veličine jezgre, koja je morala biti razbijena na više dijelova samo kako bi se mogla izvoditi (DirectX u verziji 9.0c koji je podržan na korištenom grafičkom procesoru postoje ograničenja na broj operacija koje program za sjenčanje odnosno jezgra može sadržavati. Kasnije verzije DirectX-a ublažavaju ili u potpunosti uklanjanju ova ograničenja). Zbog broja instrukcija u jezgri tijekom izvođenja glavnog programa, samo sakupljanje RapidMind naredbi unutar svih jezgara traje oko 1.17 sekundi, dok prevođenje traje preko 13,2 sekunde.

Page 56: optimizacija rojem čestica na paralelnim arhitekturama

0,01

0,1

1

10

100

100 1000 10000 100000 1000000 10000000

Količina podataka

Vri

jem

e o

bra

de

po

toč

ki s

like

/ sCPU

GPU

GPU +kopiranjememorije

Slika 3.17 Vremena obrade točke pri postupku JPEG sažimanja

Među mjerenjima vršenima u ovom pokusu bila su i mjerenja potrebna za pretvaranje bloka memorije alocirane u C/C++ programskom jeziku u RapidMind niz. Rezultati ovih mjerenja grafički su prikazani (Slika 3.18) i uspoređeni s vremenima potrebnima za klasičnu C/C++ alokaciju memorije. Iz grafičkog prikaza je vidljivo da je uvijek potrebno uračunati vrijeme potrebno za konverziju podataka koje u RapidMind razvojnoj okolini raste s količinom podataka.

0

10

20

30

40

50

60

70

80

90

10 100 1000 10000 100000 1000000 10000000

Količina podataka

Tra

jan

je/s

RapidMind Array

C alloc

Slika 3.18 Vremena alokacije memorije

3.6 Marching cubes algoritam Marching Cubes (eng. marširajuće kocke) algoritam opisan u [13] i [14] razvijen je za ekstrakciju poligonalnih reprezentacija izopovršina iz skalarnih volumetrijskih skupova podataka (podaci koji su definirani položajem točke u trodimenzionalnom

Page 57: optimizacija rojem čestica na paralelnim arhitekturama

prostoru i nekom skalarnom vrijednosti u toj točki). U praksi se često primjenjuje na CT (računalna tomografija – Slika 3.20) i MR (magnetska rezonancija) snimkama, za rekonstrukciju podataka dobivenih mikroskopom, ali i za vizualizaciju podataka koji nisu dobiveni nekom vrstom skeniranja nego matematičkim proračunom (primjerice izgled elektronskog oblaka, vizualizacija metaballs objekata u računalnoj grafici, iscrtavanje magnetskih polja u prostoru…). S vremenom su se pojavile i mnogobrojna poboljšanja i varijacije algoritma.

Slika 3.19 Osnovni tipovi poligona u Marching Cubes algoritmu (preuzeto sa http://users.polytech.unice.fr/~lingrand/MarchingCubes/algo.html)

Osnovna ideja algoritma je uzorkovanje podataka u osam točaka u prostoru raspoređenih tako da predstavljaju osam vrhova zamišljene kocke. Za svaki od tih vrhova određuje se (najčešće na temelju nekog zadanog praga s kojim se vrijednost u vrhu kocke uspoređuje) pripada li unutrašnjosti objekta koji se obrađuje ili je izvan toga objekta. Ako je neki vrh kocke unutar objekta, a neki drugi izvan njega, onda je između njih izopovršina. Postoji 28 = 256 mogućih kombinacija vrhova koji su izvan odnosno unutar objekta od kojih je 14 osnovnih (Slika 3.19), dok se ostale mogu dobiti rotacijom kocke u prostoru ili su simetrične u odnosu na prikazane osnovne kombinacije. Preslikavanje između ovih kombinacija i poligona koji njima nastaju najčešće je zapisano u tablično.

Nakon što je jedna kocka obrađena, uzima se idućih osam točaka koje najčešće dijele stranicu s prethodno razmatranom kockom tako da izgleda kao da zamišljena kocka putuje kroz domenu (odatle i naziv algoritma).

Page 58: optimizacija rojem čestica na paralelnim arhitekturama

Slika 3.20 Uzorak CT smnimke ljudskog L3 kralješka

3.6.1 Implementacija

Marching cubes algoritam ostvaren je tako da svaka instanca napisane RapidMind jezgre obrađuje po jednu kocku. Ulazi u jezgru su osam jednodimenzionalnih vektora tipa float koji predstavljaju vrijednosti u osam vrhova kocke. Za svaku od tih osam vrijednosti uspoređuje se jesu li unutar zadanog intervala. Granice intervala se prenose u jezgru kao programski parametri i omogućavaju podesivu segmentaciju slike na temelju intenziteta točaka. Određivanjem pripadnosti vrijednosti pojedinog proslijeđenog vrha zadanom intervalu formira se broj koji je u kodu predstavljen varijablom nazvanom cubeIndex. Broj se formira tako da se inicijalno njegova vrijednost postavi na nulu i potom se za svaki proslijeđeni vrh kocke koji se nalazi unutar granica zadanog intervala uvećava za vrijednost koja odgovara broju dva potenciranom s rednim brojem vrha (npr. ako je nulti vrh unutar intervala varijabla cubeIndex biti će uvećana za 20 odnosno 1, a ako je i treći vrh unutar granica ukupna vrijednost varijable cubeIndex će iznositi 20 + 23 = 1 + 8 = 9). Slika 3.21 prikazuje redne brojevi vrhova i bridova kocke. Ovim postupkom postiglo se kodiranje svake moguće kombinacije vrijednosti vrhova kocke (u odnosu na pragove zadane intervalom) vrijednošću jednog broja iz intervala [0, 255]. Ako je svih osam točaka unutar ili izvan promatranog objekta, ostatak algoritma neće biti izvršen, čime će se ubrzati samo izvršavanje jezgre na područjima slike gdje postoji velik broj kocaka koje su u potpunosti unutar ili izvan

Page 59: optimizacija rojem čestica na paralelnim arhitekturama

objekta (na područjima gdje postoje kocke koje su djelomično unutar objekta i kao takve sadržavaju određeni broj trokuta, instance jezgre koje završe ranije morat će čekati završetak jezgri koje moraju generirati trokute).

Slika 3.21 Redni brojevi stranica i vrhova kocke

U sljedećem koraku koristi se tablica s 256 redaka i 15 stupaca, koja se u programskom kodu naziva triTable (eng. triangles table). Svaki redak ove tablice predstavlja jednu moguću kombinaciju vrijednosti vrhova kocke (riječ je o vrijednosti vrhova kocke u smislu pripadnosti zadanom intervalu), a prethodno izračunati broj pohranjen u varijabli cubeIndex koristi se za indeksiranje pojedinog retka. Trokuti su zapisani kao niz od tri vrha tako da maksimalni broj trokuta zapisanih u retku tablice pet. Zato jezgra ima ukupno petnaest izlaza koji predstavljaju po tri točke u prostoru za svaki od vrhova pet mogućih trokuta. Ako je broj trokuta manji od maksimalnih pet, za „nepostojeće“ trokute sve tri točke trokuta imati će jednaku vrijednost u svakoj prostornoj dimenziji – trokut će biti reduciran u jednu točku i kao takav će pri kasnijoj obradi biti odbačen. Sami zapisi u recima tablice su brojevi u intervalu [0, 11] i predstavljaju redni broj brida kocke na kojem leži vrh trokuta (Slika 3.21). Za prethodno navedeni primjer u kojem su prvi i treći vrh pripadali promatranom trodimenzionalnom objektu predstavljenom s nizom slika (odnosno bili unutar zadanog intervala), u devetom retku tablice (vrijednost varijable cubeIndex za opisani slučaj iznosi devet) pohranjen je skup vrijednosti: {0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}. Ova kombinacija brojeva označava da zadane vrijednosti vrhova kocke definiraju dva trokuta: prvi ima vrhove na bridovima 0, 11 i 2, a drugi na bridovima 8, 11, 0. Vrijednosti -1 označavaju da nije riječ o trokutima i stupci tablice počevši od prvog pojavljivanja elementa izvan intervala [0, 11] ne moraju biti uzimani u obzir. Samo učitavanje vrijednosti redaka obavlja se čitanjem s nasumičnim pristupom, a radi postizanja što veće učinkovitosti prijenosa podataka i alokacije memorije koriste se četvorke umjesto trojki, tako da tablica zapravo ima 16 stupaca. Kasnije se unutar jezgre te četvorke preslaguju pomoću postupka writemasking-a u trojke koje predstavljaju vrhove trokuta.

Vrhovi trokuta zasada su opisani preko bridova kocke na kojima leže i moraju biti transformirani u točke u prostoru. Kako se ovo preslikavanje mora ponoviti i do petnaest puta po svakoj instanci jezgre (za sva tri vrha od svih pet mogućih trokuta), bilo je logično izdvojiti ga u zasebnu funkciju koja će opetovano biti pozivana za sve vrhove. Sama RapidMind platforma će programski kod funkcije ionako samostalno integrirati u programski kod glavne jezgre. Zato je napisana

0 1

2 3

4 5

6 7

0

1

2

3

4

5

6

7

8 9

10 11

Page 60: optimizacija rojem čestica na paralelnim arhitekturama

posebna RapidMind jezgra koja će za redni broj vrha vratiti koordinate točke. Vraćene koordinate vezane su uz prostor definiran (jediničnom) kockom (u računalnoj grafici koristi se termin object space) i svaka koordinata točke imat će vrijednost u intervalu [0, 1]. Samo preslikavanje između rednog broja brida i koordinata točke ostvareno je pomoću niza uvjetovanih pridruživanja (koristi se RapidMind-ova verzija ternarnog logičkog operatora) kojima se simulira switch-case funkcionalnost. Razvijene su dvije verzije jezgre za određivanje koordinata na temelju proslijeđenog rednog broja brida kocke. Prva verzija jednostavno smješta vrh trokuta na polovicu brida kocke na kojem taj brid leži. Druga verzija radi linearnu interpolaciju pozicije vrha trokuta na temelju vrijednosti u točkama koje omeđuju brid kocke na kojima dotični vrh leži. Funkcija za interpolaciju pretpostavlja da se raspon vrijednosti točaka koje omeđuju brid kreće u intervalu [0, 255]. Ovoj verziji jezgre za određivanje koordinata potrebno je proslijediti i vrijednosti u vrhovima kocke. Odabir između dvije verzije jezgri obavlja se dinamički na temelju postavki koje korisnik odabere.

Na samom kraju procesa potrebno je koordinate vrhove trokuta transformirati u globalne koordinate (u računalnoj grafici za ovakve koordinatne sustave se obično koristi termin world space): do sada su naime sve instance jezgre tretirale kocku koja im je bila proslijeđena kao da je ona smještena u ishodište koordinatnog sustava i razapeta jediničnim vektorima u smjeru koordinatnih osi. Svaka kocka, odnosno trokuti koji su unutar nje određeni moraju biti posloženi na svoju stvarnu poziciju u trodimenzionalnom prostoru. Zato se u jezgru prosljeđuje i virtualni niz tipa grid kao deveti ulaz. Pomoću njega se određuju horizontalna i vertikalna prostorna pozicija kocke. „Dubina kocke“ prosljeđuje se kao programski parametar iz glavnog programa za svaki par proslijeđenih slika. Podaci o odmaku cijelog objekta od ishodišta koordinatnog sustava (translacija cjelokupnog objekta) i podaci o skaliranju jedinične kocke duž pojedine koordinatne osi također su proslijeđeni jezgri kao programski parametri. Drugo navedeni parametri imaju veliku važnost zato što je broj slika (razlučivost u smjeru dubine objekta) često puno manji od broja točaka u pojedinoj slici (razlučivost u smjeru visine i širine objekta) pa će bez skaliranja rekonstruirani objekt izgledati „spljošten“ u jednom smjeru.

Tijekom razvoja korištena je i (neregistrirana) aplikacija 3D Object Converter v4.40 koja je sposobna učitavati i prikazivati trodimenzionalne objekte pohranjene u različitim formatima. Aplikacije je korištena prvenstveno za vizualnu inspekciju dobivenih rezultata. Kao ulazni skup podataka korištene su CT snimke raznih ljudskih kostiju. Snimke su objavljene od strane Laboratorija za ljudsku anatomiju i embriologiju na Sveučilištu u Brussels-u u Belgiji ("The image datasets used in this experiment were from the Laboratory of Human Anatomy and Embryology, University of Brussels (ULB), Belgium"). Snimke su radi jednostavnosti pretvorene u tekstualne datoteke pomoću jednostavne skripte napisane u Matlab-u, i kao takve su učitavane u aplikaciju.

3.6.2 Rezultati

Iako ovaj algoritam ima naizgled veoma povoljan odnos broja računskih operacija u odnosu na broj podataka koji je potrebno prenijeti, rezultati dobiveni mjerenjima (Slika 3.24) pokazuju inferiornost u odnosu na slijedni algoritam izvođenom na glavnom procesoru, ukazujući tako na postojanje jednog ili više uskih grla u samoj izvedbi algoritma. Pristup podacima iz tablice (čitanje s nasumičnim pristupom) u

Page 61: optimizacija rojem čestica na paralelnim arhitekturama

svakoj instanci jezgre obavlja se pet puta i vjerojatno je jedan od čimbenika koji ograničavaju brzinu izvođenja algoritma. Tomu pogotovo doprinosi činjenica da je tablica relativno malena (256 redaka), a neke kombinacije točaka su češće od drugih pa je moguće, ovisno internoj arhitekturi grafičkog procesora, da dolazi do konflikta između dvije ili više instanci jezgre prilikom pristupanja određenim recima tablice.

Slika 3.22 Vizualizacija kosti ljudske zdjelice

Drugi čimbenik koji utječe na brzinu izvođenja je potreba za konstantnim upisivanjem podataka u memoriju grafičke kartice. Za svaki sloj koji se obrađuje potrebno je prebaciti po dvije slike u memoriju grafičke kartice i potom iščitati sve generirane trokute iz memorije (ovo čitanje spada u fazu naknadne obrade poligona i kao takvo ne utječe na mjerenje vremena izvođenja jezgre). Također, samo pokretanje jezgre zahtjeva određenu količinu vremena.

Dodatna neučinkovitost proizlazi iz dimenzionalnosti ulaznih podatka u jezgru: ulazi su jednodimenzionalni vektori, a kako grafička kartice interno vjerojatno barata s četverodimenzionalnim vektorima – preostale dimenzije ulaznog vektora se popunjavaju s nulama i nemaju nikakvog značaja, ali i dalje se prenose dodatno ograničavajući propusnost.

Page 62: optimizacija rojem čestica na paralelnim arhitekturama

Slika 3.23 Vizualizacija ljudskog L3 kralješka

Mjerenja su vršena na CT snimci dijela ljudske zdjelice i L3 kralješka. Vizualizaciju istih (dobiven primjenom marching cubes algoritma) prikazuje Slika 3.22 odnsono Slika 3.23.

0

2

4

6

8

10

12

14

16

l3 kralježak Zdjelica

CPU

GPU

GPU + prevođenje

Slika 3.24 Vremena izvođenja Marchig Cubes algoritma

Page 63: optimizacija rojem čestica na paralelnim arhitekturama

4. Norma OpenCL

OpenCL (eng. Open Computing Language) [24] je programski jezik opće namjene za paralelno programiranje u heterogenim procesorskim okruženjima omogućavajući tako iskorištavanje računske moći svih procesora u računalnom sustavu. Norma je razvila i održava Khronos grupa – neprofitna grupacija za razvoj i normiranje besplatnih otvorenih programskih sučelja. Članovi ove grupe su vodeće tvrtke u području računarstva i elektronike (npr. Google, Intel; AMD/ATI, Nokia, Apple, Ericsson, nVidia, Motorola, Sony Computer Entertainment, Samsung Electronics, Oracle/Sum MicroSystems, Texas Instruments, itd.). Kasnije je patent za normu dobio Apple Inc.

Osnovne sastavnice OpenCL-a bile bi programsko okruženje (eng. framework), koje koordinira izvođenje proračuna na različitim procesorima, i OpenCL C - jezik koji je temeljen na C99 (službeno: ISO/IEC 9899:1999) verziji C programskog jezika. Sam način funkcioniranja OpenCL norme najčešće se opisuje preko nekoliko modela koji su hijerarhijski posloženi:

• Sklopovski model odnosno model platforme (eng. Platform model)

• Model memorije (eng. Memory model)

• Model izvršavanja (eng. execution model)

• Model programiranja (eng. Programming model)

Iz specifikacije je norme vidljivo je da je dobrim dijelom temeljen na grafičkim procesorima, ali zbog općenitosti, moguće je preslikavanje i na mnoge druge vrste procesora.

4.1 Sklopovski model OpenCL u svojoj specifikaciji opisuje općenitu arhitekturu sklopovlja na kojem se izvode (Slika 4.1) jezgre pisane u OpenCL C programskom jeziku. Ova arhitektura sastoji se od centralnog dijela, odnosno glavnog procesora ili domaćina (eng. host) na kojem se izvodi glavna aplikacija.

Osim centralnog procesora, specificirana apstrakcija računalnog sustava sadrži i jedan ili više uređaja sposobnih za izvođenje OpenCL programskog koda odnosno jezgri. Ovi uređaji se obično nazivaju procesni uređaji (eng. compute device). Svaki procesni uređaj sastoji se od jedne ili više procesnih jedinica (eng. Compute Unit – CU), a svaka od tih jedinica sastoji se od jednog ili više procesnih elemenata (eng. Processing Element – PE). Ovi procesni elementi su osnovna jedinica na kojoj se obavljaju proračuni. Procesna jedinica izvršava niz (prethodno spomenuti stream) instrukcija iz OpenCL jezgre paralelno na svim procesnim elementima, odnosno ponaša se kao SIMD (svi procesni elementi unutar procesne jedinice rade u sinkroniziranom, lock-step načinu rada izvršavajući istovremeno istu instrukciju) ili SPMD (svaki procesni element ima vlastito programsko brojilo – eng. Program Counter – PC). Unutar samog procesnog elementa moguće je osim skalarnih koristiti i vektorske tipove podataka (kao i kod RapidMind platforme), čime se dodaje još jedna razina paralelizma sklopovskom modelu.

Page 64: optimizacija rojem čestica na paralelnim arhitekturama

Slika 4.1 OpenCL model izvršavanja

4.2 Model izvršavanja Model izvršavanja opisuje kako će jezgra biti preslikana na prethodno definirani sklopovski model. Prilikom izvršavanja jezgre definira se n-dimenzionalni diskretni prostor kojem se svaka instanca jezgre dodjeljuje i izvršava jednoj točci u prostoru. Ova instanca jezgre naziva se radnom stavkom. Dimenzionalnost definiranog prostora može poprimiti vrijednost manju ili jednaku tri. Radne stavke sačinjavaju radne grupe unutar kojih svaka instanca jezgre također ima definirani indeks u odnosu na ostale radne stavke u grupi. Radne grupe predstavljaju dodatnu podjelu globalnog diskretnog prostora. Iz ove činjenice je vidljivo da dimenzionlanost ovog prostora definiranog unutar radne grupe mora odgovarati dimenzionalnosti globalno-definiranog prostora. Također, veličina lokalnog prostora radne grupe duž svake dimenzije mora biti djelitelj veličine globalnog prostora duž te iste dimenzije. Svaki radni zadatak definiran je putem svojeg globalnog položaja i položaja unutar radne grupe. Također, svaka radna grupa ima svoj jedinstveni indeks u n-dimenzionalnom prostoru. Informacija o rednom broju pojedine instance jezgre (ili dretve kod nekih drugačijih pristupa paralelizmu, kao što je primjerice MPI), odnosno njezinom položaju u odnosu na ostale jezgre često je ključna u paralelnim sustavima jer se pomoću nje može definirati ponašanje pojedinog paralelnog elementa. Ovo je posebno važno kod trivijalno paralelnih zadataka.

Sa strane programiranja aplikacije na centralnom procesoru za izvršavanje jezgri mora postojati definirani kontekst (eng. context) unutar kojeg će se OpenCL programski kod izvršavati. Kontekst obuhvaća sve uređaje (eng. devices), programe (eng. program), jezgre i memorijske objekte. Kontekst se stvara za

Page 65: optimizacija rojem čestica na paralelnim arhitekturama

određene uređaje (GPU, CPU, druge vrste akceleratora) i nad određenom platformom koja predstavlja implementaciju OpenCL norme. Potom se uz kontekst vežu programi koji su sačinjeni od cjelokupnog programskog koda koji je potrebno obaviti na određenom skupu procesnih uređaja. Jezgre predstavljaju dijelove programa.

Uz kontekst se vežu i memorijski objekti koji predstavljaju podatke koje će jezgra koristiti ili generirati kao izlazne vrijednosti. Memorijski objekti mogu biti tipa buffer (podatci su sekvencijalno spremljeni i iz jezgre im je moguće pristupati pomoću klasičnih pokazivača) ili image (slike) – posebni spremnici podataka gdje je način pohranjivanja podataka skriven od korisnika. Objekti tipa image koriste se za spremanje dvodimenzionalnih ili trodimenzionalnih (od OpenCL verzije 1.2 slike mogu biti i jednodimenzionalne) tekstura, slika, ili međuspremnika okvira (eng. frame-buffers) i moguće ih je filtrirati i definirati vrijednost koje se će bit vraćene ako se pristupi elementima izvan granica (out of bounds) slično kao i kod RapidMind platforme. Funkcije kojima se pristupa elementima objekata tipa image mogu koristiti i normalizirane koordinate i uvijek vračaju četverodimenzionalne vektore cjelobrojnog tipa (signed/unsigned int) ili tipa s pomičnim zarezom (float).

Na posljetku se uz uređaje na temelju kojih je kontekst definiran vežu redovi za izvršavanje (command queues) kojima se prosljeđuju jezgre i naredbe za rad s memorijskim objektima na izvršavanje.

4.3 Memorijski model Specifikacija OpenCL-a predviđa hijerarhijsku strukturu memorijskog sustava (Slika 4.2), sličnu kako i kod klasičnog modela računala. Na dnu hijerarhije obično se nalazi memorija s najmanjom podatkovnom propusnošću, ali najvećeg kapaciteta (Slika 4.3). U OpenCL memorijskom modelu podatci su prvenstveno pohranjeni u memoriji centralnog procesora. Radne stavke nemaju nikakvu mogućnost pristupa ovoj vrsti memorije.

Svaki procesni uređaj sadrži svoju vlastitu memoriju za nasumično čitanje podataka i zapisivanje rezultata izvođenja jezgri. Ova vrsta memorije naziva se globalnom memorijom jer će svaka instanca OpenCL jezgre imati mogućnost nasumičnog pristupa podatcima pohranjenim u ovoj memoriji. Osim ove memorije OpenCL memorijski model specificira da procesni uređaji imaju i memoriju za konstante. Kada se podatci iz glave aplikacije šalju nekom OpenCL kompatibilnom uređaju na obradu, prvo se kopiraju iz memorije domaćina u memoriju procesnog uređaja. Glavna aplikacija ima mogućnost čitanja, pisanja i dinamičke alokacije globalne memorije i memorije za konstante. Ovim putem će podatci za obradu biti preneseni OpenCL jezgrama, i rezultati koje su OpenCL jezgre pohranile mogu biti učitani nazad u glavnu aplikaciju. Same jezgre u trenutku pokretanja imaju pristup dijelovima globalne memorije i memorije za konstante koje je glavna aplikacija alocirala (statička alokacija iz perspektive programskog koda koji se izvršava na računskom uređaju), uz napomenu da će jezgre moći samo čitati iz memorije namijenjene konstantama. OpenCL norma predviđa da se pristup glavnoj memoriji i memoriji za konstante može ubrzati korištenjem priručne memorije.

Page 66: optimizacija rojem čestica na paralelnim arhitekturama

PROCESNI

ELEMENT 1

PRIVATNA

MEMORIJA

PROCESNA JEDINICA 1

PROCESNI

ELEMENT N

PRIVATNA

MEMORIJA

LOKALNA

MEMORIJA

PROCESNI

ELEMENT 1

PRIVATNA

MEMORIJA

PROCESNA JEDINICA N

PROCESNI

ELEMENT N

PRIVATNA

MEMORIJA

LOKALNA

MEMORIJA

PRIRUČNA MEMORIJA ZA GLOBALNU MEMORIJU I KONSTANTE

PROCESNI UREĐAJ

GLOBALNA MEMORIJA

MEMORIJA ZA KONSTANTE

MEMORIJA PROCESNOG UREĐAJA

Slika 4.2 OpenCL memorijski model

Radne grupe imaju svoju vlastitu memoriju koja je namijenjena dijeljenju podataka između radnih stavki unutar radne grupe. Ova memorija naziva se lokalnom memorijom i nije vidljiva radnim stavkama iz drugih radnih grupa. Lokalna memorija se dinamički alocira od strane glavne aplikacije, ali joj glavna aplikacija nema pristup. Sve radne stavke mogu čitati i pisati u lokalnu memoriju. Po izvršenju jezgre podatci iz lokalne memorije se gube. Lokalna memorija obično se koristi kako bi se osiguralo da algoritam ne mora biti striktno trivijalno paralelan – obično se podatci iz glavne memorije ili neki među rezultati koje radne stavke produciraju pohranjuju u nju i nakon toga im druge radne stavke pristupaju i koriste ih (dijeljenje podataka).

Slika 4.3 Hijerahijska struktura OpenCL memorijskog modela

Page 67: optimizacija rojem čestica na paralelnim arhitekturama

Svaka radna stavka ima i svoju privatnu memoriju koju koristi tijekom obrade podataka. Glavna aplikacija nema nikakvog dodira s ovom vrstom memorije. Privatna memorija alocira se statički, prilikom pokretanja jezgre i radna stavka može čitati i pisati podatke u dotičnu, dok niti jednoj drugoj radnoj stavci varijable deklarirane u privatnoj memoriji nisu vidljive. Podatci iz privatne memorije se gube nakon što se instanca jezgre izvrši do kraja.

Stanje memorije vidljivo pojedinoj radnoj stavci ne mora biti identično stanju koje vide druge radne stavke (eng. relaxed consistency memory model)

4.4 Programski model Paralelizam u OpenCL normi ostvaren je kao podatkovni paralelizam (data parallel) koji iskorištava najčešće relativno velik broj procesnih elementa u procesnim uređajima. Za razliku od starijih rješenja poput RapidMind platforme, kod OpenCL norme preslikavanje radnih stavki na podatke nije nužno jedan na jedan, nego je moguće nasumično čitanje podataka pomoću pokazivača (eng. relaxed data parallel programming). Ovakav način preslikavanja potpomognut je i hijerarhijskom strukturom podatkovnog paralelizma koja je predviđena sklopovskim modelom i modelom izvršavanja. Broj instanci jezgre, odnosno dimenzionalnost i veličinu samog prostora izvršavanja određuje korisnik, najčešće prema količini podatka koje je potrebno obraditi u odnosu na količinu podataka koje obrađuje pojedina instanca jezgre. Ovdje OpenCL prema specifikaciji obvezuje korisnika da specificira globalne dimenzije, dok je specificiranje dimenzija radne grupe moguće, ali nije obvezno. U slučaju da dimenzije radne grupe nisu navedene, sama implementacija OpenCL norme mora biti sposobna odrediti veličinu radne grupe s obzirom na dimenzije cjelokupnog radnog prostora (veličina radne grupe mora biti djelitelj radnog prostora u duž svake dimenzije).

Istovremeno, osim podatkovnog paralelizma, omogućeno je koristiti i paralelizam na razini zadataka (task parallelism). Računalo domaćin može istovremeno neovisno pristupati različitim redovima za izvršavanje, odnosno različitim računskim procesorima. Unutar pojedinog reda za izvršavanje, naredbe se ne moraju nužno izvršavati onim redom kojim su pristigle (po potrebi, moguće je specificirati da red za izvršavanje izvršava pristigle naredbe striktno onim redoslijedom kojim su poslane). Od verzije 1.2 podržana je i podjela procesnih uređaja na razini procesnih jedinica (eng. device partitioning ili device fission kod AMD-ove implementacije OpenCL 1.1 norme), tako da različite skupine procesnih jedinica istog računskog procesora mogu obavljati različite zadatke u skladu s prioritetom i brojem potrebnih radnih stavki pojedinog zadatka. Potrebno je napomenuti da prema službenom OpenCL modelu paralelizma na razini zadatka, dotični se ostvaruje pokretanjem jezgri koje imaju samo jednu instancu, a paralelizam se ostvaruje korištenjem vektora kao tipova podatka unutar jezgre, istovremenim izvođenjem više jezgri ovakvog tipa i paralelnim korištenjem drugih aplikacija koje su sposobne koristiti iste procesne uređaje. Ipak, specifikaciji unatoč, ovakva vrsta paralelizma, kao što je opisano u prvom dijelu ovog odlomka, ne bi trebala biti ograničena samo na jezgre s jednom instancom.

4.5 Sinkronizacija Kako OpenCL norma ne predviđa samo trivijalno paralelne zadatke, moraju postojati mehanizmi sinkronizacije među pojedinim paralelnim procesorima. Na

Page 68: optimizacija rojem čestica na paralelnim arhitekturama

razini radnih stavki sinkronizacija je moguća na razini radne grupe: stavke iz iste radne grupe osim dijeljenja podataka putem lokalne memorije, mogu se i međusobno sinkronizirati (OpenCL C funkcija barrier). Ovakav način sinkronizacije implementiran je pozivom funkcija iz OpenCL C programskog jezika i sve radne stavke moraju pozvati tu funkciju – ne smije se dogoditi da neka radna stavka, primjerice zbog djelovanja mehanizama za kontrolu toka, ne pozove sinkronizacijsku funkciju. Sinkronizacijske funkcije na razini radnih stavki obično se koriste kako bi se osiguralo da su sve instance jezgre došle do određene točke u programskom kodu, najčešće da su obavile neku memorijsku radnju poput zapisivanja podatka u lokalnu memoriju tako da ih druge radne stavke mogu učitati i koristiti. Tijekom izvođenja pojedine OpenCL jezgre, sinkronizacija među radnim grupama koje izvode tu jezgru nije izravno moguća, ali se može obavljati neizravno, nedjeljivim funkcijama (atomic functions) ili po završetku izvođenja jezgre iz glavne aplikacije ili nekom drugom jezgrom.

Na razini procesnih uređaja, OpenCL norma zamišljena je tako da se za svaku naredbu koja se prosljeđuje nekom od redova za izvršavanje može definirati lista događaja (eng. events) koji se moraju dogoditi, odnosno naredbi koje moraju biti prethodno izvršene kako bi se trenutno zadana naredba mogla pokrenuti. Sama naredba također može vratiti objekt tipa događaj na koji onda druge naredbe mogu čekati. Naravno i glavna aplikacija može čekati na događaje (primjerice da neka jezgra kojoj su podatci poslani na obradu završi s radom pa da glavna aplikacija može dohvatiti rezultate obrade) ili pozivati sinkronizacijske funkcije koje nalažu izvršavanje svih događaja koji čekaju u redu za izvršavanje. Ovakav pristup predstavlja vrlo moćan, a s druge strane vrlo fleksibilan i jednostavan mehanizam za implementiranje zavisnosti među događajima. Po načinu sinkronizacije unutar glavne aplikacije OpenCL bi mogao biti klasificiran kao sustav za programiranje temeljeno na događajima, ali ova klasifikacije nije jedina moguća (primjerice, sam OpenCL C sposoban je raditi s vektorskim tipovima podatka odnosno i kao takva može se smatrati i koordinacijskim programskim jezikom).

4.6 Povezivanje modela i preslikavanje na sklopovlje Preslikavanje modela izvršavanja na sklopovski model u velikoj je mjeri intuitivno. Radne stavke izvršavaju se na procesnim elementima. Svaki procesni element ima svoju vlastitu memoriju koja ima ulogu privatne memorije. Analogno tome, radna grupa izvodi se na procesnoj jedinici čija memorija predstavlja lokalnu memoriju, dok se cjelokupni n-dimenzionalni diskretni prostor radnih stavki izvodi na procesnom uređaju koristeći njegovu memoriju kao globalnu memoriju i memoriju za konstante.

Vrijedi istaknuti da OpenCL norma niti na jednom mjestu ne specificira da je preslikavanje s modela izvršavanja na sklopovski model “jedan na jedan“ preslikavanje. Ovu činjenicu proizvođači procesora često koriste kako bi sakrili latencije vremenski zahtjevnijih operacija pomoću višedretvenosti: više radnih stavki u vidu više niti pokreće se na istom procesnom elementu i međusobno se izmjenjuju. Kada neka radna stavaka prilikom izvršavanja svojih instrukcija naiđe na operaciju čiji rezultat mora čekati, biti će stavljena na čekanje završetka dotične operacije, a na istom procesnom elementu počet će se izvršavati neka druga radna stavka odnosno nit dok i ona ne naiđe na vremenski zahtjevnu operaciju. (Tipičan primjer ovakvih vremenski zahtjevnih operacija bilo bi čitanje iz globalne

Page 69: optimizacija rojem čestica na paralelnim arhitekturama

memorije čija je propusnost usko grlo kod mnogih procesora.) Ovakav pristup omogućava visoku propusnost i brzinu obrade podatka. Ako je broj radnih stavki koje se moraju obaviti toliko velik da nije moguće istovremeno preslikati sve stavke na dostupno sklopovlje, čak ni koristeći upravo opisani višedretveni pristup, sklopovlje će obrađivat dio po dio radnih stavki pokrećući nove niti kada stare završe s izvođenjem. Opisani način obrade već je spomenut u poglavlju 2.2.1 i u potpunosti je sakriven od korisnika.

Samo sklopovlje koje ima sposobnost izvođenja OpenCL jezgri je vrlo raznoliko i značajno varira kako po pitanju prvenstvene namjene i tako i u pogledu interne arhitekture. To mogu biti različiti procesori za digitalnu obradu signala, programirljivi logički uređaji ili razni hibridni procesori kao što je Cell BE. Proizvođač mora samo pronaći načina da implementira norma. Moderni grafički procesori u pravilu su daleko najsličniji opisanim sklopovskim i memorijskim modelima. Sadrže velike količine brze i visokopropusne globalne memorije, i veći broj procesnih jedinica s lokalnom memorijom još više propusnosti. Svaki procesni element unutar procesne jedinice ima izrazito brzu privatnu memoriju i sposobnost obrade vektorskih podataka. Također u pravilu koriste se priručnim memorijama radi postizanja povećanja propusnosti.

Glavni procesori također se često koriste kao OpenCL procesni uređaji. U tom slučaju njihova uloga u sustavu je dvojaka jer istovremeno predstavljaju i procesor računala domaćina i ciljani uređaj za izvršavanje OpenCL programskog koda. Zbog višedretvenosti ova dva aspekta aplikacije izmjenjuju se na glavnom procesoru bez problema.

Ipak, glavni procesori dosta odudaraju od sklopovskih i memorijskih modela opisanih OpenCL normom. Na relativno malen broj jezgri u pravilu se preslikavaju procesne jedinice. Veće dimenzije radnih grupa najčešće se postižu višedretvenošću. Kako u pravilu ne postoje mogućnosti kontrole priručne memorije i procesorskih registra koji bi možda mogli predstavlja lokalnu i privatnu memoriju, kod većine implementacija norme sistemska memorija s nasumičnim pristupom koristi se za simuliranje svih vrsta memorije navedenih OpenCL memorijskom modelu. Neke vrste glavnih procesora imaju posebne vektorske instrukcije koje im omogućavaju obradu vektorskih tipova podataka, dok će procesori koji nemaju takve instrukcije morati baratati s vektorskim tipovima podatka slijedno, obrađujući jednu komponentu za drugom.

Kako OpenCL u osnovi predstavlja samo normu, a implementacija je prepuštena proizvođačima, mnogi dijelovi sklopovskog modela često su izostavljeni, a da pri tom nimalo nije umanjena mogućnost izvršavanja OpenCL jezgri. Kao tipičan primjer funkcioniranja ovakvog sklopovlja mogla bi se navesti OpenCL jezgra koja iz globalne memorije kopira podatke u lokalnu kako bi ih potom koristila. Jezgra se izvršava na glavnom procesoru. U ovakvoj situaciji glavni procesor, koji nema nikakve stvarne lokalne memorije, koristi dio memorije s nasumičnim pristupom (RAM) kao lokalnu memoriju. Ovime je OpenCL norma zadovoljena i programski kod pisan u OpenCL-u za neki procesor koji posjeduje stvarnu lokalnu memoriju može se izvršavati i na ovakvom procesoru. Navedeni primjer zorno demonstrira fleksibilnost norme koja nije odveć detaljno definirana i mogućnost prenošenja programskog koda na velik broj bitno različitih procesorskih arhitektura kod takve općenite norme.

Page 70: optimizacija rojem čestica na paralelnim arhitekturama

Razmatrano sa stajališta performansi prethodni primjer otkriva i veliku manu ovakvog pristupa definiranju norme. Lokalna memorija obično se koristi jer u pravilu ima osjetno bržu komunikaciju s procesnim elementima nego globalna memorija. Kada se neki podatak treba podijeliti s ostalim radnim stavkama u grupi on će biti prebačen iz globalne u lokalnu memoriju i potom će mu druge radne stavke pristupati. U navedenom primjeru s glavnim procesorom, kako ne postoji fizička lokalna memorija, procesor obavlja kopiranje iz memorije s nasumičnim pristupom nazad u memoriju s nasumičnim pristupom. Ovo kopiranje je u mnogim slučajevima nepotrebno (osim ako neka druga radna grupa neće kasnije pisati na isto mjesto u globalnoj memoriji i time uništiti podatke), a dobitaka u brzini neće biti s obzirom na to da su globalna i lokalna memorija ista vrsta memorije na sklopovskoj razini. Povrh svega, smanjuje se i ukupna količina dostupne globalne memorije jer će dio iste bit prenamijenjen u lokalnu memoriju. Drugim riječima, funkcionalna prenosivost ne bi trebala biti poistovjećivana s prenosivošću performansi – potrebno je pisati različite jezgre za arhitekturalno različite procesore. Čest je slučaj da čak i isti tipovi procesora različitih proizvođača (primjerice grafički procesori AMD-a i nVidije) ili čak različiti procesori istog proizvođača, moguće čak i iz iste serije, zahtijevaju optimizacije u OpenCL programskom kodu koje će najbolje iskoristiti arhitekturu nad kojom se izvode.

4.7 OpenCL programsko okruženje Sučelje glavne aplikacije prema OpenCL-u podijeljeno je na tri osnovna sloja. Prvi sloj vezan je uz samu implementaciju OpenCL norme (OpenCL Platform layer) i omogućava dohvaćanje podatka o različitim aspektima pojedine implementacija. Moguće je provjeriti koje implementacije (koje se još nazivaju i OpenCL platformama) su dostupne na sustavu i koje su njihove karakteristike (primjerice koje ekstenzije podržavaju). Čest je slučaj da je na istom sustavu dostupno više OpenCL platformi. Kao primjer, moguće je zamislit situaciju u kojoj se na u sustavu nalazi jedna OpenCL platforma koju isporučuje proizvođač grafičkog procesora i koja je vezana isključivo za dotični grafički procesor, jedna OpenCL platforma koju isporučuje proizvođač glavnog procesora i, primjerice, još jedna koja predstavlja implementaciju OpenCL norme od nekog nezavisnog proizvođača i koja je namijenjena i glavnim i grafičkim procesorima.

Prvi sloj također omogućava i dohvaćanje podataka o dostupnim procesnim uređajima vezanima uz svaku pojedinu platformu. Moguće je saznati različite sklopovske karakteristike (radni takt, količina memorije, broj procesnih jedinica…), karakteristike vezane uz OpenCL funkcionalnost (podrška za slike, dimenzije radnih grupa, podrška za brojeve s pomičnim zarezom u dvostrukoj preciznosti, podržane ekstenzije…), kao i neke općenite identifikatore procesnog uređaja (proizvođač, naziv procesora, vrsta procesora). Ovakve široke mogućnost informiranja o dostupnosti pojedinih elementa i mogućnosti na određenom sustavu potrebne su jer je OpenCL kao norma dosta općenit i stoga, radi postizanja prenosivosti programskog koda, mora postojati mogućnost programske provjere postojanja karakteristika sustava koje su potrebne određenoj aplikaciji.

Na temelju dobivenih informacija, aplikacija bi trebala odabrati idealne elemente za stvaranje konteksta, čime funkcionalnost prvog sloja završava.

Drugi sloj (OpenCL Runtime) koristi se kako bi aplikacija na sustavu domaćinu upravljala s kontekstom. U ovom sloju postoji bogata podrška za premještanje

Page 71: optimizacija rojem čestica na paralelnim arhitekturama

podataka između memorije domaćina i globalne memorije. Drugi sloj koristi se i za rad s događajima i pokretanje jezgri.

Treći sloj (OpenCL Compiler) sačinjavaju prevoditelji za OpenCL C programski jezik. Kao i kod RapidMind platforme (poglavlje 3.1.3.3), moguće je dinamički prevoditi jezgre napisane u OpenCL C programskom jeziku u binarni format pogodan za izvođenje na nekom procesoru.

OpenCL razvojno okruženje prvotno je bilo vezano uz C programski jezik, ali u međuvremenu su nastale verzije za mnoge druge popularne jezike kao što su C++ [25], Java, Phyton, Fortran, “.NET“ programski jezici…

4.8 OpenCL C Sam programski jezik za pisanje OpenCL jezgri vrlo je sličan C programskom jeziku i kao takav programerima vrlo intuitivan za korištenje. Podržani su svi osnovni tipovi ((u)char, (u)short, (u)int, (u)long, float, bool, void, size_t, pokazivači) s iznimkom da se logički tip (bool) ne može koristiti kao ulazni ili izlazni argument u jezgru je mu je veličina (u bajtovima) definirana u implementaciji a ne samom normi. Implementirani su i neki dodatni tipovi podataka. Half tip predstavlja brojeve s pomičnim zarezom, ali ima pola širine (2B) tipa float. Postoje tipovi za rad s pokazivačima, slikama (image) i događajima.

OpenCL također definira i vektorske tipove koji se sastoje od bilo koje vrste prethodno definiranih cjelobrojnih tipova ili tipova s pomičnim zarezom. Vektorski tipovi imaju većinom se ponašaju kao i skalarni, ali svaka operacija ili funkcija nad njima odvija se po komponentama (primjerice zbrajanje dvo-komponentnog vektora zapravo predstavlja dva skalarna zbrajanja). Vektorski tipovi predstavljaju najnižu razina paralelizma u OpenCL normi. Vektori mogu imati 2, 3, 4, 8 ili 16 komponenti. Kao i kod RapidMind platforme u OpenCL normi podržane su brze i intuitivne manipulacije pojedinim komponentama ili skupinama komponenti u vektoru (swizzling i writemasking), samo što su dodane i neke naprednije opcije (primjerice pristupanje samo parnim ili neparnim komponentama).

Podržane su različiti načini pretvaranja (casting) implicitnog i eksplicitnog pretvaranja jednog tipa podatka u drugi, uključujući i proširivanje skalarnih tipova tijekom interakcije s vektorskim tipovima.

Osim uobičajenih operatora za aritmetiku, usporedbu, pridruživanje te logičkih operatora, postoji i velik broj predefiniranih funkcija koje rade s ugrađenim tipovima. Ove funkcije uključuj širok spektar matematičke funkcionalnosti, od one koje se smatra uobičajenijom (primjerice trigonometrijske funkcije, potenciranje i logaritmiranje, interpolacija, zaokruživanje), pa do ne toliko česte funkcionalnosti (npr funkcija ldexp prvi argument koji je broj s pomičnim zarezom množi s brojem dva potenciranim s vrijednošću drugog argumenta koji je cjelobrojnog tipa). Neke važnije matematičke funkcije imaju i svoj pandan s prefiksom native_. Ovakve funkcije kao parametre primaju brojeve sa pomičnim zarezom, a trebale bi biti implementirane tako da zahtijevaju osjetno kraće vrijeme za izvršavanje nauštrb preciznosti proračuna, uz napomenu da je sama implementacija i preciznost prepuštena proizvođaču. Tablica 4.1 prikazuje brzinske dobitke u slučaju korištenja _native funkcija.Kako u OpenCL C programskom jeziku postoje i vektorski tipovi, definirane su i različite vektorske funkcije kao što su vektorsko množenje, normalizacija ili računanje udaljenosti među točkama. Dodatno, osim

Page 72: optimizacija rojem čestica na paralelnim arhitekturama

uobičajenih funkcija za obradu podatka, podržane su i funkcije za sinkronizaciju među radnim stavkama (barrier, mem_fence), asinkrono kopiranje između globalne i lokalne memorije, funkcije koje daju informaciju o indeksu radne stavke ili radne grupe u odnosu na globalni radni prostor (i radni prostor grupe) te funkcije koje vračaju informaciju o dimenzijama radne grupe i globalnog diskretnog radnog prostora. Posebnu vrstu funkcija čine funkcije za dohvaćanje elemenata iz objekta tipa slika (image objekata). OpenCL također podržava klasične nedjeljive funkcije (atomic functions).

Ostatak OpenCL C programskog jezika, kao što je već rečeno, relativno dobro prati C99 normu, pa tako primjerice sadrži svu uobičajenu sintaksu za kontrolu toka. Ipak postoji više iznimaka i odstupanja od C99 norme koja su često bila nužna zbog računalnih procesora kojima je ovaj programski jezik namijenjen. U nastavku ovog odlomka biti će navedeno nekoliko primjera. Postoji dodatna ključna riječ (__kernel) kojom se pravi distinkcija između jezgre i obične funkcije (funkcije deklarirane kao __kernel moraju vračati tip void) kao i ključne riječi za označavanje pokazivača na globalnu (__global) ili lokalnu (__local) memoriju (svi pokazivači koje se prosljeđuju kao argumenti funkciji deklariranoj kao __kernel moraju bit deklarirani kao __global ili __local i ne smiju biti pokazivači na pokazivače). S druge strane neke ključne riječi kao što su extern, static, auto ili register nisu podržane. Pokazivači na funkcije, rekurzije i nizovi varijabilne duljine nisu podržani.

Tablica 4.1 Ubrzanje dobiveno korištenjem _native funkcija

Funkcija Faktor ubrzanja

sin() 27.1

cos() 34.2

tan() 13.4

exp() 4.0

exp2() 3.4

exp10() 5.2

log() 12.3

log2() 11.3

log10() 12.8

sqrt() 1.8

rsqrt() 6.4

powr() 28.7

divide() 4.4

Page 73: optimizacija rojem čestica na paralelnim arhitekturama

4.9 Ostale karakteristike OpenCL norme Jedna od osnovnih ideja OpenCL norme bila je prenosivost na različite vrste procesora. Ostvarivanje takvog svojstva zahtjeva određene generalizacije, žrtvujući tako neke od prednosti koje bi određene arhitekture mogle imati. Kako bi se kompenziralo za ovu pojavu i omogućilo proizvođačima što veću fleksibilnost i inovativnost ponude, OpenCL norma koristi se ekstenzijama. Ekstenzije omogućuju proširivanje osnovne funkcionalnosti OpenCL norme. S druge strane predstavljaju samo opciju pa ih niti jedna proizvođač nije obvezan implementirati ako kojim slučajem nisu pogodne za njegov proizvod. Postoje tri osnovne vrste ekstenzija:

1. KHR ekstenzije

• razvijene od strane cjelokupne Khronos grupacije

• široko podržane i implementirane na mnogim platformama

• formalno verificirane

• format: cl_khr_<naziv_ekstenzije>

• primjer: cl_khr_fp64 – podrška za brojeve s pomičnim zarezom u dvostrukoj preciznosti (double)

2. EXT ekstenzije

• razvijane od više proizvođača

• podržane od relativno velikog broja platformi

• često bivaju deklarirane kao KHR ekstenzije sa slijedećom verzijom OpenCL norme

• format: cl_ext_<naziv_ekstenzije>

• primjer: cl_ext_device_fisssion – podrška za podjelu nekog procesnog uređaja na razini procesne jedinice na dva ili više dijelova koji potom mogu izvršavati neovisne OpenCL jezgre

3. Ekstenzije specifične za pojedinu platformu

• razvijaju ih pojedini proizvođači

• u pravilu vezane za jednu ili vrlo mali broj platformi i njihovim korištenjem programi postaju neprenosivi na druge platforme

• format: cl_<naziv_proizvođača>_<naziv_ekstenzije>

• primjer: cl_amd_printf – dodaje mogućnost ispisa na standardni izlaz (stdout) OpenCL C programima

Pojedine ekstenzije mogu se uključiti ili isključiti pomoću predprocesorskih direktiva u OpenCL C programskom kodu.

Postoje posebne verzije OpenCL norme namijenjene implementaciji na ugradbenim sustavima (eng. embedded systems) kao što su pametni telefoni, upravljačke jedinice različitih elektroničkih uređaja itd. Ova verzija norme zapravo predstavlja podskup punog OpenCL norme.

Page 74: optimizacija rojem čestica na paralelnim arhitekturama

OpenCL norma predviđa jednostavnu komunikaciju i prenošenje podataka između OpenCL aplikacija ili dijelova aplikacije i programskih sučelja koja se koriste u računalnoj grafici. Time bi se, primjerice, mogao ostvariti vrlo učinkovit cjevovod koji odmah obavlja i vizualizaciju rezultata koje je OpenCL dio aplikacije generirao. Dodatna pogodnost ove mogućnosti leži u činjenici da se kao OpenCL procesni uređaj često koristi grafički procesor pa na sklopovskoj razini nije potrebno ni kopiranje podataka koje je OpenCL dio aplikacije generirao, nego se jednostavno OpenCL memorijski objekt redefinira kao objekt iz odgovarajućeg programskog sučelja za računalnu grafiku. Osim memorijskih objekata, moguće je pretvarati i OpenCL događaje u odgovarajuće sinkronizacijske mehanizme programskih sučelja za računalnu grafiku. Norme za programiranje računalne grafike s kojima OpenCL može biti u interakciji su OpenGL (Open Graphics Library - otvorena norma koja definira višeplatformsko sučelje za razvoj programa koji generiraju 2D i 3D računalnu grafiku), OpenGL ES (OpenGL for Embedded Systems – podverzija OpenGL norme namijenjena implementaciji u ugradbene sustave), i Direct3D (dio Microsoftovog DirectX programskog sučelja zadužen za iscrtavanje trodimenzionalne računalne grafike).

4.10 Druga rješenja za programiranje masovno paralelnih sustava Osim OpenCL postoji još nekoliko sustava za programiranje sklopovlja koje ima karakteristike masovnog paralelizma. Jedno od takvih rješenja bila bi i RapidMind Platforma opisana u poglavlju 3.1. RapidMind platforma i slična rješenja (primjerice Brook programski jezik) postoje veće relativno dugo i mogli bi biti klasificirani kao rješenja starije generacije. Karakterizira ih da su se pojavila s prvim višejezgrenim glavnim procesorima i grafičkim procesorima koji su mogli biti donekle programirani, odnosno na početku pojave koncepta GPGPU-a i paralelizacije kao metode ubrzanja procesora. Niti jedno od tih rješenja nije se uspjelo nametnuti kao široko prihvaćena norma. Rješenja starije generacije morala su biti prilagođena sklopovlju koje je imalo različite arhitekture i ograničenu funkcionalnost u pogledu općeg računanja (primjerice u poglavlju 3.1.3.1 navodi se kako grafički procesori nisu podržavali cjelobrojni tip podataka i kakvi su sve problemi proizlazili iz ove činjenice). Neposrednov programiranje takvog sklopovlja bilo je relativno komplicirao pa tako primjerice RapidMind platforma koristi OpenGL kako bi prenamijenila grafičke procesore u masovno paralelne procesore opće namjene.

Usporedba OpenCL norme i RapidMind platforme kao tipičnih rješenja različitih generacija, jasno prikazuje napredak na gotovo svim područjima programiranja masovno paralelnih sustava. Sama ideja da se donese norma osigurava veliku prenosivost kako u sadašnjosti, tako i u budućnosti. Ovdje je ponovo vidljiva pojava opisana u poglavlju 2.2.3: norme i programi diktiraju specifikacije sklopovlja – kako bi prodrli na još jedno tržišno područje proizvođači grafičkih procesora ugrađuju u svoju arhitekturu podršku za OpenCL normu baš kao što su prije ugrađivali podršku za OpenCL i DirectX. Svaki proizvođač može samostalno implementirati OpenCL normu tako da nema potrebe da jedan proizvođač pokušava napraviti univerzalan alat koji dobro funkcionira na svom sklopovlju (što se u slučaju RapidMind platforme ipak pokazalo neizvedivim).

Jedna od glavnih prednosti OpenCL norme u odnosu na RapidMind platformu bila bi osjetno veća fleksibilnost u pristupanju podacima iz memorije iz jezgri, pri čemu

Page 75: optimizacija rojem čestica na paralelnim arhitekturama

veliku ulogu igra i dosta složeniji memorijski model koji je sada vidljiv programeru (iako je na nekim procesorima koje je RapidMind platforma podržavala možda i postojalo sve sklopovlje koje OpenCL modeli predviđaju, ono nije bilo vidljivo samoj platformi). Na strani glavne aplikacije, OpenCL programsko okruženje (drugi sloj) ima nešto više mogućnosti prilikom manipulacije podataka, ali RapidMind platforma u ovom aspektu ne zaostaje puno i ima nešto veću jednostavnost korištenja. Općenito, neki programeri donekle kritiziraju OpenCL zbog djelomičnog žrtvovanja jednostavnosti korištenja radi većeg broja mogućnost. Redovi za izvršavanje i sinkronizacija na temelju događaja uvode izrazito visok stupanja fleksibilnosti kakav nemaju ni RapidMind platforma, ali ni moderniji sustavi kao što je CUDA (koja će biti opisana nešto kasnije u ovom poglavlju). Dinamičko generiranje jezgri također je naprednije kod OpenCL norme s obzirom na to da se proizvoljni programi mogu generirati u glavnoj aplikaciji i prosljeđivati prevoditelju (treći sloj) kao nizovi znakova. S druge strane, već prevedeni programi mogu se pohraniti tako da se ne gubi vrijeme na prevođenje prilikom svakog pokretanja aplikacije. OpenCL C programski jezik ima znatno veće mogućnosti u odnosu na RapidMind platformu, a tu je i dodatna mogućnost ekstenzija. Ipak RapidMind platforma nudi određenu funkcionalnost koje OpenCL C-u nedostaju (primjerice ugrađena podrška za redukcije kako na razini vektora, tako i na razini cijelih nizova).

Među modernijim rješenjima za programiranje masovno paralelnih sustava najviše se ističe CUDA (Compute Unified Device Architecture) koja je razvila nVidia. CUDA je prvenstveno namijenjena grafičkim procesorima koje proizvodi nVidia dok se na drugim procesorima donekle može koristiti zahvaljujući prevoditeljima nezavisnih proizvođača. Sintakso, CUDA je, uz neka ograničenja, relativno slična C i C++ programskom jeziku. Sklopovski model, model izvođenja i memorijski model gotovo su identični. Kako su moderni grafički procesori dotičnog proizvođača kompatibilni s OpenCL normom, razlike niti ne mogu biti velike. Iz perspektive glavne aplikacija CUDA je nešto jednostavnije građe i se svodi na prebacivanje podataka i instrukcija (ne postoji sofisticiraniji mehanizam kontrole i sinkronizacije kao što su događaji u OpenCL normi). Zbog vezanosti isključivo za grafičke procesore jednog proizvođač, CUDA je bolje povezana s tim procesorima i sposobna je bolje iskoristiti njihove specifičnosti. Tako će na nVidijinim grafičkim procesorima isti (paralelni) algoritam implementiran u CUDA-i i u OpenCL C programskom jeziku davati bolje performanse u CUDI (pod uvjetom da je optimalno implementiran). Iako visoki stupanj specijaliziranosti donosi prevagu na jednoj vrsti procesora, nedostatak općenitosti ograničava prenosivost koju OpenCL norma posjeduje. Kako je CUDA dosta starija od OpenCL norme, a nVidijini proizvodi široko rasprostranjeni i relativno visokih performansi, njezina zastupljenost na tržištu je velika.

Osim CUDA-e, vrijedi spomenuti i DirectCompute programsko sučelje (Microsoft) i OpenHMPP normu za programiranje masovno paralelnih sustava.

Page 76: optimizacija rojem čestica na paralelnim arhitekturama

5. Karakteristike korištenog sustava

Tijekom implementacije korišteno je računalo opremljeno višejezgrenim glavnim procesorom i grafičkom karticom s procesorom novije generacije. Većina paralelnih algoritam testirana je na grafičkom procesoru, dok su neki testirani i na glavnom. Neke jezgre pisane su posebno za glavni, a posebno za grafički procesor, zbog razlika u arhitekturi.

5.1 Glavni procesor

Glavni procesor korišten prilikom testiranja je Athlon II X3 445 kojeg proizvodi AMD. Riječ je o 64-bitnom procesoru koji ima tri aktivne fizičke jezgre (u stvarnosti procesor ima četiri jezgre, ali jedna je onemogućena od proizvođača, bilo zbog defekta ili oštećenja u procesu proizvodnje, bilo zbog marketinških razloga) koje rade na taktu od 3.1 GHz. Svaka jezgra ima vlastitih 128 kB prvostupanjske (L1) priručne memorije podijeljene tako da se u 64 kB memorije spremaju dohvaćeni podatci, a u 64 kB procesorske instrukcije. Svak jezgra posjeduje i 512 kB drugostupanjske priručne memorije. Sva priručna memorija radi na istoj frekvenciji kao i procesor. Važno je spomenuti da procesor podržava i vektorske instrukcije (MMX, SSE, SSE2, SSE3, SSE4a, Enhanced 3DNow) koje rade s registrima širine 128 bitova, odnosno sposobne su raditi s vektorima širine 4 - ako se uzimaju u obzir primjerice klasični cjelobrojni tipovi (int) ili brojevi s pomičnim zarezom u jednostruko preciznosti (float). Procesor ima dvostruki memorijski kontroler i podržava DDR2 i DDR3 tip memorije s nasumičnim pristupom efektivne frekvencije 1066 MHz (DDR2 memorija)) odnosno 1333 MHz (DDR3 memorija). Teoretska propusnost ovakve memorijske konfiguracije iznosi otprilike 20 GB/s za DDR3 memoriju s efektivnim radnim taktom od 1333 MHz. Testno računalo opremljeno je s 2 modula DDR3 memorije efektivne frekvencije 1333 MHz (stvarna frekvencije memorijskih modula iznosi 333 MHz). Svaki modul ima kapacitet 2 GB i spojen je na zasebni memorijski kontroler procesora radi maksimizacije memorijske propusnosti.

Kao OpenCL procesni uređaj (u AMD-ovoj implementaciji OpenCL norme), korišteni glavni procesor svaku jezgru koristi kao procesnu jediniciu, na kojoj se procesni elementi izvršavaju slijedno. Memorijski objekti tipa image nisu podržani (oni su uostalom temeljni na tekstuama pa je logično da su vezani u grafičke procesore). Podržana dimenzionalnost radnog prostoa je tri, a najveći broj radnih stavki u radnoj grupi iznosi 1024. Za raziku od VLIW arhitekture objašnjene u poglavlju 5.2.1.1, glavni prcesor paralelizam unutar jezgre može ostvariti samo kao podatkovni paralelizam – vektorizacijom (SSE jedinice su SIMD jedinice i nisu sposobne za pralelizam na razini zadtaka). Kako je objašnjeno u poglavlju 2.2.2, glavni proceosri prilagođeni su skirvanju latenijca i slijednoj izvedbi (ovome pridonose i nekoliko puta viši radni taktovi). Zato će, primjerice, zbrajanje brojeva (bilo float ili int tipa) imati za dva reda veličine manju latenciju ako se izvodi na glavnom procesoru. Samo pokretanje OpenCL jezgre također će biti za red veličine brže (25 µs naprema 225 µs na grafičkom procesoru), a neće biti ni potrebe za kopiranjem podataka preko sabirnice, pa je u nekim situacijama korištenje glavnog procesora kao OpenCL procesnog uređaja isplativije. Unutar samog OpenCL postoji mogućnost definiranja posebnih vrsta jezgri koje imaju samo jednu radnu stavku. Ovakve jezgre koriste se za slijedne algortime i

Page 77: optimizacija rojem čestica na paralelnim arhitekturama

vjerojatno su i zamišljene za izvođenje izvođenje na glavnim procesorima (korišteni grafički procesor ih niti ne podržava).

5.2 Grafički procesor Računalo korišteno u razvoju i mjerenjima bilo je opremljeno XFX 5850 Black Edition grafičkom karticom. Kartica je bazirana na AMD-ovom Radeon HD 5850 procesoru, ali je radna frekvencija tvornički podignuta sa 725 MHz na 765 MHz (frekvencija radne memorije je postavljena na 1125 MHz umjesto uobičajenih 1000 MHz). Grafička kartica ima i daljnje mogućnosti podizanja radnog takta od strane korisnika što je označeno “Black Edition“ u nazivu kartice. Tijekom testiranja radni taktovi nisu dodatno podizani.

Tablica 5.1 prikazuje su neke opće karakteristike Radeon HD 5850 grafičkog procesora, a radi usporedbe, tablica također navodi iste karakteristike za stariji ATI Mobility Radeon X1600 korištenog za razvoj RapidMind baziranih aplikacija. kako bi se ilustrirao kontinuirani porast performansi u tablici su i karakteristike grafičko procesora AMD Radeon HD 7970 GHz Edition– jednog od najjačih grafičkih procesora iz dvije generacije mlađe serije procesora istog proizvođača (ATI odnosno AMD).

Tablica 5.1 Usporedba općih karakteristika grafičkih procesora različitih generacija

ATi Mobility Radeon X1600

AMD Radeon HD 5850

AMD Radeon HD 7970 GHz

Edition

Datum plasiranja na tržište prosinac, 2005. rujan, 2009. lipanj, 2012.

Proizvodni proces (nm) 90 40. 28

Sabirnica prema računalu PCIe x16 PCIe 2.1 x16 PCIe 3.0 x16

Radni takt grafičkog procesora (MHz)

445 725 1000

Page 78: optimizacija rojem čestica na paralelnim arhitekturama

Broj jedinica za sjenčanje vertexa 5

Broj jedinica za sjenčanje točaka 12

14402 20482

Broj jedinica za izlazno iscrtavanje 4 32 32

Brzina iscrtavanja (MT/s 1) 1 800 52 200 128 400

Tip memorije GDDR3 GDDR5 GDDR5

Količina memorije (MB) 256 1024 3072

Širina memorijske sabirnice (bita) 128 256 384

Radni takt memorije (MHz)

470 MHz (efektivno3 940)

1000 (efektivno3 4000)

1500 (efektivno3 6000)

Teoretska maksimalna memorijska propusnost (GB/s)

15.4 128 288

Podržane (grafičke) biblioteke

DirectX 9.0c; OpenGL 2.0

DirecX 11, OpenGL 4.1, OpenCL 1.1

DirecX 11.1, OpenGL 4.2, OpenCL 1.2

1 MT/s – Milijuna Texela u sekundi – texel (eng. texture element) je element teksture preslikan na

odgovarajući element slike (pixel)

2 Unificirane jedinice za sjenčanje Vertex-a, točaka (pixel-a) i geometrije

3 Memoriji je moguće pristupiti više puta unutar jednog takta signala vremenskog vođenja

Page 79: optimizacija rojem čestica na paralelnim arhitekturama

5.2.1 Naputci za programiranje AMD Radeon HD 5850 grafičkog procesora

Prenosivost OpenCL aplikacija, kako je već objašnjeno, ne nosi garanciju optimalnih performansi na različitim procesorskim arhitekturama. Većina programskog koda, kao što je već spomenuto, morat će biti posebno napisana za pojedine procesore kako bi se postigle maksimalne performanse. Zato većina proizvođača daje naputke kako najbolje iskoristiti određene procesore. Detaljno poznavanje načina funkcioniranja procesora može polučiti velike razlike u brzini izvođenja koda. Zato je u idućim poglavljima dan osnovni pregled unutarnje građe i svojstava korištenog grafičkog procesora, kao i naputci za najbolje iskorištavanje istog (eng. best practices) temeljeni na [23]. Kratak pregled svojstava procesora prikazan je i tablično (Tablica 5.2), pri čemu je radi usporedbe prikazan i aktualniji grafički procesor istog proizvođača.

Tablica 5.2 Usporedba unutarnje građe i performansi grafičkih procesora različitih generacija

AMD Radeon HD 5850 XFX BE

AMD Radeon HD 7970 GHz Edition

Radni takt (MHz) 765 1 000

Radni takt globalne memorije (MHz) 1 125 (efektivno1 4

500) 1 500 (efektivno1

6000)

Širina memorijske sabirnice prema globalnoj memoriji (bita) 256 384

Broj procesnih jedinica 18 32

Broj procesnih elemenata 1 440 2 048

Broj vektorskih registara2 po procesnoj jedinici 16 384 16 384

Količina registarske memorije po procesnoj jedinici (kB) 256 256

Količina lokalne memorije po procesnoj jedinici (kB) 32 64

Količina globalne memorije(MB) 1024 – 2048 3072-6144

Količina memorije za konstante (kB) 40 128

Page 80: optimizacija rojem čestica na paralelnim arhitekturama

Memorija za konstante po procesnoj jedinici (kB) 8 163

Količina priručne memorije prvog stupnja (L1) po procesnoj jedinici (kB)

8 16

Količina priručne memorije drugog stupnja (L2) (kB) 512 768

Propusnost privatne memorije na razini procesnog uređaja/procesne jedinice (GB/s)

10 574 / 587 24 576 / 768

Propusnost lokalne memorije na razini procesnog uređaja / procesne jedinice (GB/s)

1 762 / 98 4 193 / 131

Propusnost priručne memorije za konstante (GB/s) 3 525 512

Propusnost priručne memorije prvog stupnja (L1) na razini procesnog uređaja / procesne jedinice (GB/s)

881 / 49 2 047 / 64

Propusnost priručne memorije drugog stupnja (L2) (GB/s) 392 767

Propusnost globalne memorije (GB/s) 144 288

GFLPOS (jednostruka/dvostruka preciznost) 2 203 / 440.6 4 300 / 1 075

GFLPOS/Watu 13.83 4 17.2

Maksimalan broj valova/ radnih stavki po procesnom uređaju 496 / 31 744 1 280 / 81 920

Makismalan broj valova po procesnoj jedinici (prosjek) 27.6 40

1 Memoriji je moguće pristupiti više puta unutar jednog takta signala vremenskog vođenja

2 Svaki vektorski registar širok je 128 bita

3 16 kB raspoređeno je na četiri procesne jedinice

4 Vrijednost se donosi na procesor AMD Radeon HD 5850

Page 81: optimizacija rojem čestica na paralelnim arhitekturama

5.2.1.1 Interna arhitektura AMD Radeon HD 5850 grafičkog procesora

AMD Radeon HD 5850 sastoji se od 18 procesnih jedinica. Slika 5.1 prikazuje građu korištenog procesora. Svaka procesna jedinca sastoji se od 16 stream jezgri (eng. stream core), a svaka od njih sastoji se od pet procesnih elemenata. Sve stream jezgre u procesnoj jedinici izvršavat će istu procesorsku instrukciju istovremeno. Različite procesne jedinice mogu izvršavati različite instrukcije. Općenito modeli AMD Radeon HD 5xxx serije grafičkih procesora (ali i ostalih grafičkih procesora neke serije dotičnog proizvođača) arhitekturalno se razlikuju prvenstveno po broju procesnih jedinica (kao i kod višejezgrenih procesora dio procesnih jedinica može biti prisutan ali je defektan ili isključen iz marketinških razloga).

Svaki procesni element unutar stream jezgre može izvršavati po jednu skalarnu operaciju, ne nužno jednaku operaciji koju izvode preostali elementi iz stream jezgre. Kako bi se ovo postiglo, svaka procesorska instrukcija, sastoji se od pet različitih podinstrukcija i kontrole toka. Ovo se naziva VLIW arhitektura (eng. Very Long Instruction Word). U slučaju brojeva s pomičnim zarezom u dvostrukoj preciznosti (double), po dva procesna elementa će obrađivati samo jedan broj. Jedan od navedenih pet procesnih elemenata naziva se T-element i sposoban je izvršavati transcendentalne operacije kao što su trigonometrijske operacije ili logaritmiranje. Isto sklopovlje koje se koristi za izvršavanje instrukcija koristi se i za izračunavanje memorijskih adresa u fazama pristupa memoriji.

Page 82: optimizacija rojem čestica na paralelnim arhitekturama

Slika 5.1 Građa AMD Radeon HD 5850 grafičkog procesora

OpenCL radna stavka preslikava se na stream jezgru. Pogodno je da unutar same jezgre postoji paralelizam koji se može preslikati na VLIW arhitekturu, odnosno na

Page 83: optimizacija rojem čestica na paralelnim arhitekturama

same procesne elemente. Ovo preslikavanje ponekad se naziva stupnjem vektorizacije i za neku jezgru izražava se u postotcima, pokazujući koliko se u prosjeku koristi procesnih elemenata po instrukciji. Iz navedenog je vidljivo da samo sklopovlje ne mora nužno biti u potpunosti preslikano na OpenCL sklopovski model: stream jezgra AMD Radeon HD 5850 grafičkog procesora predstavlja procesni element OpenCL sklopovskog modela dok su procesni elementi unutar stream jezgre zapravo zaduženi za paralelizam unutar jezgre. Od poglavlja 5.2.1, pojam procesnog elementa odnosit će se na procesne elemente koji sačinjavaju stream jezgru korištenog grafičkog procesora, a ne na procesni element koji je definiran unutar OpenCL sklopovskog modela (poglavlje 4.1). Pseudokod 5.1 prikazuje (u assemblerskom kodu) različite stupnjeve iskorištenosti VLIW arhitekture: ALU instrukcija 10 koristi samo jedan procesni element iz stream jezgre dok je ostalih pet neiskorišteno tijekom procesorskih ciklusa u kojim se ALU instrukcija 10 izvršava. ALU instrukcija 11 koristi dva procesna elemenata, dok ALU instrukcija 13 iskorištava svih pet procesnih elemenata (uključujući i T-element). Za prikazane instrukcije ukupni stupanj vektorizacije iznosio bi 53.33%. Jednostavan način za ostvarivanje paralelizma unutar jezgre bio bi korištenje vektorskih tipova podataka. S obzirom na to da VLIW instrukcija može sadržavati različite podinstrukcije vektorizacija nije nužno jedini način paralelizacije (ovo nije slučaj kod vektorskih instrukcija u glavnim procesorima što je jedan od uzroka nužnosti pisanja različitih jezgri za različite procesorske arhitekture). AMD-ov OpenCL prevoditelj pokušat će prilikom prevođenja pakirati u VLIW instrukciju bilo koje instrukcije koje ne pokazuju nikakvu međusobnu ovisnost. Drugim riječima, paralelizam na razini zadataka ovdje se ostvaruje na instrukcijskoj razini unutar same OpenCL jezgre. Povećanjem broja instrukcija u jezgri, povećava se vjerojatnost da će se dio instrukcija moći izvršiti paralelno. U nekim slučajevima, ovo se može postići jednostavnim razmatanjem programskih petlji: sadržaj petlje duplicira se onoliko puta koliko se puta smanjuje broj ponavljanje petlje (pod uvjetom da su iteracije petlje međusobno neovisne). Vrijedi napomenuti da je ponekad i suprotan pristup učinkovit – kako bi se povećao broj instrukcija unutar jezgre, moguće je cijeli sadržaj jezgre obuhvatiti programskom petljom i tako natjerati radnu stavku da uvišestruči količinu posla.

Korišteni grafički procesor nudi i posebne instrukcije za ubrzavanje obrade (multi)medijskog sadržaja. Maksimalna dimenzionalnost radnog prostora je 3. Preko ekstenzija su podržani trokomponentni vektori.

Page 84: optimizacija rojem čestica na paralelnim arhitekturama

10 x:ADD R25.x, T0.y,PV9.y

11 x:AND_INT R26.x, (0x7FFFFFFF, 1.#QNANf).x, PV10.x

t:SETE_INT R27.x, PV10.x, (0x3F800000, 1.0f).y

12 x:LSHR T0.x, PV11.x,

(0x00000017, 3.222986468e-44f).x

y:ADD R9.y, -PV11.x, 1.0f

z:SETGT_UINT T0.z, PV11.x, (0x7F800000, 1.#INFf).y

w:OR_INT ____, PV11.x, (0x3F800000, 1.0f).z

t:SETGT_UINT R6.y, (0x3F800000, 1.0f).z, PV11.x

Pseudokod 5.1 Primjeri različitih stupnjeva iskorištenosti procesnih elemenata stream jezgre

Noviji grafički procesori istog proizvođača kao što je AMD 7xxx serija grafičkih procesora, napustili su koncept stream jezgre i VLIW arhitekture.

5.2.1.2 Valovi

Svaka radna stavka može pokrenuti po jednu VLIW instrukciju po procesorskom ciklusu. Kako bi se poništilo kašnjenja (latencije) i povećala učinkovitost, po četiri radne stavke preslikavaju se na istu stream jezgru. Na razini procesne jedinice izgledat će kao da se svaka instrukcija izvršava po četiri ciklusa za 64 radne stavke. Ove 64 radne izvodit će se istovremeno i sinkronizirano, u lock-step načinu rada.

Grupa radnih stavki koje se izvode istovremeno naziva se valom i sadrži do 64 radne stavke. Ako je broj radnih stavki manji od 64, dio stream jezgri neće biti iskorišten u potpunosti.

Više valova sačinjava radnu grupu (Slika 5.2). Valovi se međusobno izmjenjuju, kako bi maskirali vremenski zahtjevne operacije kao što su pristup globalnoj memoriji. Primjerice, kada jedan val mora čekati na izvršenje zahtjeva za dohvatom iz globalne memorije, drugi se može izvoditi dok i on ne naiđe na neku vremenski zahtjevnu operaciju. Zato je preporučljivo da radna grupa sadrži broj dovoljan broj valova da sakrije sve memorijske latencije. Gornja granica na broj radnih stavki u radnoj grupi je 256 – četiri vala. Dok jedan val čeka četiri procesorska ciklusa izvršenje ALU instrukcije, drugi val može se pokrenuti. Korištenje dva vala dovoljno je da sakrije kašnjenje ALU.

Kako je opisano u poglavlju 4.6, na svaku procesnu jedinicu može biti preslikano više radnih grupa, maksimalno 8 njih. Na razini cijelog procesora postoji ograničenje na broj aktivnih radnih grupa, koje za korišteni procesor iznos 496.

Page 85: optimizacija rojem čestica na paralelnim arhitekturama

Slika 5.2 Odnos procesnih jedinica, aktivnih radnih grupa, valova i radnih stavki

Zbog činjenice da se instrukcije unutar pojedinog vala izvode u lock-step načinu rada, potrebno je izbjegavati divergencije u toku radnih stavki koje zahvaćaju samo dio radnih stavki u valu. Primjerice, kada se naiđe na grananje unutar jezgre, ako dio radnih stavki u valu izvršava jednu granu,a drugi dio drugu, u stvarnosti će drugi dio stavki biti deaktiviran (maskiran) i besposlen dok prvi dio izvodi svoju granu. Potom će prvi dio stavki čekati drugi dio da izvrši svoju granu. Sličan princip vrijedi i za programske petlje ako neka radna stavaka unutar vala prolazi kroz veći broj iteracija petlje od svih ostalih, sve ostale radne stavke morat će čekati najsporiju. Ova problematika česta je kod stream procesora (bila je prisutna i kod programiranja na RapidMind platformi). Naredbe grananja, kada je moguće, dobro je zamijeniti ternarnim operatorima (? :), dok je petlje često korisno razmotati.

5.2.1.3 Globalna memorija

AMD Radeon HD 5850 grafički procesor opremljen je s 1 GB DDR5 radne memorije kojoj se pristupa preko osam memorijskih kontrolera ili kanala. Svaki kanal ima širinu 32 bita (ukupna širina sabirnice prema memoriji iznosit će 8 * 32 = 256 bita) i spojen je na više memorijskih blokova (eng memory bank). Ova memorija predstavlja globalnu memoriju u OpenCL memorijskom modelu. Kada radna stavka pristupa podatku u globalnoj memoriji ona šalje zahtjev odgovarajućoj sklopovskoj jedinici. Navedena sklopovska jedinica preuzima tu radnu stavku od procesne jedinice stavlja ju u stanje čekanja. U međuvremenu druge radne stavke mogu se izvršavati na oslobođenom mjestu u procesnoj jedinici. Na ovaj način skriva se visoko vrijeme odziva koje karakterizira globalnu memoriju i koje iznosi 300 do 600 procesorskih ciklusa. Ako se uzme u obzir da val na procesnoj jedinici izvršava jednu VLIW instrukciju unutar četiri procesorska

Page 86: optimizacija rojem čestica na paralelnim arhitekturama

ciklusa, biti će potrebno izvršiti 75 do 150 instrukcija kako bi se maskiralo jedno pristupanje globalnoj memoriji.

AMD-ova implementacija OpenCL norme na programskoj razini ograničava veličinu pojedinog OpenCL memorijskog objekta na 128 MB.

Kako bi se ubrzao pristup globalnoj memoriji koriste se priručni spremnici. Slika 5.3 prikazuje sučelje procesora prema globalnoj memoriji. Vidljivo je da svaka procesna jedinica ima 8 kB vlastite priručne memorije prvog stupnja koja je ugrađena u sam procesor i koja se koristi tijekom dohvata podataka. Ova memorija ima propusnost od 49 GB/s prema svakoj procesnoj jedinici, odnosno 881 GB/s na razini cjelokupnog grafičkog procesor što je čini šest puta bržom od globalne memorije (144 GB/s). Svaki memorijski kanal ima i 512 kB drugostupanjske priručne memorije koja prema prvostupanjskoj priručnoj memoriji ima propusnost 392 GB/s. Prilikom zapisivanja u globalnu memoriju također se koristi priručna memorija (WC – eng. Write Cache).

Ako se tijekom pisanja u globalnu memoriju koriste širine manje od 32 bita ili ako se koriste nedjeljive operacije (atomic functions), radne stavke pristupat će memoriji na drugačiji način (eng. complete path) i zbog toga će propusnost prema memoriji biti manja. Sve jezgre koje se koriste unutar ovog rada koriste brži pristup memoriji (eng. fast path), tako da ovaj parametar ne predstavlja faktor u analizi korištenih jezgri.

Slika 5.3 Pristup radnoj memoriji

Page 87: optimizacija rojem čestica na paralelnim arhitekturama

Pristup globalnoj memoriji optimiran je za prijenos vektorskih podataka (128 bita) i zato će baratanje s ovom širinom podatka davati puno bolje rezultate prilikom čitanja i pisanja u globalnu memoriju.

Ako su dva pristupa globalnoj memoriji proslijeđena istom kanalu ili istom memorijskom bloku biti će izvršeni slijedno, jedan za drugim. Opisana pojava naziva se memorijskim konfliktom. Kanalni (eng. channel conflict) odnosno blokovski (eng. bank conflict) konflikti imaju velik utjecaj na performanse, a s programerskog stajališta nastupaju kada radne stavke ne pristupaju slijedno memoriji (primjerice, ako dvije susjedne radne stavke pristupaju elementima u memoriji koji su udaljeni jedna od drugog za neku potenciju broja 2) Takva situacija česta je kada je radni prostor višedimenzionalan. Za udaljenosti elementa koje predstavljaju velike potencije broja dva, dešavaju se kanalni konflikti, a za još veće potencije broja dva nastupaju i blokovski konflikti. Konflikti se izbjegavaju preslagivanjem načina na koji jezgre pristupaju memoriji. Kada više radnih stavki pristupa istoj memorijskoj adresi, također dolazi do memorijskog konflikta. U ovom slučaju isplativije je da jedna radna stavka pročita podatak i pohrani ga u lokalnu memoriju. Potom će ostale radne stavke dohvaćati taj podatak iz lokalne memorije.

Kod nekih proizvođača (grafičkih) procesora potrebno je koristiti spojena zapisivanja (eng. coalesced writes) kako bi se postigle bolje performanse. Kod AMD-ovih procesora spojeno zapisivanje ne igra veliku ulogu u brzinama izvođenja. Prilikom spojenih zapisivanja, procesna jedinica šalje u jednom procesorskom ciklusu 16 podataka i jednu adresu memorijskom kontroleru. Podatci se zapisuju u memoriju slijedno, počevši od navedene adrese. Ako se ne koriste spojena zapisivanja, procesna jedinica koristi jedan procesorski ciklus za slanje 16 adresa i jedan za slanje 16 podataka prema memorijskom kontroleru. Kako se koriste dva ciklusa, ovakav pristup je dvostruko sporiji, ali, u usporedbi s vremenima koje će radne stavke provesti na čekanju u memorijskim kontrolerima ova vremena su zanemariva.

Unutar jezgri, dobro je grupirati dohvate odnosno zapisivanja u globalnu memoriju. Na taj način sklopovlje će ih lakše kombinirati i obavljati istovremeno (burst način rada).

5.2.1.4 Lokalna memorija

Lokalna memorija (eng. Local Data Storage – LDS) ugrađena je unutar procesne jedinice i ima propusnost od 1762 GB/s na razini cijelog grafičkog procesora, odnosno 98 GB/s na razini procesne jedinice (1.5 GB/s na razini procesnog elementa). Ovo predstavlja za red veličine veću propusnost u odnosu na globalnu memoriju (faktor 12.5). Zato se korištenjem lokalne memorije mogu dobiti puno bolji brzinski rezultati. Lokalna memorija također se može koristiti za komunikaciju i sinkronizaciju među radnim stavkama. Lokalna memorija ne zahtjeva spojena pisanja.

Na AMD Radeon HD 5850 grafičkom procesoru, lokalna memorija podijeljena je na 32 bloka. svaki blok može odraditi po jedan zahtjev za pristup memoriji po procesorskom ciklusu (ukupno 32 zahtjeva po procesorskom ciklusu). Ako više radnih stavki unutar vala pristupa istom bloku, ponovo dolazi do memorijskih konflikata, ali, ako više radnih stavki pristupa istoj adresi, podatak će biti poslan svim radnim stavkama koje su ga tražile bez dodatnih troškova (eng. broadcast).

Page 88: optimizacija rojem čestica na paralelnim arhitekturama

Stream jezgra obavlja provjeru postojanja memorijskih konflikata na razini lokalne memorije svakih pola vala (dva procesorska ciklusa). Ako dođe do memorijskog konflikta, val će čekati njegovo razrješenje i neće se dogoditi prebacivanje na novi val kako bi se prikrio zastoj.

Kako svaka radna stavka može generirati do pet podinstrukcija po procesorskom ciklusu, a svaka od njih zahtijevati će jedan do tri operanda (ukupno 5 do 15 operanada po radnoj stavci po procesorskom ciklusu), postoji mogućnost da propusnost lokalne memorije od 8 B/s po sekundi neće biti dovoljna. U tom slučaju potrebno je pokušati preurediti jezgru da koristi privatnu memoriju ili memoriju za konstante.

Za razliku od sučelja prema globalnoj memoriji, sučelje prema lokalnoj memoriji optimirano je za dohvaćanje podataka širine 64 bita (primjerice float2).

Tablica 5.3 Ograničenje broja valova zbog količine alocirane lokalne memorije

Količina lokalne memorije alocirana po radnoj grupi (kB)

Maksimalan broj radnih jedinica po procesnoj jedinici

Maksimalan broj valova po procesnoj jedinici

< 4.0 8 32

4.0 – 4.6 7 28

4.6 – 5.3 6 24

5.3 – 6.4 5 20

6.4 – 8.0 4 16

8.0 – 10.7 3 12

10.7 – 16.0 2 8

16.0 – 32.0 1 4

Svaka procesna jedinica opremljena je s 256 kB lokalne memorije. Iz tog područja za svaku radnu grupu koja se preslikava na procesnu jedinicu alocira se jednaka količina memorije. Ako radna grupa zahtjeva prevelike količine lokalne memorije, maksimalan broj radnih grupa koje je moguće preslikati na pojedinu procesnu jedinicu biti će ograničen, što može za posljedicu imati nedostatak mogućnosti maskiranja ALU i memorijskih kašnjenja. Ovisnost broja radnih grupa po procesnoj jedinici o količini lokalne memorije koja je potrebna po radnoj grupi prikazuje Tablica 5.3. U ovakvim situacijama preporučuje se, ako je moguće, korištenje privatne memorije ili memorije za konstante. Ponekad je poželjna situacija u kojoj se alokacijom prevelike količine lokalne memorije ograničava broj valova koji se mogu pokrenuti na procesnoj jedinici. Primjerice, slučaj u kojem jedan val povuče podatke u priručnu memoriju, ali dolaskom sljedećeg oni bivaju prebrisani – ograničenje od samo jednog vala po procesnoj jedinici moglo bi odnijeti bolje rezultate u ovakvoj situaciji.

Page 89: optimizacija rojem čestica na paralelnim arhitekturama

5.2.1.5 Privatna memorija

Privatna memorija na AMD Radeon HD 5850 grafičkom procesoru nije fiksno dodijeljena nekoj stream jezgri ili procesnom elementu: svaka procesna jedinica posjeduje skupinu (eng. pool) registara opće namjene (eng. General Purpose Registers – GPR) koji se prilikom pokretanja jezgre dodjeljuju radnim stavkama kao privatna memorija. Registri su širine 128 bita, a ukupno ih ima 16384 (256 kB privatne memorije po procesnoj jedinici; 4.5 MB na grafičkom procesoru). Propusnost privatne memorije izrazito je visoka i za korišteni grafički procesor iznosi preko 10 TB/s (faktor ubrzanja veći od 73).

Kao i kod lokalne memorije, i u ovom slučaju može doći do iscrpljivanja zaliha dostupne memorije: ako se alocira previše registara po radnoj stavci broj valova koje je moguće pokrenut na procesnom elementu biti će ograničen. Ovaj efekt prikazuje Tablica 5.4. U slučaju da je nema dovoljan broj dostupnih registara, sustav će automatski napravit preljev u radnu memoriju – radna memorija koristi će se da se pohrane one vrijednosti koje ne stanu u privatnu memoriju. Ovo će imati veliki utjecaj na brzinu izvođenja. Ako su dimenzije radne grupe poznate unaprijed, ova informacija može se ugraditi u OpenCL C programski kod jezgre. Prevoditelj u tom slučaju neće morati ostavljati prostora za najgori mogući slučaj.

Tablica 5.4 Ograničenje broja valova zbog količine alocirane privatne memorije

Količina registara po radnoj stavci

Maksimalan broj valova po procesnom elementu

0-1 248

2 124

3 82

4 62

5 49

6 41

7 35

8 31

9 27

10 24

11 22

12 20

13 19

14 17

15 16

Page 90: optimizacija rojem čestica na paralelnim arhitekturama

16 15

17 14

18 – 19 13

19 – 20 12

21 – 22 11

23 – 24 10

25 – 27 9

28 – 31 8

32 – 35 7

36 – 41 6

42 – 49 5

50 – 62 4

63 – 82 3

83 – 124 2

5.2.1.6 Memorija za konstante

Različite memorije za konstante mogu se s programske strane iskoristi na više načina. Ako su adrese podataka koji se isključivo čitaju konstante poznate u vrijeme prevođenja jezgre, podatci će biti prebačeni u sklopovsku priručnu memoriju ukupnog kapaciteta 40 kB i propusnosti 3.5 TB/s. U slučaju da svi podatci pristupaju istoj konstantnoj adresi, koristi će se prvostupanjska priručna memorija opisana u poglavlju 5.2.1.3. Svi ostali slučajevi koristi će globalnu memoriju za pohranu konstanti.

Memorijski objekti tipa slike (image) također koriste priručnu memoriju prvog i drugog stupnja za ubrzanje dohvata podataka.

Sklopovska akceleracija također će se dogoditi u slučajevima kada je veličina niza unaprijed poznata. U tim slučajevima, ponovno će se koristiti (16 kB) sklopovske priručne memorije za konstante.

5.3 Korišteni programski alati

Razvoj OpenCL aplikacija temeljen je na C++ programskom jeziku (za razliku od prethodno spomenutih RapidMind aplikacija). Tijekom razvoja većinom se oslanjalo na razvojno okruženje Visual Studio 2010 Professional Edition. Osim navedenog, korišten je i AMD APP (prije se nazivao ATI Stream) SDK v2.3 – čiji je najvažniji dio AMD-ova implementacije OpenCL 1.1 standarda. AMPD APP također dolazi s velikim brojem korisnih primjera, bibliotekama funkcija (BLAS,

Page 91: optimizacija rojem čestica na paralelnim arhitekturama

FFT). i odgovarajućim zaglavljima (eng. headers) za pozivanje OpenCL funkcionalnosti iz C i C++ programskih jezika.

Same jezgre pisane su u zasebnoj aplikaciji koja se isporučuje sa AMD APP-om: StreamKernelAnalyzer 1.7.815. Ova aplikacija prvenstven je namijenjena podršci prilikom razvoja za AMD-ove grafičke procesore. Osim OpenCL-a, podržava i Brook+ (stariji programski jezik na razini HLSL koji je svojedobno konkurirao RapidMind platformi, a namijenjen prvenstveno za programiranje stream procesora) i razvoj programa za sjenčanje (eng. shadera) u IL (AMD-ov Intermediate Language – jezik namijenjen za apstrakciju sklopovlja za sjenčanje vertex-a, pixela i geometrije; po složenosti služi kao premosnica između viših jezika poput Brook+ i HLSL i assemblerskih jezika). Centralni dio aplikacije predstavlja uređivač teksta s podrškom za označavanje sintakse podržanih jezika. Osim toga, za podržane grafičke procesore moguće je dobiti apriori analizu napisanih jezgri, sličnu onoj kakva se dobiva kod niže opisane ATI Stream Profiler aplikacije. Naravno, u ovakvoj analizi nije moguće predvidjeti neke stavke koje korisnik određuje dinamički prije pokretanja jezgre, a koje mogu bitno utjecati na ponašanje jezgri (primjerice broj ponavljanje petlji, broj instanci jezgre…). Ipak i ovakva analiza često se pokazala korisnom tijekom razvoja. StreamKernelAnalyzer također prikazuje prijevod napisanog koda u assemblerski jezik ili IL, a sposoban je i označavati i ispisivati uzrok grešaka i upozorenja koja prevodilac vraća tijekom prevođenja jezgri.

Za aposteriori analizu ponašanja same OpenCL platforme i sklopovlja na kojem se izvršavaju OpenCL jezgre korišten je dodatak ATI Stream Profiler, kojeg tvrtka AMD isporučuje uz AMD APP. Ova aplikacija ostvarena je kao dodatak (eng. plug-in) Visual Studio razvojnom okruženju i služi za prikupljanje, analizu i pohranjivanje podataka s grafičkog procesora na kojem se izvodi OpenCL ili DirectCompute aplikacija. Među ostalim ATI Stream Profiler sposoban je prikazati sve pozive AMD ovoj implementaciji OpenCL norme i vizualno dočarati tijek izvođenja aplikacije (Slika 8.11). Za OpenCL jezgre koje izvode na odgovarajućim AMD-ovim grafičkim procesorima, moguće je dobiti detaljnu analizu kako je jezgre bila izvedena. Ova analiza uključuje razne podatke kao što su stupanj vektorizacije (vidjeti poglavlje 5.2.1.1), načini (brzi ili puni – poglavlje 5.2.1.3) i frekventnost pristupa lokalnoj i globalnoj memoriji (također učestalost konflikata prilikom pristupanja pojedinim vrstama memorije), količine registara koju svaka instanca jezgre koristi, broj dretvi (eng. thread) i valova koji su bili pokrenuti, postotak vremena koji je ALU bio zauzet, itd. Ovakvi podatci pokazali su se iznimno korisni prilikom uklanjanja uskih grla u jezgri.

Korišteni upravljački programi (deriver) za grafički procesor su ATi Catalyst 11.2.

5.4 Sklopovlje i razvojno okruženje za RapiMind platformu

Prilikom razvoja i mjerenja vezanih uz RapidMind platformu korišteno je Microsoft Visual Studio 2005 razvojno okruženje MSDNa pod licencom u sprezi s RapidMind v2.1.0. platformom, u to vrijeme dostupnom besplatno za nekomercijalnu uporabu (mogla se preuzeti s foruma namijenjenih podršci RapidMind korisnicima). Korišteni operacijski sustav bio je Microsoft Windows Xp Home Edition, a verzija upravljačkih programa grafičke kartice je 8.453-080122a-060293C. Razvoj je vršen u C/C++ programskom jeziku, s time da je naglasak bio stavljen na neobjektno programiranje odnosno C programski jezik.

Page 92: optimizacija rojem čestica na paralelnim arhitekturama

Mjerenja su vršena na HP-Compaq nx9420 prijenosnom računalu s Intel Core II Duo (dvojezgrenom) procesoru na frekvenciji radnog takta od 1.66 GHz. Računalo je opremljeno sa 2.5 GB DDR2-SDRAM-a koji radi na frekvenciji 333 MHz i čvrstim diskom kapaciteta 80 GB. Grafički podsustav je ATi Mobility Radeon X1600 čija svojstva prikazuje Tablica 5.1.

5.5 Uvjeti tijekom testiranja Sva mjerenja brzine izvođenja pojedinih algoritama obavljana su na netom pokrenutom operacijskom sustavu, na računalima koja nisu spojena na bilo kakvu računalnu mrežu. Tijekom mjerenja, antivirusni alati i ostale aplikacije bile su isključene. Za potrebe samog mjerenja korištena je, za tu svrhu posebno napisana C++ klasa nazva Timer koja prolazak vremena određuje na temelju broja procesorskih ciklusa i poznate frekvencije procesorskih ciklusa. Svi programi pokretani u “Realese“ verziji više puta uzastopce, kako bi se moglo izračunati prosječno vrijeme izvođenja u više pokretanja.

Page 93: optimizacija rojem čestica na paralelnim arhitekturama

6. Optimizacija rojem čestica

Optimizacija rojem čestica (eng. Particle Swarm Optimization – PSO) je metoda optimizacije prvotno predložena 1995. godine od Jamesa Kennedya i Russella Eberharta [16] te Yuhui Shi-a [17] (1998). Inicijalno je osmišljena radi modeliranja društvenog ponašanja u rojevima ili jatima, ali je pojednostavljivanjem prerasla u optimizacijsku metodu za kontinuirane nelinearne funkcije baziranu na “inteligenciji roja“ (eng. swarm inteligence). Sam algoritam metode je relativno jednostavan za implementaciju, pogodan za paralelizaciju i na razini podataka i na razini zadataka i sposoban relativno brzo naći rješenje za mnoge optimizacijske probleme.

Postupak optimizacije temelji se na iterativnim pokušajima poboljšanja potencijalnih rješenja čija se kvaliteta procjenjuje temeljem dane evaluacijske funkcije. Potencijalna rješenja, koja se u terminologiji ovog algoritma nazivaju česticama, su vektori čija dimenzionalnosti odgovara dimenzionalnosti funkcije koja se optimira. Sva potencijalna rješenja sačinjavaju populaciju rješenja odnosno roj čestica. Populacija se na početku izvođenja algoritma inicijaliziran na nasumične vrijednosti koje predstavljaju početna, suboptimalna rješenja. Osnovna zamisao algoritma je da se svako rješenje iterativno pokušava poboljšati na temelju nekog vlastitog gibanja koje čestica posjeduje, ali i na temelju kvaliteta koje posjeduju ostala rješenja odnosno čestice u roju (heuristička komponenta algoritma). Zato se za svaku česticu osim vektora koji predstavlja položaj čestice u prostoru rješenja, pohranjuje i vektor koji odgovara brzini kojom se čestica giba u tom prostoru. Vektor brzine čestice također biva inicijaliziran na nasumične vrijednosti, a kasnije se u svakoj iteraciji ažurira tako da se u obzir uzme povijest dotične čestice i najbolje do tada pronađeno rješenje unutar cjelokupne populacije. Pri tome se određeni udio prethodnog vektora brzine čestice zadržava kako bi se simulirala inercija čestice. Očito je da će za svaku česticu biti potrebno pohraniti vektor koji opisuje najbolji položaj čestice, kao i vrijednost evaluacijske funkcije za taj položaj. Na globalnoj razini biti će potrebno pohranjivati i najbolji rezultat cjelokupne populacije.

( ) ( )t

i

t

g

t

i

t

ip

t

i

t

i xgUxpUvv −⋅+−⋅+⋅=+ ϕϕω1 (6.1)

Formalno, roj sadrži S čestica opisanih sa vektorima položaja xi i brzine vi u N-dimenzionalnom prostoru rješenja. Za svaku česticu pamti se položaj pi koji predstavlja najbolji pronađeni položaj čestice (s obzirom na vrijednost evaluacijske funkcije f(x) u tom položaju). g predstavlja najbolji pronađeni položaj na razini cijeloga roja. Neka je prostor pretraživanja ograničen s vektorima blo i bhi. Uz danu nomenklaturu osnovni algoritam optimizacije rojem čestica prikazuje Pseudokod 6.2, u kojem U(a, b) označava funkciju koja vrača slučajnu vrijednost uniformne distribucije sa intervala [a, b].

Page 94: optimizacija rojem čestica na paralelnim arhitekturama

Za svaku česticu i = 1 do S (1)

Za svaku dimenziju j = 1 do N (2)

xi, j ← U(blo, j , bhi, j) (3)

vi, j ← U(–|bhi, j – blo, j|, |bhi, j – blo, j|) (4)

pi, j ← xi, j (5)

ako je (f(xi) < f(g)) ili ako je i = 1 (6)

Za svaku dimenziju j = 1 do N (7)

gj ← xi, j (8)

Pseudokod 6.1 Inicijalizacija vrijednosti u algoritmu za optimizaciju rojem čestica

Pseudokod 6.1 prikazuje inicijalizaciju početnog stanja roja. Vidljivo je da se tijekom inicijalizacije pazi da čestice budu smještene unutar intervala [blo , bhi]. Neke verzije algoritma tijekom svake iteracije provode dodatne provjere jesu li čestice dospjele izvan zadanog dijela prostora. Nakon svakog osvježavanja pozicije čestice, ako je čestica u nekoj dimenziji prešla ograničenja zadana za dotičnu dimenziju, vrijednost položaja čestice u toj dimenziji biti će postavljena na vrijednost zadanog ograničenja (eng. clamp). Dodatno, iznos brzine moguće je ograničiti na raspon koji definiraju donja i gornja granica [18]. Ovakvim ograničavanjima prostora pretraživanja izbjegava se nepotrebno pretraživanje a postoji i mogućnost da se pretraživanje prostora rješenja ograniči na neko područje interesa, primjerice na neki lokalni minimum ili maksimum.

Iz priloženog pseudokoda vidljivo je da će se čestica projicirati prema određenim točkama ili, da ju određene točke privlače. Faktori φp i φg određuju koliko će česticu privlačiti njezino najbolje rješenje odnosno globalno najbolje rješenje. Očito je da najbolje globalno rješenje služi kao svojevrsno sredstvo komunikacije među česticama. Ono je vidljivo svim česticama. Čestica koja je pronašla globalno najbolje rješenje može se zbog inercije nastaviti gibati nekim svojim putem, dok će položaj u prostoru rješenja koji predstavlja najbolje rješenje ostati zapamćen. Ako se pretpostavi da je čestica pronašla najbolje rješenje jer se gibala po nekom gradijentu, nastavak gibanja mogao bi pronaći još bolje rješenje u blizini. Najbolje rješenje privlačit će ostale čestice u svoju okolicu (pogotovo onu koja je i pronašla trenutno globalno najbolje rješenje jer će kod te čestice vektor p biti jednak vektoru g), omogućujući tako detaljnije pretraživanje u okolici najboljeg pronađenog rješenja (koje nije nužno globalni optimum). Ova pojava naziva se zgušnjavanjem ili zbijanjem roja. Vidljivo je da optimizacija rojem čestica zapravo u osnovni algoritma ima ugrađen elitizam – pamćenje najboljeg pronađenog rješenja bez obzira kako se populacija mijenja. Elitizam se kod sličnih optimizacijskih metoda kao što su genetski algoritmi mora zasebno implementirati. Faktor ω određuje koliko će čestica biti konzistentna u svome gibanju.

Page 95: optimizacija rojem čestica na paralelnim arhitekturama

Dok (kriterij zaustavljanja nije zadovoljen) ponavljati (1)

Za svaku česticu i = 1 do S (2)

Za svaku dimenziju j = 1 do N (3)

rp ← U(0, 1) (4)

rg ← U(0, 1) (5)

vi, j ← ω * vi, j + (6)

+ φp * rp * (pi, j – xi, j) +

+ φg * rg * (gj – xi, j)

xi, j ← xi, j + vi, j (7)

ako je (f(xi) < f(pi)) (8)

Za svaku dimenziju j = 1 do N (9)

pi, j ← xi, j (10)

ako je (f(xi) < f(g)) (11)

Za svaku dimenziju j = 1 do N (12)

gj ← xi, j (13)

Pseudokod 6.2 Optimizacija rojem čestica

Kriteriji zaustavljanja koje Pseudokod 6.2 navodi u retku (1), mogu biti višestruki. U ovom radu korištena su tri najčešća. Prvi je dosezanje određenog broja iteracija optimizacijske faze algoritma. Ako se najbolje pronađeno rješenje ne promjeni određeni broj iteracija optimizacijske faze algoritma, proces optimizacije se zaustavlja. Najčešće se potom algoritam ponovno pokreće , jer se pretpostavlja da je optimizacijski postupak “zapeo“ u lokalnom ekstremu. Treći najčešći kriterij zaustavljanja nastupa kad najbolje pronađeno rješenje dosegne neku predefiniranu vrijednost.

( ) ( )( )t

i

t

g

t

i

t

ip

t

i

t

i xgUxpUvv −⋅+−⋅+⋅⋅=+ ϕϕωχ1 (6.2)

( )

−⋅+⋅= ∑

=

+S

j

t

i

t

jj

t

i

t

i xpUvv1

1 ϕχ (6.3)

Kriteriji zaustavljanja koje Pseudokod 6.2 navodi u retku (1), mogu biti višestruki. U ovom radu korištena su tri najčešća. Prvi je dosezanje određenog broja iteracija optimizacijske faze algoritma. Ako se najbolje pronađeno rješenje ne promjeni određeni broj iteracija optimizacijske faze algoritma, proces optimizacije se zaustavlja. Najčešće se potom algoritam ponovno pokreće , jer se pretpostavlja da je optimizacijski postupak “zapeo“ u lokalnom ekstremu. Treći najčešći kriterij

Page 96: optimizacija rojem čestica na paralelnim arhitekturama

zaustavljanja nastupa kad najbolje pronađeno rješenje dosegne neku predefiniranu vrijednost.

Dosada su izvedene mnoge modifikacije verzije ove metode, a većina ih se zasniva na modificiranju formule za ažuriranje brzine (jednadžba (6.1), odnosno Pseudokod 6.2 Optimizacija rojem česticaPseudokod 6.2, (6) redak). U nekim modificiranim verzijama algoritma faktori φp, φg i ω dinamički se mijenjaju tijekom algoritma smanjujući tako utjecaj pojedine komponente brzine sa zgušnjavanjem jata. U [19] Shi i Eberhart predlažu smanjivanje faktora inercije a vremenom. Pedersnen je u [20] postavio φp na vrijednost nula, nazvavši takav pristup MLO (eng. Many Optimizing Liaisons). Neke verzije algoritma koriste informacije o drugim česticama, pa tako Mendes u [21] koristi jednadžbu (6.3) za ažuriranje vrijednosti brzine. Clerc and Kenedy [22] uvode fiksni faktor sužavanja X prikazan u jednadžbi (6.2). Radi smanjenja zahtjevnosti, neke verzije ažuriraju poziciju samo određenog postotka čestica po iteraciji. Postoje i mnoge druge modifikacije koje (primjerice pretraživanje diskretnih prostora rješenja) koje omogućuju ovoj optimizacijskoj metodi široku primjenu.

Page 97: optimizacija rojem čestica na paralelnim arhitekturama

7. Implementacija

Uspješno ostvarivanje potpune paralelizacije metode optimizacije rojem čestica zahtjeva prethodnu paralelnu implementaciju nekoliko algoritama. Potrebno je ostvariti paralelno pretraživanje roja za vektorom g. Nadalje, dotični optimizacijski algoritam na nekoliko mjesta zahtjeva generiranje pseudo-slučajnih brojeva. Radi lakšeg programiranja odlučeno je da još neki elementi algoritma budu implementirani kao nezavisne funkcije te da se potom sam algoritam metode optimizacije rojem čestica jednostavno sastavi od već provjerenih elemenata. Među spomenute građevne elemente algoritma spadaju većinom jednostavne funkcije kao što su paralelno zbrajanje, množenje ili oduzimanje većeg broja elemenata. Opisana funkcionalnost objedinjena je u klasu koja pruža osnovnu funkcionalnost rada s matricama.

Ovakav pristup u kojem se algoritam sastavlja od jednostavnijih algoritama ima nekoliko mana. Vrijeme razvoja je dulje, osim ako se ne koriste već gotove komponente. U odnosu na slučaj u kojem je sva funkcionalnost sažeta u malom broju jezgri, algoritam će se izvoditi sporije zbog više slijednog programskog koda koji poziva i sinkronizira jezgre, kao i zbog većih potreba za prebacivanjem podataka duž memorijske hijerarhije. Unatoč manama ovakav pristup osigurava brži i lakši razvoj jednom kada su sve komponente ostvarene. Dodatna prednost ovakvog pristupa je i mogućnost ponovnog iskorištavanja ostvarene funkcionalnosti: i matrične operacije i generiranje pseudo-slučajnih nizova brojeva i operacije redukcije predstavljaju široko korištene operacije.

Na posljetku je odlučeno da će tražena funkcionalnost biti ostvarena u tri sloja. Slika 7.1 prikazuje zamišljenu strukturu programa; svjetlije strelice naznačuju mogućnosti pristupa nižim slojevima koji nisu korišteni u ovom diplomskom radu, ali bi inače bili mogući u nekoj drugoj aplikaciji (primjerice, glavna aplikacija koja zbraja dvije matrice). Prvi i najniži sloj biti će sučelje prema samoj implementaciji OpenCL norme. Ovo sučelje biti će ostvareno kao klasa koja olakšava pristup funkcionalnosti OpenCL norme, djelomično skrivajući rjeđe korištene mogućnosti i postavljajući mnoge parametre funkcija na predefinirane vrijednosti tako da ih se ne mora uvijek unositi. Sučelje je sposobno prilično samostalno stvoriti kontekst i na jednostavan način prevoditi programe i jezgre. U svojim podatkovnim strukturama ono će pohranjivati različite OpenCL objekte kao što su jezgre, događaji ili memorijski objekti i brinuti o njihovom brisanju i kopiranju. Iz glavne aplikacije (ili drugog sloja) tim će se objektima pristupati preko njihovih imena: svaki objekt će imati ime (sučelje ima podršku i za automatsko generiranje imena) preko kojeg će biti referenciran. Tako će jezgra moći biti pozvana jednostavnim navođenjem njezina imena, dimenzija i popisa memorijskih objekata nad kojim se poziva, a samo sučelje će pronaći jezgru i memorijske objekte u svojim memorijskim strukturama, pridružiti memorijske objekte jezgri, parsirati podatke o dimenzijama radnog prostora i na posljetku pokrenuti jezgru.

Drugi sloj sačinjavaju navedene funkcionalnosti reduciranja, generiranja pseudo-slučajnih nizova brojeva i matričnih operacija. C++ klase koje enkapsuliraju ovu funkcionalnost koriste se prvim slojem kako bi je ostvarili.

Page 98: optimizacija rojem čestica na paralelnim arhitekturama

SUČELJE PREMA OPENCL-U

cLCG REDUKCIJE MATRICA

PSO

OPENCL PROGRAMSKO OKRUŽENJE

GLAVNA APLIKACIJA

TREĆI SLOJ

DRUGI SLOJ

PRVI SLOJ

OPENCL PLATFORMA

OPENCL-KOMPATIBILNO SKLOPOVLJE

Slika 7.1 Građa aplikacije

Treći sloj je sam algoritam metode optimizacije rojem čestica koji se većinom oslanja na funkcionalnost drugog sloja, iako na nekim mjestima komunicira izravno s prvim slojem.

Većina algoritama optimirana je za grafički procesor. Za potrebe mjerenja i usporedbe svaka važnija klasa ili funkcija ima svoju “slijednu“ inačicu.

7.1 Operacija redukcije

Osnovna ideja redukcijskih operacija temeljena je na algoritmu koji se isporučuje s AMD-ovom implementacijom OpenCL norme. Algoritam obavlja opetovano smanjivanje problema na razini radne grupe. Svaka radna grupa prebacuje iz globalne u lokalnu memoriju blok podataka. Veličina bloka odgovara broju radnih stavki u radnoj grupi (označeno s M) i mora biti potencija broja dva. Po završetku kopiranja u lokalnu memoriju, obavlja se sinkronizacija radnih stavki u radnoj grupi (pomoću OpenCL C funkcije barrier spomenute u poglavljima 4.5 i 4.8) i svaka

Page 99: optimizacija rojem čestica na paralelnim arhitekturama

radna stavka ostaje s vrijednošću koju je kopirala iz globalne u lokalnu memoriju pohranjenom u privatnoj memoriji u vidu varijable.

Slika 7.2 Redukcija implemtirana u OpenCL-u

U sljedećoj fazi algoritam koristi programsku petlju koja će se pokrenuti log2(M) puta. Svaka iteracija programske petlje deaktivira polovicu radnih stavki, tako da će svakim ulaskom u programsku petlju stavke koje su ostale aktivne izgubit po jednog desnog susjeda. Aktivne radne stavke tada dohvaćaju iz lokalne memorije podatke koje je tamo zapisala sada neaktivna polovica radnih stavki. Na dohvaćeni podatak i podatak koji je već pohranjen u privatnoj memoriji, primjenjuje se neki redukcijski operator čiji se rezultat ponovo zapisuje u lokalnu memoriju kako bi ga, po potrebi, mogle dohvatiti radne stavke koje će u sljedećoj iteraciji ostati aktivne (Slika 7.2). Na kraju svake iteracije obavlja se sinkronizacija na razini radne grupe, kako bi se osiguralo da su sve radne stavke obavile

Page 100: optimizacija rojem čestica na paralelnim arhitekturama

zapisivanje u lokalnu memoriju: ako se radna grupa sastoji od više valova, situacija u kojoj će radna stavka iz prvog vala pokušati dohvatiti iz lokalne memorije podatke koje radna stavka iz posljednje vala nije još zapisala je moguća, pogotovo zato što u svakoj iteraciji preostale aktivne radne stavke dohvaćaju sve udaljenije podatke, kao što prikazuje Slika 7.2. Na posljetku će samo jedna radna stavka u radnoj grupi ostati aktivna. Ona će u svojoj privatnoj memoriji sadržavati rezultat redukcije primijenjene na učitani blok. Ovaj rezultat zapisuje se u globalnu memoriju. U odnosu na originalni AMD-ov algoritam, uvedene su neke manje promjene u kontroli broja ponavljanja i uvjetima izlaska iz petlje. Kako bi se omogućilo rekurzivno reduciranje do samo jedne vrijednosti koriste se skalarni umjesto vektorskih tipova (ovo je također olakšalo rad s pronalaženjem indeksa kod traženja najmanjeg i najvećeg elementa niza). Povećanje zrnatosti koje prikazuje Slika 3.4 provodilo bi se tako da se u svakoj iteraciji petlje više od pola (primjerice 2 / 3 ili 3 / 4 radnih stavki) deaktivira.

Opisani postupak reducira blok veličine M na jednu vrijednost. Za ukupni broj podataka N broj radnih grupa biti će N/M. Navedena vrijednost također predstavlja i broj dobivenih elemenata koji će biti potrebno dalje reducirati u idućem pozivu redukcijske jezgre.

Jezgra za redukciju prilagođena izvođenju na glavnom procesoru napisana je tako da svaka radna stavka slijedno prolazi kroz svoj dio podatka i nad njime obavlja redukciju te rezultat zapisuje u globalnu memoriju.

Ostvarene su OpenCL jezgre za šest vrsta redukcija: sumiranje elemenata niza (SUM), produkt elemenata niza (PRODUCT), najveći element u nizu (MAX), najmanji element u nizu (MIN), broj pojavljivanja navedenog elementa u nizu (COUNT) i provjera postoji li navedeni element u nizu (EXISTS). Navedene jezgre rade nad nizovima koji koriste cjelobrojni tip podataka ((u)int) ili tip podataka s pomičnim zarezom (float). Ostvarene jezgre po namjeni su jednake redukcijama ugrađenima u RapidMind platformu. Sve jezgre namijenjene su instanciranju u jednodimenzionalni radni prostor.

Na strani glavne aplikacije, funkcija koja pokreće odgovarajuću redukcijsku jezgru, poziva se rekurzivno sve dok se cjelokupni niz ne reducira na jednu vrijednost. Izlazni niz prethodnog poziva jezgre predstavlja ulaz u jezgru prilikom sljedećeg poziva unutar istog rekurzivnog spusta. Ako kardinalnost početnog niza elemenata ne odgovara potenciji broja 2, niz će biti rekurzivno dijeljen sve dok dobiveni podnizovi nemaju odgovarajuću veličinu. Postoji mogućnost zaustavljanja rekurzije prije nego što dođe do najdublje razine (primjenjivo u slučaju potrebe za sumama dijelova niza)

Jezgre za traženje najvećeg (MAX) odnosno najmanjeg (MIN) elementa u nizu također određuju i redni broj pronađenog elementa. Kako svaka stavka na početku jezgre učitava po jedan element, redni broj tog elementa biti će jednak položaju te radne stavke u globalnom radnom prostoru. Jezgre prilikom usporedbe elemenata koriste znakove “≤“ odnosno “≥“ (umjesto “<“ odnosno “>““). Zato će, u slučaju jednakosti uspoređivanih elemenata jezgra odabirati lijevi element (onaj s manjim rednim brojem u nizu). U glavnu aplikaciju ove jezgre vraćati će niz s pronađenom vrijednošću i niz s indeksom (rednim brojem) dotične vrijednosti. Kako je izlazni niz jedne razine rekurzije ulazni niz za sljedeću, niz s indeksima sadržavati će točne vrijednosti samo za prvu razinu rekurzije. Na drugoj razini pronađeni indeks odnosit će se na reducirani niz iz prethodne razine rekurzije. Zato se prilikom

Page 101: optimizacija rojem čestica na paralelnim arhitekturama

povratka na višu razinu rekurzije, pronađeni indeks koristi kako bi se dohvatila vrijednost iz niza indeksa na toj razini.

Funkcija COUNT u prvom prolazu uspoređuje elemente niza sa zadanim elementom i u lokalnu memoriju upisuje jedan u slučaju jednakosti odnosno nula u slučaju nejednakosti. Ostatak jezgre identičan je jezgri za zbrajanje elemenata niza. Nakon prvog prolaza koristi se funkcija SUM za zbrajanje rezultata.

Funkcija EXISTS jednostavno vrača vrijednost usporedbe funkcije COUNT s nulom. U slijednoj izvedbi funkcija EXISTS osjetno je učinkovitija jer je nakon prvog pojavljivanja traženog elementa sposobna prekinuti daljnju pretragu niza.

7.2 Generator pseudo-slučajnih brojeva Korišteni generator pseudo-slučajnih brojeva gotovo je sasvim jednak onome opisanom u poglavlju 3.3. Svaka radna stavka dohvaća vektor u kojem su pohranjena stanja pojedinih komponenti cLCG-a i potom računa slučajne brojeve. Zbog veće preciznosti, obični operator modul dijeljenja pokazao se zadovoljavajućim za ovu primjenu. U ovom slučaju dobiveni brojevi zapisuju se u zaseban niz, dok se stanja cLCG-a pamte u odvojenom nizu. Niz u koji se zapisuju dobiveni pseudo-slučajni brojevi može bit građen od skalarnih ili vektorskih elemenata (dimenzionalnosti 4). Korištenje vektorskih elemenata preporučljivo je jer povećava i memorijsku propusnost i količinu ALU instrukcija u odnosu na broj pristupa (globalnoj) memoriji.

Inicijalizacija se obavlja na temelju položaja instance jezgre u radnom prostoru. Ova vrijednost se skalira i translatira za neke slučajne vrijednosti generirane u glavnom programu. Tako se postiže adekvatan razmak između susjednih instanci jezgre. Potom će svaka instanca jezgre unutar programske petlje nekoliko stotina puta uzastopce generirati pseudo-slučajni broj. Broj iteracija petlje također je slučajna vrijednost generirana u glavnom programu. Dobiveno stanje cLCG zapisuje se u niz. Vidljivo je da je i inicijalizacija generatora pseudo-slučajnih brojeva slična rješenju korištenom prilikom ispitivanja RapidMind platforme.

7.3 Osnovne matrične operacije U sklopu rada implementirane su dvije osnovne vrste matričnih operacija. Prva vrsta su jednostavnije matrične operacije koje djeluju neovisno, na razini pojedinih komponenti matrice. Primjer ovakve operacije bilo bi zbrajanje matrica ili množenje matrice s konstantom. Također su uvedene i neke druge, za matrice manje uobičajene operacije kao što su dijeljenje elemenata matrice s elementima druge matrice, ili određivanje apsolutne vrijednosti elemenata matrice. Mnoge funkcije imaju verziju u kojoj rade isključivo s matricama i verziju u kojoj rade i s konstantama. Primjerice zbrajanje matrice s matricom zbrojiti će odgovarajuće elemente matrica (uvjet je da su matrice jednakih dimenzija, u protivnom će operator izazvati iznimku), dok će pribrojavanje konstante matrici uvećat sve elemente matrice za vrijednost priložene konstante. Opisana funkcionalnost ostvarena je uglavnom pomoću jednostavnih jezgri koje učitavaju elemente matrice, primjenjuju operator na njih i zapisuju rezultat. Vidljivo je da će omjer ALU instrukcija u odnosu na broj pristupa (globalnoj) memoriji biti izrazito nepovoljan. Matrice također imaju mogućnost inicijalizacije svojih elemenata na pseudo-slučajne vrijednosti, kao i sposobnost provođenja redukcije nad svojim

Page 102: optimizacija rojem čestica na paralelnim arhitekturama

elementima. Ova funkcionalnost oslanja se na već ostvarene metode opisane u poglavljima 7.1 i 7.2.

Matrično množenje spada u nešto složenije matrične operacije. U ovom radu implementiran je minimalno izmijenjeni algoritam koji dolazi uz AMD-ovu implementaciju OpenCL norme. Algoritam se ne koristi u samom postupku optimizacije rojem čestica, nego se upotrebljava usputno, radi provjere procesorske snage korištenog sklopovlja. Postoje dvije inačice algoritma: jedna namijenjena množenje kvadratnih matrica, a druga množenju matrica koji nisu nužno kvadratne. Potonja je pogodna i za korištenje na procesorima bez lokalne memorije (primjerice na glavnom procesoru). U obje verzije radna stavka računat će šesnaest elementa matrice-produkta matričnog množenja (radi učinkovitijeg pristupa memoriji i ostvarivanja paralelizma unutar same jezgre). Zato sve matrice moraju imati dimenzije koje su višekratnici broja četiri. Potrebno je poznavati sve elemente u odgovarajućem retku i stupcu matrica koje sudjeluju u množenju kako bi se izračunao jedan element produkta. Svaka radna stavka učitavat će iz globalne memorije u više iteracija po dva bloka od 4 * 4 elementa. Jedan blok kretat će se duž retka prvog faktora u operaciji množenja, dok će drugi blok prolaziti duž stupca drugog faktora. Kada se novi blokovi učitaju, odgovarajuće vrijednosti će se pomnoži i pribrojiti vrijednostima rezultata. Na kraju će se dobiveni rezultat zapisati na odgovarajuće mjesto u matrici koja predstavlja produkt.

Algoritam za množenje kvadratnih matrica koristi se i lokalnom memorijom. U njemu se također učitavaju po dva bloka podataka koji se potom pohranjuju u lokalnu memoriju. Ovi blokovi su veći jer svaka radna stavka u radnoj grupi kopira blok od 4 * 4 elementa u lokalnu memoriju. Nakon što su kopirani u lokalnu memoriju, ovim podatcima pristupaju svi elementi radne grupe.

Veliki problem kod matričnog množenja predstavlja određivanje dimenzija radne grupe. Za kvadratne matrice, visina i širina radne grupe moraju biti jednake. Istovremeno moraju biti i djelitelji broja elemenata u retku (ili stupcu) matrice podijeljenom s četiri (jer svaka radna stavka računa šesnaest vrijednosti istovremeno). Radna grupa mora biti što veća kako bi maksimalno iskoristila lokalnu memoriju, a istovremeno postoji ograničenje na broj radnih stavki u grupi (256 radnih stavki za korišteni grafički procesor).

Za množenje nekvadratnih matrica ograničenja su još veća. Radna grupa mora po širini biti djelitelj broja stupaca matrice koja se dobiva kao produkt (podijeljenog s četiri), a također mora biti i djelitelj broja stupaca prvog faktora u matričnom množenju (također, podijeljenog s četiri). Slično vrijedi i za visinu radne grupe – broj redaka u matrici-produktu kao i broj redaka u matrici koja čini drugi faktor u množenju moraju biti višekratnici visine radne grupe pomnožene s četiri. Istovremeno, umnožak visine i širine radne grupe mora bit manji od najvećeg dopuštenog broja radnih stavki u grupi (256). Kod ove inačice algortima množenja također je preporučljivo da broj radnih stavki ne bude premalen (manji od 64), kako stream jezgre u procesnoj jedinici ne bi ostali neiskorišteni.

Pronalaženje rješenja s ovim ograničenjima je moguće (primjerice linearnim programiranjem), ali potrebno je uzeti u obzir velika vremenska ograničenja na traženje optimalnog rješenja. Ne bi bilo dopustivo da vremena potrebna za određivanje dimenzija radne grupe budu sumjerljiva trajanju samog procesa matričnog množenja. Za kvadratne matrice, i visina i širina radne grupe postavljaju

Page 103: optimizacija rojem čestica na paralelnim arhitekturama

se na vrijednost šesnaest i potom se dijele s dva (posmak u desno) dok ne postanu i djelitelji dimenzija matrica podijeljenih s četiri. Za nekvadratne matrice, postupak je osjetno složeniji i uključuje višestruke petlje koje pokušavaju što prije pronaći najbolje rješenje smanjujući inicijalno predložene veličine grupa. Iako u najgorim slučajevima ovaj postupak traje dugo, velike su šanse da najgori slučajevi neće nastupiti.

7.4 Paralelizacija metode optimizacije rojem čestica Sama priroda osnovne verzije metode optimizacije rojem čestica čini dotičnu metodu idealnom za paralelizaciju na razini podataka: kako se svaka čestica giba kroz prostor rješenja gotovo neovisno od ostalih čestica, moguće je proračune za sve čestice odrađivati zasebno. Dodatno, svaka komponenta vektora koji definiraju stanje čestice može se nezavisno računati. Iz navedenog slijedi da je za S čestica u N dimenzionalnom prostoru moguće koristiti S * N paralelnih procesora (odnosno radnih stavki). U većini koraka koje prikzauje Pseudokod 6.1 odnosno Pseudokod 6.2, ovi procesori djelovati će sasvim neovisno – kao trivijalno paralelni zadatci. Ovo prvenstveno vrijedi za korake inicijalizacije položaja, brzine i najboljeg položaja (Pseudokod 6.1, redci (1), (3) i (5)) i korake ažuriranja položaja, brzine i najboljeg položaja čestice (Pseudokod 6.2, redci (6), (7) i (10)). U tim slučajevima koriste se paralelni algoritmi za generiranje nizova pseudo-slučajnih nizova brojeva (poglavlje 7.2) i matrične operacije (poglavlje 7.3). Na ovaj način programske petlje koje iteriraju kroz sve čestice u roju i kroz svaku dimenziju pojedine čestice mogu sve iteracije obavljati istovremeno (eng. loop unrolling ili loop unwinding – jedan od čestih načina (automatske) paralelizacije).

Općenito, za pohranjivanje svih podataka vezanih za čestice koriste se klasa matrica iz poglavlja 7.3. Na početku faze inicijalizacije, matrice koje predstavljaju položaj i brzinu čestice popunjavaju se pseudo-slučajnim brojevima iz zadanog intervala. Svaki redak u matrici predstavlja po jednu česticu, a svaki stupac po jednu dimenziju. Slijedi proces evaluacije čestica.

Evaluacija mora biti implementirana od strane korisnika optimizacijskog algortima i zato apriorne procjene o mogućnostima paralelizacije evaluacijske funkcije nisu moguće. U pravilu, evaluacijska funkcija trebala bi biti sposobna evaluirati svaku česticu neovisno pa bi na razini čestica trebao postojati paralelizam, dok će na razini pojedinih komponenti vektora koji opisuje položaj čestice paralelizam vrlo vjerojatno biti ograničen jer će vrijednost svih čestica morati na neki način utjecati na konačnu vrijednost evaluacijske funkcije (ovo sugerira potrebu za nekom vrstom redukcije negdje u procesu evaluacije). Ipak, kao što je pokazano kroz korištene evaluacijske funkcije, određeni stupnjevi paralelizacije mogući su i pri računanju evaluacije pojedine čestice. Kako je evaluacijska funkcija često vremenski najskuplji dio nekog optimizacijskog algoritma, potrebno je smanjiti broj poziva u najvećoj mogućoj mjeri. Iako se u pseudokodu algoritma optimizacije rojem čestica prikazanom u poglavlju 6 ova funkcija pojavljuje često, činjenica je da ju je potrebno pozivati samo jednom po iteraciji, evaluirajući pri tome cjelokupnu populaciji istovremeno. Nakon inicijalizacije položaja čestica, sve čestice biti će evaluirane i dobivene vrijednosti biti će pohranjene u memoriju u posebnu matricu koja sadrži ocjene kvalitete najboljih položaja čestice. Ova matrica ima S elemenata. Osim nje koristi se još jedna matrica jednakih dimenzija u kojoj će biti pohranjene vrijednosti evaluacijske funkcije za trenutni položaj

Page 104: optimizacija rojem čestica na paralelnim arhitekturama

čestica. Korištenjem dvaju matrica za pohranjivanje rezultata evaluacije ostvaruje se samo jedan poziv evaluacijske funkcije po ciklusu optimizacijske metode, odnosno smanjuje se procesorska složenost povećavanjem memorijskih zahtjeva. Ovime završava faza inicijalizacije i kreće optimizacijska faza algoritma.

U implementaciji algoritma optimizacijska faza započinje pronalaženjem najboljeg i globalno najboljeg rješenja. Razlog ovakvom redoslijedu biti će objašnjen nešto kasnije. Na temelju vrijednosti dobivenih evaluacijom rješenja pronalazi se najbolje rješenje u populaciji. Ovaj postupak koristi se operacijom redukcije opisanom u poglavlju 7.1. Redukcija, pored evaluacije, predstavlja drugu točku algoritma u kojoj nije moguća sasvim neovisna obrada svakog elementa svake čestice. U ovoj točki koristi se redukcijska operacija (MAX ili MIN – ovisno o tome da li se traži maksimum ili minimum funkcije) koja osim kvalitete najboljeg rješenja vraća i njegov redni broj u populaciji, kako bi se vrijednosti vektora položaja mogle izdvojiti.

Ako je riječ o prvoj iteraciji optimizacijske faze algoritma, matrica položaja kopira se pomoću OpenCL naredbi u matricu najboljih položaja čestice. U protivnom se ažurira matrica najboljih vrijednosti na temelju vrijednosti dobivenih prethodnom evaluacijom, U algoritmu se koriste još dvije matrice nazvane Fi_p i Fi_g koje dimenzijama odgovaraju matricama položaja, najboljeg položaja i brzine. Ove matrice inicijaliziraju se na početku svake iteracije optimizacijskog algoritma na interval [0, φp] odnosno [0, φg] i predstavljaju umnožak φp * U(0, 1) odnosno φg * U(0 , 1). Navedene matrice kasnije će se koristiti za određivanje komponenti brzine vezanih uz osobni najbolji i globalni najbolji položaj čestice.

U fazi ažuriranja brzine i položaja čestice ponovo se može koristiti S * N paralelnih procesora. Iako su ovakve faze naizgled idealne za paralelizaciju, zbog rascjepkanosti cijelog ažuriranja na više jednostavnih matričnih operacija, korištene OpenCL jezgre imaju relativno nizak broj ALU operacija u odnosu na broj pristupa globalnoj memoriji. Sam postupak ažuriranja brzine izvodi se po komponentama (inercija, utjecaj najboljeg i najboljeg globalnog položaja), pri čemu se spomenute matrice Fi_p i Fi_g koriste za privremeno pohranjivanje vrijednosti odgovarajućih komponenti koje utječu na brzinu:

Fi_p ← Fi_p * (p – x)

Fi_g ← Fi_g * (g – x)

Trenutne vrijednosti dotičnih matrica mogu biti prebrisane jer će ionako biti ponovno postavljene na nasumične vrijednosti prije idućeg ažuriranja brzine. Za računanje utjecaja najbolje pozicije u populaciji na vektor brzine bilo je potrebno implementirati posebnu matričnu funkciju koja će vektor g tretirati kao matricu. Ovime se osiguralo da se i u ovom koraku koristi S * N paralelnih procesora. Prije pozivanja evaluacijske funkcije provjerava se odgovara li trenutna iteracija maksimalnom zadanom broju iteracija (kriterij zaustavljanja).

Sama C++ implementacija predviđa da neka klasa koja predstavlja problem koji je potrebno riješiti koristi funkciju optimizacije rojem čestica. Ova klasa mora implementirati i evaluacijsku funkciju što je osigurano putem apstraktne klase koju mora naslijediti. Klasa će pozvati jednu iteraciju (ili funkciju za inicijalizaciju roja). Povratak iz iteracije algoritma za optimizaciju predstavlja signal klasi koja je pozvala iteraciju. Na taj signal klasa će dohvatiti vrijednosti položaja čestica,

Page 105: optimizacija rojem čestica na paralelnim arhitekturama

obaviti evaluaciju i zapisati rezultate u odgovarajuću matricu u optimizacijskoj klasi koja predstavlja optimizaciju rojem čestica. Ovo je ujedno i razlog što svaka iteracija započinje pronalaženjem najbolje čestice u populaciji i ažuriranjem najboljih položaja čestica: iteracija se pokreće odmah nakon završetka evaluacije. Dvije klase komuniciraju preko povratnih vrijednosti a interno pamte stanja potrebna za ovakav način funkcioniranja.

Samo ažuriranje najboljih položaja čestica koristi posebne OpenCL jezgre. Ažuriranje se obavlja tako da se za svaku komponentu svake čestice uspoređuje vrijednost evaluacijske funkcije koja je za tu česticu dobivena u posljednjoj iteraciji algoritma sa zapamćenom vrijednošću evaluacije za najbolji položaj čestice. Na temelju rezultata ove usporedbe, ažuriraju se vrijednosti najboljeg položaja čestice, a sukladno tome i matrica u kojoj su pohranjene pripadne vrijednosti dobivene evaluacijskom funkcijom.

Page 106: optimizacija rojem čestica na paralelnim arhitekturama

INICIJALIZACIJA POLOŽAJA

EVALUACAIJA POPULACIJE

AŽURIRAANJE BRZINE

AŽURIRANJE POLOŽAJA

EVALUACAIJA POPULACIJE

INICIJALIZACIJA

GLOBALNA KOMPONENTA

INICIJALIZACIJA φg * U(blo, bhi)

INICIJALIZACIJA φp * U(blo, bhi)

KOMPONENTA SA OSBNIM NAJBOLJI

REZULTATOM

INICIJALIZACIJA NAJBOLJEG POLOŽAJA

INICIJALIZACIJA BRZINE

INERCIJSKA KOMPONENTA

AŽURIRANJE NAJBOLJIH POLOŽAJA

ČESTICE

TRAŽENJE GLOBALNO NAJBOLJE RJEŠENJA

OPTIMIZACIJA

GLOBALNO NAJBOLJI POLOŽAJ

INICIJALIZACIJA φp * U(blo, bhi)

INICIJALIZACIJA φg * U(blo, bhi)

Slika 7.3 Paralelizam na razini zadataka u PSO algoritmu

Osim podatkovnog paralelizma, ovaj algoritam može se paralelizirati i na razini zadataka. Vidljivo je da se neki dijelovi algoritma mogu obavljati istovremeno jer

Page 107: optimizacija rojem čestica na paralelnim arhitekturama

ne zavise jedan o drugome. Primjerice inicijalizacija položaja, brzine i Fi_p i Fi_g mogu pokrenuti istovremeno. Po završetku inicijalizacije, položaji se prosljeđuju evaluacijskoj funkciji. Teoretski, istovremeno je moguće obavljati i inicijalizaciju najboljih položaja čestica. Za to vrijeme inicijalizacija Fi_g može se i dalje izvršavati jer mora čekati na pronalazak globalno najbolje pozicije kako bi sa tim rezultatom mogla sudjelovati u izračunu utjecaja globalne komponente na brzinu. Dok algoritam i dalje traži najbolje rješenje u populaciji, Fi_b i inicijalizacija najboljih položaja mogu se nesmetano odvijati, ali ako su obje stavke završile s radom, računanje utjecaja najboljeg položaja na brzinu može se započeti ranije. Slika 7.3 prikazuje mogućnosti istovremenosti i međusobne zavisnosti pojedinih elemenata algoritma. Prostor označen plavom bojom označava dodatno vrijeme koje neka stavka može kasniti ili sljedeća stavka može iskoristiti kako bi počela ranije (pod uvjetom da ne treba čekati ni na koju drugu stavku). U stvarnosti, potrebno je znati vremena potrebna za izvršavanje pojedinih stavki za neki broj čestica (i određenu dimenzionalnost problema koji biva optimiran) kako bi se optimalno iskoristila istodobnost u algoritmu. Paralelizam na razini zadataka u ovom radu nije u potpunosti implementiran (ali je svakako moguć) radi složenosti sinkronizacije i (vremenske) cijene prebacivanja podataka između glavne memorije i memorije grafičkog procesora.

( )( ) ( )floatsizeofNNSmemorijapotrebna ⋅++⋅⋅⋅= 162 (7.1)

Memorijska zahtjevnost implementacije definirana je prvenstveno brojem čestica i dimenzionalnošću problema. U algoritmu se koristi pet matrica dimenzija N * S. To su: Fi_g, Fi_p, matrica položaja čestica, matrica najboljeg položaja čestica i matrica brzine čestica. Dodatno se koriste i dvije matrice za pohranu rezultata evaluacijske funkcije sa S elemenata i jedna matrica za pohranu najboljeg pronađenog položaja dimenzija N * 1 (vektor). Kako neke matrice zahtijevaju mogućnost inicijalizacije sa pseudo-slučajnim brojevima, potrebno je pamtiti i stanja tri komponente cLCG generatora za N * S elemenata matrice (u stvarnosti se pamte četiri komponente) Izrazi za minimalne ukupne zahtjeve na kapacitet memorije prikazan je u jednadžbi (7.1). Za brojeve s pomičnim zarezom u jednostrukoj preciznosti (float), svaki element zauzimati će 4 B. Kako je ograničenje veličine memorijskog objekta 128 MB, najveća dopuštena vrijednost umnoška N * S biti će 223.

( ) ( ) ( ) ( )

( ) ( )

−−−==

+⋅−+−+⋅−+⋅⋅−=

0,,2

3,,,,,,,

;42

1322,,,

43214321max

2

4

2

3

2

2

2

14321

CA

BxxxxzaDxxxxf

DxCxxBxAxxxxf

(7.2)

( )

( ) ( ) ( )0,...,0,0,...,,0,...,,

;

1000

11

2

1sin

2

1,...,,

2121max

2

0

2

0

22

21

==

+

−=

=

=

NN

N

i

i

N

i

i

N

xxxzaxxxf

x

x

xxxf (7.3)

Page 108: optimizacija rojem čestica na paralelnim arhitekturama

( )

( ) ( ) ( )0,...,0,0,...,,0,...,,

;150sin,...,,

2121max

10

0

224

0

2

21

==

+

⋅⋅= ∑∑

==

NN

N

i

i

N

i

iN

xxxzaxxxf

xxxxxf (7.4)

Za potrebe testiranja korištene su tri različite evaluacijske funkcije prikazane u jednadžbama (7.2), (7.3) i (7.4). Sve evaluacijske funkcije implementirane su u OpenCL-u i predviđene za paralelno izvođenje, tako da se proces optimizacije može u potpunosti izvršavati ma nekom procesnom uređaju. Za sve funkcije traži se globalni maksimum. U funkciji iz jednadžbe (7.2) parametri A, B, C i D učitavaju se iz glavnog programa i definiraju položaj maksimuma. To je jednostavna četverodimenzionalna parabola (jedan maksimum). Jezgra će učitati česticu kao četverodimenzionalni vektor brojeva s pomičnim zarezom (float4). Sama jezgra prilikom preslikavanja na VLIW arhitekturu postiže stupanj vektorizacije od 40% (objašnjeno u poglavljima 5.2.1.1 i 5.3).

Slika 7.4 Prikaz funkcije 7.4 za jednu (gore) i dvije (dolje) dimenzije

Page 109: optimizacija rojem čestica na paralelnim arhitekturama

Funkcije iz jednadžbi (7.3) i (7.4) predstavljaju osjetno složenije optimizacijske probleme jer imaju izrazito velik broj lokalnih minimuma i maksimuma. Obje funkcije postižu globalni maksimum kada je vrijednost svih komponenti domene jednaka nuli. Funkcija iz jednadžbe (7.3) implementirana je kao četverodimenzionalna dok je funkcija iz jednadžbe (7.4) implementirana i u četiri i u osam dimenzija. Ostale dimenzionalnosti su moguće, ali bi zahtijevale ostvarivanje nove jezgre za svaku dimenzionalnost zasebno (ili “univerzalne“ jezgre koje bi bile sposobne pokriti širok raspon dimenzionalnosti, ali vjerojatno uz osjetno smanjenu učinkovitost). Slika 7.5 prikazuje funkciju iz jednadžbe (7.3) za jednu odnosno dvije dimenzije, dok Slika 7.4 prikazuje funkciju iz jednadžbe (7.4) za istu dimenzionalnost. Same jezgre učitavaju česticu kao četverodimenzionalni (ili osmerodimenzionalni) vektor brojeva s pomičnim zarezom (float4 / float8). Nakon zbrajanja komponenti, suma se koristi dalje u formuli. Obje jezgre imaju mogućnost korištenja native_ funkcija. Apriori analiza jezgri pomoću Stream KernelAnalyzer aplikacije pokazala je da se broj ALU instrukcija prilikom korištenja native_ funkcija smanjuje za red veličine (sa 150 do 250, na 80 do 90 ALU instrukcija). Pretpostavlja se kako bi jezgra koja koristi float8 vektorske tipove mogla izazivati memorijske konflikte.

Slika 7.5 Prikaz funkcije 7.3 za jednu (gore) i dvije (dolje) dimenzije

Page 110: optimizacija rojem čestica na paralelnim arhitekturama

7.5 Implementacija ostvarenih RapidMind algoritama u OpenCL-u U ovom poglavlju biti će dan osvrt na mogućnosti implementacije algoritama iz poglavlja 3 u novijoj, moćnijoj i fleksibilnijoj tehnologiji. Algoritam za generiranje pseudo-slučajnih nizova brojeva neće bit razmatran jer je već implementiran u OpenCL-u (poglavlje 7.2). Sva razmatranja su striktno hipotetske prirode.

7.5.1 Računanje π

Implementacija jednadžbi (3.4), (3.5), (3.6), (3.7) bila bi slična kao i kod RapidMind platforme. Svaka radna stavka računala bi po jednu komponentu reda na temelju svog položaja u jednodimenzionalnom radnom prostoru. Za jednadžbu (3.6) možda bi bilo moguće svaki od četiri pribrojnika navedena u zagradama izračunati u zasebnoj radnoj stavci, ali korištenjem jedne radne stavke vjerojatno bi se postiglo bolje preslikavanje jezgre na VLIW arhitekturu. Po završetku obavljalo bi se sumiranje svih elemenata, kako je opisano u poglavlju 7.1.

Zbog podrške za brojeve s pomičnim zarezom u dvostrukoj preciznosti (double), sama točnost izračuna ovdje nikako ne bi bila problem, a brzina izvođenja trebala bi biti osjetno viša.

7.5.2 Algoritam k srednjih vrijednost

Uspješnost paralelizacije algoritma k srednjih vrijednosti ovisila bi prvenstveno o dimenzionalnosti prostora. Za male dimenzionalnosti, do nekoliko ili, u najgorem slučaju, nekoliko desetaka dimenzija, udaljenosti pojedinih točka od centroida mogle bi se računati na razini radne stavke. Pri tome bi se centroidi mogli pohraniti u sklopovski međuspremnik za konstante (jer bi u vrijeme prevođenja programa adrese centroida u nizu u kojem su pohranjene bile poznate) ili u priručnu memoriju prvog stupnja (jer bi sve radne stavke pristupale istim centroidima, odnosno, sve bi indeksirale iste elemente niza). Svi centroidi bili bi pokriveni kroz više iteracija programske petlje, pri čemu bi udaljenost od svakog centroida bila zapisana u lokalnu memoriju. Radna stavka bi slijednom redukcijom rezultata izračunala klasifikaciju vektora koji obrađuje. Nakon uspješne klasifikacije, na razini cijele grupe obaljalo bi se zbrajanje vektora s jednakim klasifikacijama. U tu svrhu koristila bi se posebna redukcija koja bi producirala onoliko suma koliko postoji centroida. Rezultati redukcije bili bi pohranjeni u globalnu memoriju. Dodatnim reduciranjem bili bi izračunati novi centri mase.

Za nekoliko desetaka ili stotina dimenzija udaljenosti od centroida računale bi se razini radne grupe. Svaka radna stavka obrađivala bi po jednu ili nekoliko komponenti vektora, i na kraju bi se obavljalo sumiranje (redukcija) na razini radne grupe (i potom ponovno računanje za idući centroid).U ovom slučaju već postoji realna mogućnost da će centroidi morati biti pohranjeni u globalnu memoriju. Kao i u prethodnom slučaju, iteriralo bi se kroz sve centroide i izračunala bi se klasifikacija vektora koja bi bila pohranjena u globalnu memoriju. Druga jezgra obavljala bi sumiranje (redukciju) točaka po dimenzijama. Svaka radna grupa učitavala bi predodređeni broj točaka i zbrajala komponente točaka s istom klasifikacijom. Rezultati zbrajanja bili bi zapisani u globalnu memoriju. Nova iteracija redukcije dalje bi reducirala količinu točaka koje je potrebno zbrojiti. Ponavljanjem postupka izračunali bi se novi centri masa.

Page 111: optimizacija rojem čestica na paralelnim arhitekturama

Ako je dimenzionalnost prostora reda veličine 103 svaka udaljenost od centroida morat će se računati na više radnih grupa. U tom slučaju, za N-dimenzionalni prostor, svakih N radnih stavaka obrađivat će N komponenti vektora (korištenjem vektorskih tipova, ovaj broj bi se mogao lako smanjiti istovremeno poboljšavajući učinkovitost pristupa memoriji). Pri tome će sve komponente centroida koje su stavke učitale ponvaljati s periodom N. Pomoću programske petlje obradili bi se svi centroidi i sve točke. Sumiranje rezultata obavljalo bi se pomoću jezgre u kojoj svaka radna stavka pomoću petlje učitava elemente točke i provodi zbrajanje po komponentama uzimajući pri tome u obzir klasifikaciju pojedinih točaka.

Iako se pokazalo da je algoritam problematičan za paralelizaciju, postoji mogućnost da opisani pristupi na modernom sklopovlju poluči dobre rezultate. Ovo je samo jedan pristup paralelizaciji problematike. Samo implementacijom i mjerenjima moguće je nedvosmisleno utvrdit kakav bi pristup bio najkvalitetniji. Dok su se kod RapidMind platforme za svaku dimenzionalnost prostora i svaki broj centroida pisale nove jezgre, kod OpenCL, široki spektar mogućnosti pokriven pomoću samo tri pristupa.

7.5.3 Sažimanja digitalne slike po JPEG normi

Prilagodba algoritama opisanih u poglavlju 3.5 za izvođenje u OpenCL okruženju mogla bi polučiti odlične rezultate. Zbog nepostojanja ograničenja na broj instrukcija u jezgri i zbog visoke fleksibilnosti u načinima pristupanju memoriji, sve opisane RapidMind jezgre mogle bi biti sažete u jednu jezgru.

Početno bi se obavljala pretvorba prostora boja i translacija. Ovaj dio jezgre bio bi gotovo identičan svom RapidMind pandanu. Računanje diskretne kosinusne transformacije svakog 8 * 8 bloka točaka moglo bi se obavljati na razini radne stavke. U tom slučaju ponovo bi bilo isplativo sve koeficijente (Slika 3.13) prikazati kao konstante unutar samog programskog koda jezgre. Druga mogućnost bila bi pohrana u memoriju za konstante: kako su adrese kojima radna stavka pristupa poznate prilikom prevođenja jezgre, koeficijenti će iz globalne memorije biti pohranjeni u sklopovske međuspremnike za konstante (poglavlje 5.2.1). Računanje na razini radne stavke moglo bi uzrokovati konflikte prilikom pristupa globalnoj memoriji.

Alternativno, moguće je da se svaka točka bloka računa na jednoj radnoj stavci, odnosno da se blok računa unutar radne grupe (idealno s obzirom na to da su preporučene dimenzije radne grupe 64 radne stavke). Nakon pretvorbe prostora boja i translatiranja, svaka bi radna stavka trebala imati pristup svima točkama bloka koji treba bit podvrgnut procesu diskretne kosinusne transformacije. Zato bi se nakon pretvorbe točke spremale u lokalnu memoriju. Ovakav pristup mogao bi uzrokovati memorijske konflikte na razini lokalne memorije. Kod ovakvog pristupa ne bi bilo preporučljivo koristiti koeficijente kodirane unutar samog programskog koda jer bi to uzrokovalo previše divergencije u programskom toku. Moguće je koristiti lokalnu memoriju, u koju bi se koeficijenti iz globalne memorije kopirali asinkrono, za vrijeme izvođenja pretvorbe prostora boja i translacije. Još jedna mogućnost bila bi pohrana u memoriju za konstante: kako je veličina niza koji bi sadržavao koeficijente unaprijed poznata i odgovarajuće veličine (točno 16 kB) može biti pohranjen u sklopovske međuspremnike za konstante na grafičkom procesoru (poglavlje 5.2.1). Daljnje poboljšanje algoritma uključivalo bi pohranu

Page 112: optimizacija rojem čestica na paralelnim arhitekturama

više blokova u lokalnu memoriju (primjerice po jedan za svaku komponentu prostora boja).

Računanje diskretne kosinusne transformacije pomoću jednadžbe (3.16) vjerojatno i dalje ne bi bilo isplativo zbog visoke cijene trigonometrijskih operacija (čak i uz korištenje native_ funkcija).

Postupak kvantizacije trebao bi biti relativno jednostavan. Koeficijenti za kvantizaciju mogli bi biti integrirani u programski kod, pohranjeni u lokalnu memoriju ili memoriju za konstante. Ako bi koeficijenti diskretne kosinusne transformacije bili pohranjeni u memoriju za konstante, u njoj više ne bi ostalo mjesta za kvantizacijske koeficijente i oni bi morali biti pohranjeni negdje drugdje.

Samo preslagivanje elemenata trebalo bi biti relativno jednostavno i svodilo bi se na izračunavanje nove adrese na koju bi radna stavka trebala pohraniti izračunate vrijednosti.

Odbacivanje nula na kraju niza problematično je izvesti paralelno. Proces bi vjerojatno uključivao pohranjivanje transformiranog bloka u radnu memoriju. Svaka bi radna stavka potom dohvaćala vrijednost elementa koji prethodi njezinom elementu i utvrđivala nalazi li se na dotična radna stavka na granici između ne-nula vrijednosti i nule. U novom nizu radne stavke bi označavale nalaze li se na granici ili ne. Ako je prethodni element granični može upisati indeks svojeg elementa u novi niz, a u protivnom negativnu vrijednost. Potom bi se na razini cijele radne grupe provodila redukcija koja bi tražila najveći element u nizu. Vrijednost tog elementa bila bi pozicija zadnjeg ne-nula elementa. Pronađeno mjesto bilo bi adekvatno označeno. Kopiranje u memoriju može se izvršiti samo do označene granice, ali, kako ne postoji mogućnost za komunikaciju među radnim grupama, idući blok bi i dalje bio morao biti upisan 64 mjesta iza početka prvog, jer iduća radna radna grupa ne može provjeriti gdje se nalazi kraj prethodnog niza. Za to bi bila potrebna još jedna jezgra.

Kako OpenCL u odnosu na RapidMind platformu ima puno veće mogućnosti za implementaciju nekog algoritma, postoji mogućnost da se i entropijsko kodiranje prilagodi učinkovitom izvođenju na grafičkom procesoru.

Očekuje se da će sam algoritam polučiti puno bolje brzinske rezultate na modernijem grafičkom procesoru koji koristi samo jednu jezgru za provođenje cijelog postupka, pri čemu će bitan faktor igrati i korištenje lokalne memorije i memorije za konstante.

7.5.4 Marching cubes algoritam

Marching cubes algoritam pokazao se neočekivano spor u RapidMind implementaciji. U OpenCL-u algoritam bi bio implementiran slično kao i RapidMind jezgra opisana u poglavlju 3.6.1. Opisana triTable tablica u potpunosti bi ispunila sklopovski međuspremnik za konstante.

Kako korišteni grafički procesor može raditi s trodimenzionalnim nizovima, algoritam bi bio prilagođen obradi cjelokupnog niza snimki odjednom umjesto da obrađuje po jedan par snimki, odnosno sloj istovremeno. Time bi se eliminiralo višestruko pozivanje jezgri i potreba za prebacivanjem podatka između dva poziva. Ovakav pristup ostvariv je pod uvjetom da količina podataka ne prelazi memorijsko ograničenje korištenog grafičkog sklopovlja (128 MB). U algoritmu koji je radio po slojevima, svakoj točki slike istovremeno su pristupale četiri instance

Page 113: optimizacija rojem čestica na paralelnim arhitekturama

jezgre. U trodimenzionalnoj inačici algoritma, ta vrijednost će se udvostručit pa su memorijski konflikti mogući. Razrješavanje ovih konflikata moguće je ako bi radne stavke obrađivale više od jedne kocke. Kada završi s prvom kockom radna stavka redom će obraditi još i tri susjedne kocke, po jednu u svakom smjeru (primjerice kocku desno, kocku dolje i kocku iz idućeg sloja). Za svaku od tih kocaka radna stavka će već imati učitane četiri točke. Preostalim točkama trebala bi pristupiti uz manje konflikata jer su susjedne radne stavke već su kocke koje sadrže ove točke. U trodimenzionalnom prostoru algoritam koji određuje koja radna stavka obrađuje koje kocke i u kojem redoslijedu mogao bi biti relativno složen, ali izvediv. Zbog nedostatka mogućnosti komunikacije među radnim grupama, eliminacija praznih trokuta nije jednostavno izvediva i vjerojatno bi zahtijevala još nekoliko posebnih jezgri (isti problem kao i kod zapisivanja podatka bez nula na kraju u poglavlju 7.5.3)

Page 114: optimizacija rojem čestica na paralelnim arhitekturama

8. Interpretacija rezultata

Procjena isplativosti nekog rješenja velikim se dijelom temelji na njegovim performansama. U ovom poglavlju većinom će se razmatrati brzine izvođenja pojedinih paralelnih algoritama opisanih u poglavljima 3 i 7. Na temelju dobivenih performansi donijet će se procjena u kojim uvjetima su dani algoritmi isplativi. Biti će napravljena analiza potencijalnih uskih grla sustava i mogućnosti otklanjanja istih. Kako je opisano u poglavlju 7, samo rješenje implementirano je dekompozicijom na jednostavnu funkcionalnost opće namjene pa će se prilikom analize i kretati od jednostavnijih funkcija: ako takve funkcije pokazuju lošije rezultate, vjerojatno, barem djelomično, utječu na eventualne loše rezultate cjelokupnog sustava. S druge strane, postoji mogućnost da je neka funkcionalnost u potpunosti upotrebljiva unatoč lošim performansama nekog sustava koji sačinjava.

8.1 Generiranje nizova pseudo-slučajnih brojeva

Wichmann-Hillov cLCG implementiran u OpenCL C programskom jeziku pokrenut je na grafičkom i glavnom procesoru. Rezultati su uspoređeni sa slijednom implementacijom Wichmann-Hillovog cLCG-a, kao i s “ugrađenim“ generatorom nizova pseudo-slučajnih brojeva iz stdlib.h biblioteke.

1,0E+04

1,0E+05

1,0E+06

1,0E+07

1,0E+08

1,0E+09

1,0E+10

1 10 100 1000 10000 100000 1000000 1E+07

Broj generiranih pseudo-slučajnih brojeva

Gen

erir

ani b

roje

vi/s

GPU

CPU

ugrađeniRNG

Slika 8.1 Brzine generiranja pseudo nasumičnih brojeva

Slika 8.1 prikazuje performanse generatora nizova pseudo-slučajnih brojeva implementiranih u OpenCL-u i pokrenutih na glavnom i grafičkom procesoru. Za svaku točku grafa uzeti su najbolji dobiveni rezultati s obzirom na veličinu radne grupe. Pretpostavka je da će se na temelju dobivenih rezultata, veličina radne grupe ionako prilagođavati u odnosu na broj pseudo-slučajnih brojeva koje je

Page 115: optimizacija rojem čestica na paralelnim arhitekturama

potrebno generirati. Vidljivo je da već za nekoliko desetaka tisuća pseudo-slučajnih brojeva OpenCL generatori premašuju slijedne izvedbe. Ovdje treba voditi računa i o činjenici da Wichmann-Hillov cLCG ima puno veće proračunske zahtjeve, ali i veći period u odnosu na korišteni slijedni generator. Za male količine pseudo-slučajnih nizova brojeva, svi kapaciteti grafičkog i glavnog procesora ne mogu biti iskorišteni (izgladnjivanje procesora) Kako svi grafovi vrijednosti na ordinati prikazuju u logaritamskom mjerilu, naizgled linearan rast performansi grafičko procesora u biti predstavlja eksponencijalan porast. Za razliku od ranijih rezultata dobivenih na glavnom procesoru i ATI Mobility Radeon X1600 grafičkom procesoru (Slika 3.7), AMD Radeon HD 5850 ne ulazi u područje zasićenja duž cijele domene. Da ne postoje ograničenja na memorijski kapacitet, moguće je kako bi stagnacija porasta performansi nastupila za još veće količine generiranih pseudo-slučajnih brojeva, ali o tome se može samo nagađati. Općenito, noviji AMD Radeon HD 5850 pokazao se za red veličine brži od ATI Mobility Radeon X1600. Paralelno generiranje pseudo-slučajnih brojeva na glavnom procesoru daje otprilike red veličine sporije rezultate od ATI Mobility Radeon X1600.

1,0E+06

1,0E+07

1,0E+08

1,0E+09

1,0E+10

100 1000 10000 100000 1000000 10000000

Broj generiranih pseudo-slučajnih brojeva

Gen

erir

ani

bro

jevi

/s

Int4

Int1

Slika 8.2 Dobitici pri korištenju vektorskih tipova podataka na grafičkom procesoru

Opaženo je da skaliranje brojeva na određeni interval ne utječe na performanse što sugerira da količina proračuna nije nužno usko grlo sustava. Barem kada su u pitanju dodatne operacije skaliranja. Samo je na glavnom procesoru prilikom korištenja OpenCL jezgri opažena manja razlika u performansama kada su u pitanju velike količine brojeva.

Page 116: optimizacija rojem čestica na paralelnim arhitekturama

1,00E+06

1,00E+07

1,00E+08

1,00E+09

100 1000 10000 100000 1000000 10000000

Broj generiranih pseudo-slučajnih brojeva

Ge

ne

rira

ni b

roje

vi/s

Int4

Int

Slika 8.3 Dobitici pri korištenju vektorskih tipova podataka na glavnom procesoru

Tip nasumičnih brojeva koji biva vraćan (float ili integer) nema gotovo nikakvog utjecaja.

Na grafičkom procesoru, razlika između korištenja vektorskog tipa širine 128 bita (primjerice float4 ili int4) u odnosu na odgovarajuće skalarne tipove vidljiva je tek za veće količine podataka – reda veličine 105 (Slika 8.2) Kod velikih količina podataka (reda veličine 106 i više), pokazalo se da su jezgre koje koriste vektorske tipove brže i nekoliko puta. Ovakvo ponašanje bilo je i očekivano.

Na glavnom procesoru, razlike u performansama koje uzrokuje korištenje vektorskih tipova u odnosu na skalarne počinju se osjećati već kod nekoliko tisuća generiranih brojeva (Slika 8.3).

Page 117: optimizacija rojem čestica na paralelnim arhitekturama

1,0E+04

1,0E+05

1,0E+06

1,0E+07

1,0E+08

1,0E+09

1,0E+10

1 10 100 1000 10000 100000 1000000 10000000

Broj generiranih pseudo-slučajnih brojeva

Gen

erir

ani

bro

jevi

/s

Neskalirani Int4 -WGS 1

Neskalirani Int4 -WGS 8

Neskalirani Int4 -WGS 32

Neskalirani Int4 -WGS 64

Neskalirani Int4 -WGS 256

ugrađeni RNG

Slika 8.4 Utjecaj veličine radne grupe na grafičkom procesoru

Veličina radne grupe (označena na priloženim grafovima s WGS – eng. WorkGroup Size) na grafičkom procesoru ima samo minimalan utjecaj vidljiv tek pri velikim količinama podataka (Slika 8.4). Ova izjava vrijedi za sve razumne veličine radnih grupa – veličine radnih grupa kao što su jedan ili osam nemaju nekog pretjeranog smisla jer će u tom slučaju na korištenom grafičkom procesoru 63 odnosno 56 procesnih elemenata biti neiskorišteno.

1,0E+04

1,0E+05

1,0E+06

1,0E+07

1,0E+08

1,0E+09

1 10 100 1000 10000 100000 1000000 10000000

Broj generiranih pseudo-slučajnih brojeva

Gen

erir

ani

bro

jevi

/s

Neskalirani Int4WGS 1

Neskalirani Int4WGS 8

Neskalirani Int4WGS 64

Neskalirani Int4WGS 256

Neskalirani Int4WGS 1024

ugrađeni RNG

Slika 8.5 Utjecaj veličine radne grupe na glavnom procesoru

Page 118: optimizacija rojem čestica na paralelnim arhitekturama

Glavni procesor pokazuje konstantni rast performansi s porastom broja radnih stavki po radnoj grupi (Slika 8.5). Prema nekim neprovjerenim informacijama, AMD-ova implementacija OpenCL norme preslikava radne grupe na jezgre procesora. Pretpostavlja se da, kako broj radnih stavki unutar radne grupe raste, cijena troškova sinkronizacije među radnim stavkama radne grupe raste osjetno sporije nego performanse koje se dobivaju većim brojem radnih stavki. Ovo bi bio razlog zašto je s povećanjem broja radnih stavki u radnoj grupi prisutan porast performansi u mnogim nezavisnim mjerenjima vršenim s AMD-ovom implementacijom OpenCL norme.

Tablica 8.1 prikazuje rezultate apriori analize korištenih jezgri. Kako je vidljivo, sam proces skaliranja rezultata zahtjeva između 5% i 20% više ALU operacija. Prelazak sa skalarnih na vektorske tipove izlaznih podatka zahtjeva približno 2.5 puta više instrukcija, dok se broj generiranih pseudo-slučajnih brojeva povećava za faktor četiri, sugerirajući isplativost ovakvog pristupa.

Tablica 8.1 Količine ALU instrukcija za različite vrste jezgri

Vrsta jezgre Broj ALU operacija za AMD Radeon HD 5870

float 19

float skalirana 20

float4 47

float4 skalirana 49

int 18

int skalirana 21

int4 45

int4 skalirana 54

U mjerenjima su u pravilu bile generirane količine pseudo-slučajnih brojeva koje odgovaraju potencijama broja 2. Kako korišteni grafički procesor ima osamnaest procesnih jedinica, a glavni procesor tri, prilikom mjerenja su korišteni i višekratnici broja osamnaest (pomnoženih s potencijama broja dva) kao količine pseudo-slučajnih brojeva koje je potrebno generirati. Pretpostavka je bila da će za takve vrijednosti preslikavanje radnih grupa na sklopovlje biti učinkovitije, ali se pokazalo da ovakav pristup nema značajnijeg utjecaja.

Ako se generirani pseudo-nasumični brojevi ne bi koristili dalje u OpenCL kodu nego bi morali biti dohvaćeni u glavnu aplikaciju, pad performansi rastao bi s brojem generiranih pseudo-slučajnih brojeva što prikazuje Slika 8.7.

Page 119: optimizacija rojem čestica na paralelnim arhitekturama

1,0E+04

1,0E+05

1,0E+06

1,0E+07

1,0E+08

1,0E+09

10 100 1000 10000 100000 1000000 10000000

Broj generiranih pseudo-slučajnih brojeva

Gen

eria

ni

bro

jevi

/s Dohvat uglavnu mem.

Bez dohvatau glavnumemoriju

Slika 8.6 Troškovi dohvata podataka iz memorije grafičkog procesora

Inicijalizacija niza pokazuje vrlo visoke brzine izvođenja koje sežu i do 1010 generiranih brojeva u sekundi. Dok su u dosadašnjim razmatranjima jezgre zapisivale rezultate izvođenja u globalnu memoriju kako bi ih kasnije koristile neke druge jezgre ili glavna aplikacija, ovaj slučaj može se uzeti kao pokazatelj koliko bi bilo isplativo pozivati funkcije za generiranje pseudo-slučajnih brojeva iz neke jezgre direktno. Ovakav pristup bio bi do tri puta brži (Slika 8.7). Zanimljivo je primijetiti da u ovom slučaju i glavni i grafički procesor ulaze u “područje zasićenja“ relativno brzo. U odnosu na slijednu izvedbi Wichmann-Hillov cLCG-a, inicijalizacija implementirana u OpenCL-u uvijek je isplativija za glavni procesor, dok za grafički procesor postaje isplativija već za nekoliko desetaka generiranih pseudo-slučajnih brojeva.

Page 120: optimizacija rojem čestica na paralelnim arhitekturama

1,0E+06

1,0E+07

1,0E+08

1,0E+09

1,0E+10

1,0E+11

1,0E+12

1 10 100 1000 10000 100000 1000000 10000000

Broj generiranih pseudo-slučajnih brojeva

Gen

erir

ani

bro

jevi

/s

GPU

CPU

SerijskicLCG

Slika 8.7 Inicijalizacija cLCG

Kako je količina memorije koju grafički procesor može koristiti za pohranjivanje jednog niza ograničena na 128 MB proizlazi kako je maksimalna veličina niza pseudo-slučajnih brojeva koje je moguće generirati po pozivu jezgre 223, što se tijekom istraživanja pokazalo kao veliko ograničenje. Generiranjem većeg broja pseudo-slučajnih brojeva po instanci jezgre zaobišlo bi se ovo ograničenje, ali bi se povećao broj zapisa u globalnu memoriju.

8.2 Matrične operacije

Učinkovita implementacija matričnih operacija predstavlja bitan faktor u učinkovitosti algoritma za optimizaciju pomoću roja čestica. Slika 8.8 prikazuje brzine izvođenja za operacije zbrajanja i pridruživanja s konstantom odnosno s drugom matricom. Slične operacije kao što su oduzimanje ili množenje imati će jednaka vremena izvođenja. Iznimka bi bile neke složenije operacije kao što su potenciranje, dijeljenje ili trigonometrijske funkcije. Primjerice, dok jezgre za zbrajanje množenje ili oduzimanje zahtijevaju sedam ALU instrukcija, jezgre u kojima se obavlja dijeljenje komponenti matrice zahtijevaju i do peterostruko više instrukcija. Općenito, omjer ALU operacija naprema broju pristupa globalnoj memoriji je vrlo nizak pa će uporaba grafičkog procesora postati isplativa tek za matrice s 105 ili više elemenata pri čemu će grafički procesor imati za jedan do dva reda veličine bolje rezultate od slijedne implementacije. Prosječan broj procesorskih ciklusa za operacije zbrajanja i slične operacije iznosit će 56. Ako se uzme u obzir da pristup globalnoj memoriji za korišteni grafički prcesor traje između 300 i 600 procesorskih ciklusa, biti će potrebno između šest i jedanaest valova kako bi se kompenziralo za jedan pristup globalnoj memoriji (vidjeti poglavlja 4.6, 5.2.1.2 i 5.2.1.3). Za složenije operacije poput dijeljenja taj će broj iznositi dva ili tri. Iako se podatci iz matrice tretiraju kao jednodimenzionalni niz, tijekom izvođenja dolazi do konflikata prilikom pristupanja globalnoj memoriji što je

Page 121: optimizacija rojem čestica na paralelnim arhitekturama

potvrđeno i analizom izvršavanja jezgri u ATI Stream Profiler aplikaciji: 30 do 50% vremena tijekom zapisivanja jedinica za pisanje u globalnu memoriju bila je u stanju čekanja (eng. stall). Aposteriori analiza također je pokazala je relativno nisku zauzetost ALU (otprilike 4 do 11% ukupnog vremena izvođenja jezgre otpada na izvršavanje ALU instrukcija), što je bilo i očekivano. Zanimljivo je primijetiti da za matrice s malim brojem elemenata (100 do 103) zbrajanje s konstantom zahtjeva više vremena u odnosu na zbrajanje dviju matrica, iako potonje zahtjeva dva čitanja iz memorije dok prvo zahtjeva samo jedno čitanje i dohvat jedne konstante koja bi se zbog korištenja priručne memorije trebala jako brzo učitati u sve jezgre. Za matrice s većim brojem elemenata ova anomalija nije uočena.

1,0E+04

1,0E+05

1,0E+06

1,0E+07

1,0E+08

1,0E+09

1,0E+10

1,0E+11

1 10 100 1000 10000 100000 1000000 10000000 1E+08

Broj elemenata matrice

Ob

rađ

eni

elem

enti

/s

GPU:A+=B

GPU:A+=const

CPU:A+=B

CPU:A+=const

Slika 8.8 Brzine izvođenja jednostavnijih matričnih operacija

Korištenjem četverodimenzionalnih vektora, povećava se propusnost, ali se na matrice postavlja ograničenje da moraju biti djeljive s četiri.

Matrično množenje učinkovito je za matrice koje imaju nekoliko tisuća elemenata (ili više), a za matrice s nekoliko milijuna elemenata postižu se performanse reda veličine 102 GFLOPS-a (Slika 8.9). U slučaju množenja kvadratnih matrica poželjne su što veće dimenzije radne grupe kako bi se maksimalno iskoristilo korištenje podataka koji su prebačeni u lokalnu memoriju. Bitan faktor u performansama igraju i algoritmi za određivanje dimenzija radne grupe. Pronalaženje odgovarajućih dimenzija radne grupe predstavlja složeni optimizacijski problem, a vrijeme za pronalaženje rješenja je izrazito ograničeno, što ostavlja puno mjesta za napredak u ovom području. Aposteriori analiza izvođenja pokazala je da preko polovice vremena izvođenja pojedine jezgre otpada na izvršavanje ALU instrukcija što predstavlja solidan rezultat. Također je zanimljivo da je OpenCL C prevoditelj uspio vrlo učinkovito preslikati jezgru na VLIW arhitekturu tako da je stupanj vektorizacije čak 75% (primjerice, kod zbrajanja matrice s konstantom iznosi 28.57%). Što se memorijskih konflikata tiče, očekivano je da će zbog same prirode matričnog množenja, paralelna

Page 122: optimizacija rojem čestica na paralelnim arhitekturama

implementacije generirati dosta konflikata. Globalna memorija bila je u stanju čekanja samo 0.12% i to samo u fazi zapisivanja. U fazi čitanja, koja sadrži puno veći broj pristupa globalnoj memoriji, memorijskih konflikata nije bilo. Prilikom pristupa lokalnoj memoriji, faza čekanja na pristup iznosila je oko 19%. Jezgra za množenje kvadratnih matrica pokazala se vrlo učinkovitom pri velikom broju instanci jezgre i prilagodba algoritma množenju nekvadratnih matrica mogla bi se pokazati isplativom.

1,0E-04

1,0E-03

1,0E-02

1,0E-01

1,0E+00

1,0E+01

1,0E+02

1,0E+03

10 1000 100000 10000000 1000000000 1E+11

Broj potrebnih računskih operacija

GF

LO

PS Kvadratne

Ne kvadrtne

Serijski

Slika 8.9 Brzine izvođenja matričnog množenja

Algoritam za množenje nekvadratnih matrica također ima visok stupanj učinkovitosti. Iako ima za dva reda veličine nepovoljniji omjer broja ALU instrukcija u odnosu na instrukcije za pristup memoriji, nema memorijskih konflikata a vektorizacija OpenCL C koda iznosi preko 77%. U odnosu na algoritam za množenje kvadratnih matrica ovaj algoritam ima osjetno manji postotak vremena provedenog u izvođenju ALU instrukcija (20.2 %), ali zato koristi manji broj registara po instanci jezgre (17 naprema 20), pa je sposoban pokretati i do 17% više radnih stavki po stream jezgri. Za množenje manjih matrica (do 108 operacija s pomičnim zarezom po matričnom množenju) algoritam za množenje nekvadratnih matrica je učinkovitiji od algoritma za množenje kvadratnih matrica. Navedena pojava vjerojatno je barem dijelom posljedica drugačijeg algoritma optimizacije dimenzija radne grupe.

Radi što veće učinkovitosti prilikom pristupanja memoriji, obje verzija algoritma za matrično množenje barataju isključivo s vektorskim tipovima podataka (float4) što nameće ograničenje da dimenzije matrice moraju biti višekratnici broja četiri.

8.3 Operacije redukcije

Sve implementirane operacije redukcije daju relativno loše rezultate koji se približavaju slijednoj implementaciji tek za velik broj elemenata nad kojima se

Page 123: optimizacija rojem čestica na paralelnim arhitekturama

redukcija obavlja (Slika 8.10). Jedan od razloga je i visoki stupanj učinkovitosti slijednog algoritma: jednostavna petlja koja slijedno pristupa memoriji i izvršava mali broj operacija. Slijedni pristup memoriji vjerojatno omogućava učinkovito korištenje procesorske priručne memorije, a mali broj operacija povećava vjerojatnost da će sve važnije varijable biti pohranjene u procesorske registre i lako dostupne za brzo izvršavanje.

1,0E+03

1,0E+04

1,0E+05

1,0E+06

1,0E+07

1,0E+08

1,0E+09

1,0E+10

1 10 100 1000 10000 100000 1000000 1E+07

Broj elemenata u nizu

Ob

rađ

eni

elem

rnti

/s SUM

MAX

COUNT

SUMslijedno

Slika 8.10 Postignute brzine pri izvođenju operacija redukcije

S druge strane, redukcije implementirane u OpenCL-u nisu sposobne u jednom prolazu obaviti redukciju u potpunosti nego ju rade u više prolaza, rekurzivno. Slika 8.11 pokazuje vremenske odnose prilikom izvršavanja sumiranja nad nizom s 226 elemenata (dobiveno pomoću ATI Stream Profiler dodatka Visual Studio razvojnom okruženju). Broj instanci jezgre u ovom slučaju redom iznosi 226, 219, 212 i 25. Pri tome broj valova iznosi 220, 213, 26 i 1, dok udio vremena koje jezgra provodi izvršavajući ALU instrukcije iznosi (u postotcima) 75.55, 74.53, 22.49 i 0.34. Vidljivo je da prvi prolaz (izvršavanje jezgre označeno je zelenim područjima u retku naslovljenom Kernel Execution) zauzima daleko najviše vremena. Ostali prolazi sve su manje učinkoviti u odnosu na vrijeme koje je potrebno kako bi se jezgra pokrenula. Možda bi bilo korisno provoditi paralelnu redukciju samo dok broj elemenata veći od određenog praga, primjerice 216 elemenata., nakon čega bi se ostatak redukcije provodio slijedno, jer je iz aposteriori analize vidljivo da učinkovitost jezgri za redukciju niska za (relativno) male brojeve instanci.

Page 124: optimizacija rojem čestica na paralelnim arhitekturama

Slika 8.11 Vremena rekurzivnog izvođenja redukcijskih jezgri

Kako je stupanj vektorizacije prilikom preslikavanja jezgre na VLIW arhitekturu relativno nizak (23.5%), korištenje vektorskih umjesto skalarnih tipova podataka (kao u izvornim AMD-ovim primjerima) moglo bi popraviti količinu paralelizacije unutar same jezgre, a istovremeno bi se popravila propusnost prema globalnoj ali i lokalnoj memoriji. Problem kod korištenja vektorskih tipova predstavljaju indeksi koje neke redukcije vraćaju (primjerice MAX i MIN redukcije koje vraćaju i redni broj najvećeg odnosno najmanjeg elementa u nizu).

Sam pristup memoriji ne predstavlja usko grlo sustava. Globalnoj memoriji svaka instanca jezgre pristupa samo jednom, prilikom čitanja. Po završetku algoritma samo jedna radna stavka iz čitave radne grupe obavlja zapisivanje u globalnu memoriju. Ovakav pristup gdje se prilikom svake iteracije obujam problema višestruko smanjuje općenito se pokazao dobar što se pristupanja memoriji tiče, ali ostavlja velik dio procesnih elemenata neiskorištenim što bi mogao bit uzrok neučinkovitosti algoritma. Za veći broj elemenata nad kojima se obavlja redukcija, zbog velikog broja ALU instrukcija (i preko 150 instrukcija odnosno preko 1200 procesorskih ciklusa) unutar jezgre i velikog broja valova, pristup globalnoj memoriji ne predstavlja usko grlo pa bi jedno od potencijalnih rješenja bilo i da svaka jezgra na početku učita i zbroji nekoliko elemenata niza uzastopce prije nego što krene dio programskog koda jezgre u kojem će broj aktivnih radnih stavki u grupi biti reduciran.

Memorijskih konflikata nema ni kod pristupa globalnoj ni kod pristupa lokalnoj memoriji. Broj pristupa lokalnoj memoriji za red veličine je veći od broja pristupa globalnoj memoriji što pokazuje učinkovitost jezgre pri iskorištavanju memorijske hijerarhije (veći udio korištenja brže memorije). Jezgre koriste izrazito malen broj registara (2) i lokalne memorije (maksimalno 0.5 KB) tako da ograničenja na broj instanci jezgre po stream jezgri praktički ne postoje.

Daljnje promjene u algoritmu koji određuje dimenzije radne grupe mogle bi donijeti određena poboljšanja u performansama, ali pri tome treba voditi računa da veličina radne grupe na nekoj razini rekurzije može utjecati na broj daljnjih rekurzivnih poziva (Slika 3.4). Kako bi se provjerilo koliki utjecaj na brzinu izvođenja ima sam poziv jezgre iz glavne aplikacije, mjerenja su vršena i u razvojnom (debug), načinu rada. Razlika između optimiranog (realese) i tek nešto manje debug načina rada rada ne bi trebala biti prevelika s obzirom na relativno

Page 125: optimizacija rojem čestica na paralelnim arhitekturama

kratak kod za pozivanje OpenCL jezgri. Slika 8.12 predočava rezultate navedenog eksperimenta. Jasno vidljiva razlika između dva načina rada sugerira nezanemarivu ulogu programskog koda za pozivanje redukcijske jezgre u performansama cjelokupne operacije.

1,0E+03

1,0E+04

1,0E+05

1,0E+06

1,0E+07

1,0E+08

1,0E+09

1 10 100 1000 10000 100000 1000000 10000000

Broj elemenata u nizu

Ob

rađ

eni

elem

rnti

/s

SUM

SUMDebug

Slika 8.12 Neizravan pokazatelj utjecaja pripreme za poziv jezgre na brzinu izvođenja

Na kraju redukcije potrebno je i dohvatiti rezultat što predstavlja relativno neučinkovitu operaciju – zbog male količine podataka koji se prebacuju: brzina prijenosa je oko 20 KB/s, dok je maksimalna teoretska propusnost korištene sabirnice iznosi 80 GB/s. Ako rezultat redukcije nije potreban u glavnom programu, spomenuto dohvaćanje iz memorije procesnog uređaja trebalo bi biti preskočeno.

Djelomične redukcije i redukcije nizova čija veličina nije potencija broja dva imaju znatno više vremena izvođenja.

U usporedbi s RapidMind redukcijama, operacija redukcije implementirana u OpenCL-u ima prednost zbog mogućnosti korištenja sinkronizacije i hijerarhijske strukture memorije što joj omogućava osjetno manji broj iterativnih poziva jezgre koja provodi redukciju.

8.4 Optimizacija rojem čestica

Optimizacija rojem čestica ovisit će o izvođenju svih podsustava na kojima se ovaj algoritma temelji. Priloženi grafovi (Slika 8.13, Slika 8.14, Slika 8.15 i Slika 8.16) prikazuju brzine izvođenja u odnosu na slijedni algoritam. Složenost evaluacijske funkcije ne predstavlja ključni faktor u brzini izvođenja u nijednom od ovih

Page 126: optimizacija rojem čestica na paralelnim arhitekturama

slučajeva. Vidljivo je kako je korištenje ove metode isplativo tek u slučajevima kada umnožak broja čestica i dimenzionalnosti funkcije nad kojom se obavlja optimizacija ima red veličine 104 ili više. Ispod ove granice slijedna izvedba ima veću učinkovitost.

1,0E-02

1,0E-01

1,0E+00

1,0E+01

1,0E+02

1,0E+03

1,0E+00 1,0E+01 1,0E+02 1,0E+03 1,0E+04 1,0E+05 1,0E+06 1,0E+07

Broj čestica

Bro

j o

pti

miz

acij

a/s

GPU

CPU

Slika 8.13 Brzina izvođenja optimizacije za testnu funkciju 1

Kako je opaženo u poglavlju 8.2, matrične operacije imaju nisku iskorištenost ALU jedinice, relativno velik broj pristupa memoriji i problema s memorijskim konfliktima i preslikavanjem programskog teksta jezgre na VLIW arhitekturu. Kako se u svakoj iteraciji optimizacije koristi čak osam ovakvih jednostavnih matričnih operacija, vidljivo je da broj instanci jezgre i valova mora biti velik kako bi velika propusnost globalne memorije došla do izražaja i kako bi se sakrilo njezino vrijeme odziva. Pored matričnih operacija, problem predstavlja i operacija redukcije koja se koristi za pronalaženje globalnog optimuma. Kako je zaključeno u poglavlju 8.3, paralelna operacija redukcije nije se pokazala pretjerano učinkovitom, a u ovom slučaju koriste se redukcije koje osim same vrijednosti vraćaju i redni broj te vrijednosti u nizu i koje su se pokazale najsporijima. Čak i sam proces popunjavanja matrica pseudo-slučajnim brojevima koji se u svakom ciklusu algoritma koristi dva puta (jednom za određivanje utjecaja globalnog optimuma, a jednom za određivanje utjecaja najbolje zapamćene pozicije) mora generirati barem nekoliko desetaka tisuća pseudo-slučajnih brojeva kako bi bio isplativiji od slijednog algoritma (poglavlje 8.1).

Page 127: optimizacija rojem čestica na paralelnim arhitekturama

1,0E-02

1,0E-01

1,0E+00

1,0E+03 1,0E+04 1,0E+05 1,0E+06 1,0E+07

Broj čestica

Bro

j o

pti

miz

acij

a/s

GPU

CPU

Slika 8.14 Brzina izvođenja optimizacije za testnu funkciju 2

Osim opisane funkcionalnost, metoda optimizacije rojem čestica koristi i neke za nju svojstvene jezgre. Ovdje se misli na jezgre koje ažuriraju vrijednosti najbolje pozicije čestice i vrijednosti evaluacijske funkcije za tu poziciju. Ove jezgre nekoliko puta pristupaju globalnoj memoriji, a imaju samo nekoliko ALU instrukcija (8 odnosno 9) što ih čini izrazito neučinkovitima. Aposteriori analiza pokazala je da jezgra za ažuriranje najbolje pozicije čestice za velike količine podataka troši oko 37% vremena na ALU instrukcije i pristupa memoriji bez konflikata. Stupanj vektorizacije prilikom preslikavanja jezgre na VLIW arhitekturu iznosi 30%. Jezgra koja ažurira vrijednosti evaluacijskih funkcija za najbolju pronađenu poziciju čestica instancira se osjetno manji broj puta u odnosu na jezgru za ažuriranje najbolje pozicije čestice. Već radi korištenja vektorskih tipova broj instanci smanjen ja za faktor četiri u odnosu na broj instanci jezgre za ažuriranje najbolje pozicije čestice. Ovaj faktor potrebno je pomnožiti s brojem dimenzija funkcije koja se optimizira. Očito je da će ova jezgra teško postizati adekvatan broj instanci odnosno valova za učinkovito izvođenje. Iako je sama jezgra postigla relativno visok stupanj vektorizacije prilikom preslikavanja na VLIW arhitekturu (44.44%), ali udio vremena koje jezgre provode izvršavajući ALU instrukcije (manje od 10%) i česti memorijski konflikti prilikom zapisivanja čine ju neisplativom. Obje jezgre za ažuriranje mogle bi možda profitirati spajanjem u jedinstvenu jezgru čime bi se uklonila kašnjenja koja pokretanje jezgre nosi sa sobom, a i ukupni broj pristupa globalnoj memoriji mogao bi biti smanjen. Postoji mogućnost korištenja lokalne memorije za spremanje podataka koje čestice trebaju ažurirati najbolji položaj, na temelju rezultata ažuriranja vrijednosti evaluacijskih funkcija. Još bolje performanse mogle bi se postići spajanjem navedenih jezgra u jednu, pri čemu bi se vjerojatno opet mogla koristiti lokalna memorija za spremanje informacija o potrebi ažuriranja pojedine čestice.

Page 128: optimizacija rojem čestica na paralelnim arhitekturama

1,0E-03

1,0E-02

1,0E-01

1,0E+00

1,0E+01

1,0E+02

1,0E+02 1,0E+03 1,0E+04 1,0E+05 1,0E+06 1,0E+07

Broj čestica

Bro

j o

pti

miz

acij

a/s

GPU

CPU

Slika 8.15 Brzina izvođenja optimizacije za testnu funkciju 3 u 4-dimenzionalnom prostoru

Same evaluacijske funkcije relativno su jednostavne i imaju povoljan omjer ALU instrukcija u odnosu na broj pristupa globalnoj memoriji. Zanimljivo je prijetiti da unatoč smanjenoj preciznosti korištenje native_ funkcija osigurava brže izvođenje (Slika 8.16). Iz perspektive same optimizacije, funkcije (7.3) i (7.4) predstavljaju veliki problem zbog velikog broja lokalnih maksimuma koji su međusobno razdvojeni područjima relativno “dubokih“ lokalnih minimuma. Zato su se često događale situacije u kojima su čestice sve završile na lokalnom maksimum i više nisu mogle “preskočiti“ preko minimuma do boljeg rješenja. Kako su vrijednosti evaluacijske funkcije u susjednim (lokalnim) maksimumima relativno bliske, a površine lokalnih maksimuma veće od površine globalnog maksimuma, vjerojatnost da će čestica doći do “brijega“ na kojem je globani maksimum bila je prilično mala, pogotovo za funkciju (7.4). Povećanjem dimenzionalnosti problema, vjerojatnost rješavanja drastično opada i sve se više svodi na nasumičnu pretragu odnosno pogađanje rješenja, a manje na heuristiku. Veći broj čestica povećava vjerojatnost pronalaženja rješenja. Reinicijalizacijom cjelokupnog optimizatora, ako se najbolje pronađeno rješenje nije promijenilo dovaljan broj iteracija, ovaj problem donekle je ublažen. Reinicijalizacija samo dijelova rješenja (brisanje najboljih pronađenih pozicija, reinicijalizacija brzina ili položaja) nije se pokazalo učinkovitim. Veličina prostora rješenja koji se pretražuje nije igrala veliku ulogu jer se inicijalno zbijanje roja odvija relativno brzo za sve korištene evaluacijske funkcije.

Page 129: optimizacija rojem čestica na paralelnim arhitekturama

1,0E-03

1,0E-02

1,0E-01

1,0E+00

1,0E+03 1,0E+04 1,0E+05 1,0E+06 1,0E+07

Broj čestica

Bro

j o

pti

miz

acij

a/s

GPU

GPUnative_CPU

Slika 8.16 Brzina izvođenja optimizacije za testnu funkciju 3 u 8-dimenzionalnom prostoru

Ukupno gledajući, cjelokupni postupak optimizacije vjerojatno bi se mogao implementirati sa samo nekoliko poziva jezgre po ciklusu algoritma, ako bi se sve korištene matrične operacije i generiranja nasumičnih brojeva ostvarile unutar jedne jezgre. U tom slučaju, veći dio vremena provodio bi se obrađujući pojedine komponente pojedine čestice unutar samo jedne, relativno složene jezgre, koju bi bilo potrebno napustiti samo radi evaluacije dobivenih rješenja i pretraživanja roja za najboljim rješenjem. Inicijalizacija bi također mogla biti svedena na samo jednu jezgru i evaluaciju. Ovakva visoka specijaliziranost vjerojatno bi polučila puno bolje rezultate, ali bi istovremeno onemogućila ponovnu iskoristivost programskog koda. Za korištene evaluacijske fukcije (i dimenzionalnosti u kojima su implementirane) i sam proces evaluacije mogao bi bit integriran u jezgru koja provodi algoritam ažuriranja omogućavajući tako još veću učinkovitost pri rješavanju izrazito specifične problematike.

Page 130: optimizacija rojem čestica na paralelnim arhitekturama

9. Zaključak

Metoda optimizacije rojem čestica uspješno je implementirana na grafičkom procesoru i unatoč suboptimalnom pristupu koji će, po pitanju brzine izvođenja, davati nešto lošije rezultate, pokazala se bržom od slijedne implementacije. Kao i kod ranijih mjerenja, pokazalo se da su velike količine podataka ključne za ovakvu vrstu sklopovlja. Kada količine podataka prerastu broj paralelnih procesora, do izražaja će doći velike sposobnosti višedretvenosti ovakvih procesora koje će, skrivajući latencije, omogućiti daljnji rast performansi. Zbog navedenih sklopovskih karakteristika, kada bi se izračunala, učinkovitost (jednadžba (2.6)) bi mogla biti i viša od jedan.

Sama OpenCL norma, iako relativno jednostavna, može se kvalitetno primijeniti na korištene arhitekture. Ipak, kako je pokazno u poglavlju 5.2.1, za kvalitetno ostvarenje paralelnog algoritma potrebno je dobro poznavati posebnosti u građi i načinu funkcioniranja procesora na kojima će se koristiti. Univerzalnost u nekim slučajevima postaje mana norme, a implementacije pojedinih proizvođača mogu sa sobom donositi neočekivane greške ili probleme (povremena rušenja sustava, nepotrebno ograničenje na memorijske kapacitete…).

Unatoč svim navedenim negativnim stranama, na primjeru uspješnog, u potpunosti paralelnog ostvarenja relativno složenog algoritma kao što je metoda optimizacije rojem čestica, vidljivo je da će GPGPU koncept, kao i (heterogeni) masovno paralelni lako dostupni sustavi općenito, biti segment s velikim potencijalom za rast i razvoj.

Page 131: optimizacija rojem čestica na paralelnim arhitekturama

10. Literatura

[1] Flynn, M. Some Computer Organizations and Their Effectiveness. IEEE Trans. Comput. (1972.), C-21: 948; (http://www.cs.utah.edu/~kirby/classes/cs6230/Flynn.pdf)

[2] Amdahl, G. M. Validity of the single-processor approach to achieving large scale. Proceeding AFIPS '67 (Spring) Proceedings of the April 18–20, (1967.), spring joint computer conference, str. 483–485; (http://www-inst.eecs.berkeley.edu/~n252/paper/Amdahl.pdf)

[3] Gustafson J. L. Reevaluating Amdahl's Law. Communications of the ACM 31(5), 1988. str. 532-533.

[4] Sun, X.-H., Ni, L. Another view on parallel speedup, in: Proceedings of IEEE Supercomputing '90 (1990.), str. 324-333; (http://www.ece.eng.wayne.edu/~sjiang/ECE7610-winter-11/scalability.pdf)

[5] Sun, X.-H., Ni, L. Scalable problems and memory-bounded speedup, Journal of Parallel and Distributed Computing 19 (rujan, 1993.), str. 27-37; (http://www.cs.iit.edu/~scs/psfiles/scalable93.pdf)

[6] Karp, A. H., Flatt, H. P. Measuring Parallel Processor Performance. Communication of the ACM 33 (5), (1990) str. 539–543; (http://my.cs.utsa.edu/~boppana/Web/courses/pproc-common/papers/speedup-karp-flatt-cacm90.pdf)

[7] Lagae A., Lefebvre S., Drettakis G., Dutre P. Procedural noise using sparse gabor convolution. SIGGRAPH '09: ACM SIGGRAPH 2009 papers (New York, NY, USA, 2009), ACM, (2009.), str. 1–10. 2, 6; (http://www-sop.inria.fr/reves/Basilic/2009/LLDD09/LLDD09PNSGC_paper.pdf)

[8] MacQueen, J. B. Some Methods for classification and Analysis of Multivariate Observations. 1. Proceedings of 5th Berkeley Symposium on Mathematical Statistics and Probability. University of California Press. (1967.), str. 281–297; (http://www-m9.ma.tum.de/foswiki/pub/WS2010/CombOptSem/kMeans.pdf)

[9] Steinhaus, H. Sur la division des corps matériels en parties. Bull. Acad. Polon. Sci. 4 (12), (1956.), str. 801–804.

[10] Lloyd, S. P. Least square quantization in PCM. Bell Telephone Laboratories Paper. (1957.) Objavljeno kasnije: Lloyd., S. P. Least squares quantization in PCM. IEEE Transactions on Information Theory 28 (2), (1982.), str.129–137; (http://www.nt.tuwien.ac.at/fileadmin/courses/389075/Least_Squares_Quantization_in_PCM.pdf)

[11] Toni Anić, Eugen Gervais, JPEG kompresija slike, 1999., JPEG kompresija slike: Tehnički izvještaj, http://dosl.zesoi.fer.hr/seminari/1998_1999/anic-gervais/jpeg.htm, 2010.

[12] JPEG, 26. srpnja, 2001., JPEG, http://en.wikipedia.org/wiki/JPEG, 2010.

Page 132: optimizacija rojem čestica na paralelnim arhitekturama

[13] Lorensen, W. E., Cline, H. E. Marching Cubes: A high resolution 3D surface construction algorithm. Computer Graphics, Vol. 21, Nr. 4, (srpanj 1987.)

[14] Newman, T. S., Yi, H. A survey of the marching cubes algorithm. Computers & Graphics, Vol. 30, No. 5. (October 2006), str. 854-879; (http://www.proxyarch.com/util/techpapers/papers/survey%20of%20marching%20cubes.pdf)

[15] Kindratenko, V. Accelerating Cosmology Applications: from 80 MFLOPS to 8 GFLOPS in 4 steps, SRC Users meeting (srpanj, 2007.); (http://www.ncsa.illinois.edu/~kindr/projects/hpca/files/ppsc08_presentation.pdf)

[16] Kennedy, J.; Eberhart, R. Particle Swarm Optimization. Proceedings of IEEE International Conference on Neural Networks. IV, (1995.), str. 1942–1948; (http://www.cs.tufts.edu/comp/150GA/homeworks/hw3/_reading6%201995%20particle%20swarming.pdf)

[17] Shi, Y.; Eberhart, R.C. A modified particle swarm optimizer. Proceedings of IEEE International Conference on Evolutionary Computation, (1998.), str. 69–73; (http://dsp.szu.edu.cn/pso/ispo/download/a%20modified%20pso.pdf)

[18] Eberhart, R. C., Shi, Y. Comparing inertia weights and constrictionfactors in particle swarm optimization. Proceedings of the 2000 Congresson Evolutionary Computation (CEC), (2000.), str. 84-88

[19] Shi, Y., Eberhart, R. Empirical study of particle swarm optimization. Marco A. Montes de Oca Particle Swarm OptimizationIn Proceedings of the 1999 IEEE Congress on Evolutionary Computation, (1999.), str. 1945–1950; (http://staff.washington.edu/paymana/swarm/shi99-cec.pdf)

[20] Pedersen, M. E. H.; Chipperfield, A. J. Simplifying particle swarm optimization. Applied Soft Computing 10 (2), (2010.), 618–628; (http://www.hvass-labs.org/people/magnus/publications/pedersen08simplifying.pdf)

[21] Mendes, R. Kennedy, J., Neves, J. The fully informed particle swarm: Simpler, maybe better, IEEE Transactions on Evolutionary Computation, 8(3), (2004.), str. 204–210; (http://www.cpdee.ufmg.br/~joao/CE/ArtigosPSO/FI_PSO.pdf)

[22] Clerc, M., Kennedy, J. The particle swarm–explosion, stability, and convergence in a multidimensional complex space, IEEE Transactions on Evolutionary Computation, 6(1), (2002.), str. 58–73

[23] (razni autori) ATI Stream SDK OpenCL Programming Guide; (http://www.ljll.math.upmc.fr/groupes/gpgpu/tutorial/ATI_Stream_SDK_OpenCL_Programming_Guide.pdf)

[24] Khronos OpenCL Working Group. The OpenCL Specification, Version 1.1; (http://www.khronos.org/registry/cl/specs/opencl-1.1.pdf)

Page 133: optimizacija rojem čestica na paralelnim arhitekturama

[25] Khronos OpenCL Working Group. The OpenCL C++ Wrapper API, Version 1.1; (http://www.khronos.org/registry/cl/specs/opencl-cplusplus-1.1.pdf)

Page 134: optimizacija rojem čestica na paralelnim arhitekturama

11. Sažetak

Paralelni sustavi postaju sve raširenija pojava u računarstvu. Za njihovo programiranje potrebne su nove, univerzalne paradigme primjenjive na širok spektar arhitektura. Primjeri takvih novih pristupa bile bi RapidMind platforma i OpenCL norma.

RapidMind platforma predstavlja, starije, manje fleksibilno rješenje koje muči nedostatak standardizacije, ali primijenjena na nekoliko algoritama pokazala se isplativom u većini slučajeva.

OpenCL je otvorena norma novije generacije i većih mogućnosti, sa sposobnošću učinkovitog preslikavanja na mnoge masovno paralelne arhitekture.

OpenCL norma iskorištena je na višejezgrenim glavnim procesorima i modernim grafičkim procesorima složene arhitekture kako bi se ubrzalo izvođenje metode optimizacije rojem čestica.

Metoda optimizacija rojem čestica temelji se na iterativnim pokušajima poboljšanja potencijalnih rješenja tako da se simulira njihovo gibanje kroz domenu funkcije koja biva optimirana. Rješenja koja se nazivaju česticama sačinjavaju roj, i na početku algoritma inicijalizirana su na nasumične vrijednosti. Na vektore brzine čestica (a time i položaja čestica) utječe inercija čestice, najbolji položaj u kojem se čestica našla i najbolji položaj pronađen u cjelokupnoj fazi pretraživanja na razini cijeloga roja.

Navedena optimizacijska metoda idealna je za paralelizaciju na razini podataka jer se u većini koraka može obrađivati svaku komponentu svakog vektora neovisno. Također je potrebno ostvariti paralelne redukcijske operatore i generatore pseudo-slučajnih brojeva.

Rezultati pokazuju da paralelna implementacija na grafičkom procesoru u pravilu postaje isplativa za količine podatka veće od 103 do 105.

Zaključno, može se reći da je OpenCL donio novu paradigmu koja olakšava inače komplicirano programiranje paralelnih sustava, ali za dobre rezultate i dalje je potrebno uložiti puno truda u optimizaciju.

Ključne riječi: GPGPU, optimizacija rojem čestica, OpenCL, računalni paralelizam, RapidMind

Page 135: optimizacija rojem čestica na paralelnim arhitekturama

12. Particle swarm optimization on parallel processor architectures

Massively parallel processors such as GPUs and multi-core CPUs are becoming omnipresent in everyday life, but remain rather unutilized. Novel paradigms like RapidMind and OpenCL enable easier programming of such systems, enabling faster parallel algorithm development, and ultimately, better resource utilization.

The focus of this thesis is the development of a particle swarm optimization method that implements both data and task level parallelism using only massively parallel hardware (predominantly GPUs) for particle state update and evaluation phases alike. When compared to serial implementations, results show a significantly higher efficiency for large swarms or problems with high dimensionality, suggesting applicability exclusively in highly complex problem solving.

Keywords: computer parallelism, GPGPU, OpenCL, RapidMind, particle swarm optimization