Razvoj mikroprocesora – konkurentno programiranje postaje nužnost

Još od 2004. / 2005. godine brzina mikroprocesora se (uglavnom) više ne povećava. I dalje se povećava broj tranzistora na čipu, ali to povećanje proizvođači mikroprocesora koriste za povećanje broja jezgri u mikroprocesoru. No, većina nas (koji barem ponekad pišemo programe) mora tek naučiti kako pisati konkurentne programe.

MIKROPROCESORI SU VIŠEJEZGRENI – A MI?

 

Još od 2004. / 2005. godine brzina mikroprocesora se (uglavnom) više ne povećava. I dalje se povećava broj tranzistora na čipu, ali to povećanje proizvođači mikroprocesora koriste za povećanje broja jezgri u mikroprocesoru. No, većina nas (koji barem ponekad pišemo programe) mora tek naučiti kako pisati konkurentne programe.

Gotovo sva današnja računala imaju više CPU-a, manja računala u obliku višejezgrenih (engl. multi-core) procesora, a veća i snažnija računala obično sadrže više procesora (isto višejezgrenih). Prednost je takvih računala da mogu paralelno izvršavati dva ili više (u ovisnosti od broja CPU-a) nezavisnih programa, ali mogu paralelno izvršavati i dijelove istog programa. U potonjem slučaju, ako su dijelovi programa nezavisni jedan od drugoga, tada programeri i dalje mogu pisati kod kao da se on odvija u jednom dijelu.

No, najčešće su dijelovi programa međusobno zavisni, jer čitaju / pišu u isto memorijsko područje ili koriste neki drugi dijeljeni resurs, pa je moguće da dođe do tzv. race conditiongdje rezultat izračuna ovisi o redoslijedu izvršenja programskih instrukcija iz različitih dijelova programa. Zbog toga je potrebna sinkronizacija dijelova programa. Sinkronizacija traži posebne programske tehnike, koje svoje porijeklo imaju još u 60-tim godinama prošlog stoljeća. Te se programske tehnike obično zovu imenom konkurentno programiranje. U članku se prikazuju kako razvoj mikroprocesora dovodi do sve veće potrebe za konkurentnim programiranjem.

Materijal znakovitog naslova “Not Your Father’s Von Neumann Machine: A Crash Course in Modern Hardware”, navodi da je Von Neumannov model računala u današnje vrijeme samo korisna apstrakcija, koja u mnogim detaljima odudara od stvarnih današnjih računala. Grafikon na slici 1 prikazuje rast performansi procesora 1978.-2005. godine. Vidljiva su tri razdoblja: razdoblje CISC računala do 1986., u kojem je prosječno godišnje povećanje performansi 25%; razdoblje velikog povećanja radnog takta procesora, gdje je povećanje performansi 52% godišnje; razdoblje višejezgrenih procesora, gdje se radni taktovi procesora više nisu povećavali:

rast_performansi

Slika 1. Rast performansi procesora od 1978. do 2005.

 

No, to ne znači da je prestao vrijediti Mooreov zakon, jer broj tranzistora po procesoru i dalje eksponencijalno raste, kako pokazuje slika 2. No, radni takt procesora praktički je prestao rasti oko 2005. Razlog za to je prije svega veliko povećanje potrošnje struje procesora na velikim brzinama. Zbog toga su se proizvođači okrenuli drugačijem načinu povećanja performansi procesora. Umjesto povećanja brzine, povećali su broj CPU-a na jednom mikroprocesorskom čipu, tj. počeli su proizvoditi višejezgrene procesore. No, dok smo kod jednojezgrenih procesora povećanjem takta procesora dobili linearno povećanje brzine programa, kod višejezgrenih procesora program najčešće moramo pisati drugačije da bismo iskoristili raspoložive jezgre, tj. moramo preći na konkurentno programiranje.

 povecanje_broja_tranzistora

Slika 2. Povećanje broja tranzistora i radnog takta procesora od 1973. do 2010.

 

Tablica 1 prikazuje razlike u veličini, brzini pristupa, propusnosti i nekim drugim karakteristikama različitih vrsta memorije, tj. procesorskih registara, priručne memorije (cache), glavne memorije i diskova (konkretni podaci vrijede za oko 2005. godinu). Vidi se da je glavna memorija 10 i više puta sporija od registara i priručne memorije:
razlike-memorija

 

Slika 3 pokazuje kako se eksponencijalno povećava jaz između performansi CPU-a i performansi glavne memorije (DRAM):

jaz_CPU

Slika 3. Povećanje jaza između performansi CPU-a i performansi DRAM-a

 

Naravno, nije nemoguće napraviti glavnu memoriju koja bi bila brza skoro kao registri procesora. Problem je što bi takva memorija bila puno skuplja. Naime, glavna memorija standardno koristi tzv. DRAM (Dynamic RAM) memoriju, koja za svaki bit memorije treba jedan tranzistor i jedan kondenzator. Daleko brža SRAM (Static RAM) memorija za svaki bit memorije treba šest tranzistora. Ako treba birati između sustava sa većom količinom sporije glavne memorije i sustava sa manjom količinom brže glavne memorije, u pravilu je bolje izabrati prvo, jer je magnetski disk daleko sporiji od najsporije glavne memorije. No, još je bolje napraviti hijerarhiju memorija, tj. koristiti bržu i manju SRAM priručnu memoriju (ili više razina te memorije) zajedno sa većom i sporijom DRAM glavnom memorijom. Budući da programi često koriste programske instrukcije koje se nalaze blizu jedna drugoj, a to često vrijedi i za podatke, priručne memorije značajno ublažavaju sporost pristupa glavnoj memoriji. Slika 4 prikazuje dva primjera korištenja priručne memorije, jedan jednostavniji i jedan složeniji (oba primjera prikazuju jednojezgreni procesor):

 

prirucna_memorija_1.1

 

Slika 4. Dva primjera korištenja priručne memorije:

a) lijevo je jednostavniji sustav sa jednom razinom priručne memorije;

b) desno je složeniji sustav sa tri razine priručne memorije, a prva razina ima odvojenu memoriju za podatke (L1d) i za instrukcije programa (L1i)

 

Osim uvođenja priručne memorije u mikroprocesorske čipove, dizajneri su od 1985. nalazili i druga rješenja za rješavanje jaza između brzine registara i brzine glavne memorije, kako bi u konačnici povećali brzinu izvođenja programa.

Jedan od načina koji je jako pridonosio povećanju performansi procesora sve dok je bilo moguće povećavati radni takt procesora (a to je postalo problematično zbog prevelikog zagrijavanja procesora) je tzv. paralelizam na razini instrukcijaILP (Instruction-Level Parallelism). ILP obuhvaća više mehanizama, od kojih je najvažniji pipelining (“cjevovod instrukcija”), gdje se u procesor paralelno dovodi više instrukcija (istog programa) koje se paralelno obrađuju, ali tako da se za svaku instrukciju istovremeno radi različita faza obrade. Na taj način, ako se npr. jedna prosječna instrukcija obrađuje u pet faza kroz pet radnih taktova, paralelnom obradom pet instrukcija istovremeno moguće je teoretski svesti prosječno vrijeme obrade instrukcije na samo jedan takt. Problem su, međutim, npr. instrukcije grananja, tj. sve instrukcije koje narušavaju sekvencijalni tok programa. Na rješavanje takvih problema kod ILP-a dizajneri procesora su trošili sve više i više tranzistora u procesoru.

Dosjetili su se da bi ILP mogli koristiti i na drugi način. Umjesto da paralelno obrađuju (u različitim fazama) instrukcije istog programa, mogli bi paralelno obrađivati instrukcije različitih programa, tj. instrukcije različitih dretvi (threads). Treba naglasiti da procesorske dretve (ili hardverske dretve) mogu odgovarati i dretvama i procesima operacijskog sustava. Takvo obrađivanje instrukcija naziva se multithreading (Intel koristi ime Hyper-Threading Technology), a prikazuje ga slika 5:

 multi_hreading

Slika 5. Hardverska višedretvenost (multithreading)

 

Multithreading (kao niti ILP) ne može činiti čuda. Naime, za razliku od višejezgrenih procesora, kod multithreadinga su duplirani samo neki elementi CPU-a. Npr. kod Intelove implementacije duplirani su samo procesorski registri. Dretve dijele i istu priručnu memoriju. Zbog toga procesor koji podržava dvije procesorske dretve nikako ne može imati 2 puta bolje performanse od istovrsnog procesora koji podržava samo jednu dretvu, već u praksi maksimalno do oko 1,3 puta (a i to vrlo rijetko). No, multithreading je ipak koristan, a koristi se i kod višejezgrenih procesora.

Kada je postalo jasno da se više ne može povećavati radni takt procesora (zbog eksponencijalnog povećanja potrošnje, pa onda i zagrijavanja procesora), a broj tranzistora po procesoru se i dalje eksponencijalno povećava, bilo je razumno početi smještati dva ili više CPU-a (jezgri) u jedan procesorski čip. U računalo se onda može ugrađivati i nekoliko višejezgrenih procesora, a svaka jezgra može imati i  procesorske dretve (dvije ili više). Dretve jedne jezgre tada dijele priručne memorije L1i i L1d, a jezgre dijele priručne memorije razine L2 i L3. Dva procesorska čipa obično nemaju zajedničku priručnu memoriju, te pristupaju glavnoj memoriji na uobičajen način – to je tzv. centralna djeljiva memorija. Takva se arhitektura glavne memorije ponekad naziva UMA (Uniform Memory Access), ali se češće naziva SMP (Symmetric Multi-Processors) arhitektura, iako se naziv SMP ponekad koristi za UMA i NUMA arhitekturu zajedno.

Osim arhitekture centralne djeljive memorije, postoji i arhitektura distribuirane djeljive memorije. Iako je i takva memorija djeljiva između svih procesora (pa neki kažu da je i to simetrična arhitektura, SMP), određeni dijelovi memorije su bliži određenim procesorima. Takva se arhitektura uobičajeno naziva NUMA (Non-Uniform Memory Access). Procesor ima najbrži pristup svom “lokalnom” dijelu glavne memorije, a brzina pristupa udaljenim dijelovima memorije ovisi o udaljenosti procesora od onoga procesora kojemu “pripada” taj dio memorije. U NUMA arhitekturi procesori nisu međusobno povezani (samo) preko memorijske sabirnice, nego su direktno povezani sa određenim brojem drugih procesora (tzv. Hyper Link; AMD koristi varijantu HyperTransport, tehnologiju licenciranu od nekadašnje firme Digital). Najčešće su povezani u obliku “hiperkocke”, kao što pokazuje slika 6. Npr., kod desne hiperkocke (sa 8 procesora, C = 3), procesor 1 najbrže pristupa vlastitoj memoriji, a nakon toga memorijama procesora 2, 3 i 5 (sa kojima je direktno povezan, C = 1), a najudaljeniji mu je procesor 8, pa je pristup njegovoj memoriji najsporiji (C = 3).

 povezivanje-procesora

Slika 6. Povezivanje procesora (i pripadajućih dijelova glavne memorije) u hiperkocke

 

Danas na tržištu postoji nekoliko procesora koji su višejezgreni, podržavaju višedretvenost i mogu se međusobno povezivati. Npr. IBM BlueGene/Q procesor (prikazan na slici 7) ima 18 jezgri, od kojih je jedna namijenjena za podršku operacijskom sustavu, a jedna je pričuvna. Koristi se (i) kod IBM superračunala Sequoia (završeno ove godine, za Lawrence Livermore National Labs), koje se sastoji od 100 000 čipova BlueGene/Q. Stručnjaci IBM-a predviđaju da će se kod navedenog superračunala svaka 3 tjedna pokvariti (u prosjeku) jedna jezgra, i zato je pričuvna jezgra vrlo značajna kod tog superračunala.

 IBM BlueGene/Q procesor

Slika 7. IBM BlueGene/Q procesor ima 18 jezgri

 

IBM BlueGene/Q podržava i tzv. hardversku transakcijsku memoriju (HTM). HTM, kao i softverska transakcijska memorija (STM), te hibridna transakcijska memorija (kombinacija HTM-a i STM-a), mogu značajno pomoći u implementaciji konkurentnih programa. Način rada transakcijske memorije podsjeća na rad baza podataka, tj. transakcije se na kraju mogu automatski potvrditi (ako je sve u redu) ili poništiti (ako je došlo do “sukoba” između konkurentnih dretvi, koje mijenjaju iste podatke u memoriji).

BlueGene/Q mikroprocesor je ipak prvenstveno namijenjen za izradu superračunala, pa je za “obično” programiranje značajnija najava Intel mikroprocesora Haswell arhitekture (prvi bi trebao izaći na tržište 2013. godine), koji će isto podržavati HTM. Time će se HTM uvesti u masovnu primjenu i očekuje se da će se olakšati pisanje konkurentnih programa. Za razliku od mikroprocesora IBM BlueGene/Q, Intelovi mikroprocesori će izgleda podržavati transakcijsku memoriju i između mikroprocesorskih čipova, a ne samo između jezgri jednog čipa.

Nažalost, kod višejezgrenih procesora (i višeprocesorskih sustava općenito) rast performansi sustava rijetko raste proporcionalno sa povećanjem broja jezgri, već je rast općenito manji. Za razliku od toga, povećanje radnog takta kod jednojezgrenih i jednoprocesorskih sustava u pravilu je povećavalo performanse skoro linearno. Zapravo, u nekim slučajevima se proporcionalno povećanje performansi može postići i kod višejezgrenih / višeprocesorskih sustava, npr. onda kada takvi sustavi služe za podršku baza podataka i aplikacijskih servera, gdje su pojedini procesi (ili dretve) međusobno nezavisni. No, kada pokušamo paralelizirati (razbiti u dretve) jedan program, najčešće dobijemo rezultat prikazan na desnoj strani slike 8, a ne onaj prikazan na lijevoj strani:

 

povecanje_broja_jezgri

Slika 8. Povećanje broja jezgri obično ne rezultira a) (skoro) linearnim povećanjem performansi (lijeva strana slike), već b) puno manjim od toga (desna strana slike)

 

Razlog za to objašnjava Amdahlov zakon. Ako je u nekom programu proporcija onih dijelova programa koji se mogu paralelno izvršavati jednaka (što znači da je proporcija dijelova koji se ne mogu paralelno izvršavati jednaka 1 – p), onda se povećanjem broja CPU-a može dobiti povećanje kao na slici 9.

 

Amdahlov zakon

Slika 9. Amdahlov zakon

 

Npr. ako imamo 10 procesora i ako je moguće paralelizirati 90% programa, onda je maksimalno povećanje brzine 5,26 puta, što je skoro dva puta manje od broja procesora. Ako je moguće paralelizirati 99% programa, onda je povećanje 9,17 puta. Nažalost, paralelizirati 99% programa (ili više) nije nimalo lako. Zapravo, uopće nije lako paralelizirati programe (na ispravan način).

No, neki noviji programski jezici, kao npr. Scala, omogućavaju lakše pisanje konkurentnih programa, jer uz uobičajene metode sinkronizacije (korištenje lokota) omogućavaju i korištenje STM-a, te tzv. aktore. Također, očekuje se da će novi procesori koji će podržavati HTM, olakšati pisanje konkurentnih programa.

 

Zlatko Sirotić, Istra informatički inženjering, Pula (“Mreža, 12/2012”)