Önálló laboratórium beszámolóaigroup.mit.bme.hu/system/files/onlab_feladat/2011/05/... ·...
TRANSCRIPT
Önálló laboratórium beszámoló
Párhuzamos programozási módszerek
bioinformatikai alkalmazása
Trosztel Mátyás
Konzulens: Hajós Gergely
Intelligens rendszerek BSc szakirány
Méréstechnika és Információs Rendszerek Tanszék
Budapesti Műszaki és Gazdaságtudományi Egyetem
2011. tavaszi félév
1
Tartalom I. Bevezetés................................................................................................................................. 2
1. A kitűzött cél ......................................................................................................................... 2
2. A rendszer ismertetése .......................................................................................................... 2
3. A feladat általánosítása.......................................................................................................... 2
II. Tervezés ................................................................................................................................... 3
1. Struktúrák és processzek ....................................................................................................... 3
2. BLOCK process ........................................................................................................................ 3
3. ADMIN process ....................................................................................................................... 3
4. CLIENT process ........................................................................................................................ 3
5. MPI (Message Passing Interface) ........................................................................................... 4
6. Alkalmazás központi cache esetében ..................................................................................... 4
III. Felmerülő problémák ............................................................................................................... 5
1. Jobkiosztás ............................................................................................................................ 5
2. Atomi írás, olvasás blokkon belül ........................................................................................... 5
3. Atomi írás, olvasás blokkok közt ............................................................................................ 5
IV. Tesztelés .................................................................................................................................. 7
1. A teszelés menete ................................................................................................................. 7
2. Eredmények .......................................................................................................................... 7
3. Adatok értelmezése ............................................................................................................... 9
V. Felhasználói segédlet ..............................................................................................................10
1. Program felépítése ...............................................................................................................10
2. Fordítás ................................................................................................................................10
3. Futtatás ................................................................................................................................10
4. Kliens függvények .................................................................................................................11
5. Példaalkalmazás ...................................................................................................................13
VI. További lépések ......................................................................................................................14
2
I. Bevezetés
1. A kitűzött cél Párhuzamos MCMC szimuláció számára egy központi cache létrehozása a valószínűségi táblák
tárolására.
Az MCMC algoritmus a Bayes-hálók felett fut, a szimuláció során azt teszteljük, hogy egy
véletlenszerűen választott Bayes-háló mennyire jól írja le az adatainkat. Előfordul, hogy egy
olyan a valószínűségi táblára van szükségünk, amit már egyszer kiszámoltunk (egy másik
processz által), ezeket az adatokat szeretnénk egy központi cache-ben tárolni, így elkerülve a
felesleges számításokat.
2. A rendszer ismertetése A szimulációkat a Genagrid3 (genagrid3.rmki.kfki.hu) nevű szerveren fogjuk futtatni,
amelynek a felépítése a következő:
62db blade
1TB memória (16GB alaplaponként)
512 mag (2db 4 magos processzor alaplaponként)
1. ábra Genagrid3 felépítése
3. A feladat általánosítása A rendszerben elérhető memória az alkalmazás számára transzparens módon történő
kezelése, független attól, hogy melyik alaplapon található a memória és melyik alaplapon futó
processz igényli a hozzáférést.
Az alkalmazásnak egy olyan rendszert kell látnia, ami egy alaplapon tartalmazza a
rendszerben található összes erőforrást.
2. ábra virtuális gép
3
II. Tervezés
1. Struktúrák és processzek Három féle processzt különböztetünk meg, ezek:
BLOCK
A fizikai allokációért és a kliensek írás/olvasás igényeinek kiszolgálásáért felelős.
ADMIN
Feladata a kezdeti kommunikáció vezénylése (kézfogás), a terhelés elosztása a BLOCK
processzek között és az allokációs tábla biztosítása a kliensek felé.
CLIENT
A felhasználói alkalmazás.
Az allokációs tábla struktúrái:
PIECE
Egy blokkon belüli memóriadarabot jelöl, amely tartalmazza a BLOCK processz
azonosítóját, a memória területre mutató pointer azonosítóját és a lefoglalt terület
méretét.
CHUNK
A blokkok által lefoglalt memóriadarabok összefűzésére szolgál. Tartalmazz egy
változót, amely megmondja mennyi memóriadarab került összefűzésre. Egy tömböt,
ami ezeket a darabokat tartalmazza (PIECE), egy azonosítót és a lefoglalt terület
méretét.
2. BLOCK process Ez a rendszer kritikus pontja, a kliensek kérelmeit a lehető leggyorsabban kell kiszolgálni a
legkisebb késleltetés elérése érdekében. Ezért ez a processz semmilyen számítást nem végez.
Az adatok pontos helyét a kliensek számolják ki az allokációs tábla segítésével.
A kiszolgálás nem blokkoló utasításokkal történik, azaz nem várjuk meg amíg az adat fizikailag
elér a kliensig (olvasás), vagy megérkezik a klienstől (írás).
3. ADMIN process Az ADMIN processz összeállítja az allokációs táblát, úgy hogy a BLOCK processzek egyenlő
mértékben legyenek terhelve. Számon tartja, hogy a klienseknél ez a tábla az aktuális értéket
tükrözi-e, amennyiben változás történik (új allokáció, memória felszabadítás vagy kibővítés),
akkor üzenetet küld a kliensek számára, hogy frissítsék az allokációs tábláikat.
4. CLIENT process Tartalmazza a memóriakezelő függvényeket, melyek az allokációs tábla alapján kiszámolja az
adatok fizikai helyét, és lekérdezi ezt a megfelelő BLOCK processzektől. Ezeket a függvényeket
fogják alkalmazni a felhasználói programok.
4
5. MPI (Message Passing Interface) Ez egy általános célú üzenetküldő interfész, amely támogatja a P2P és a kollektív
kommunikációt.
Az MPI kifejlesztésének célja az volt, hogy egy széles körben használható, hordozható
szabványt készítsenek üzenetek küldésére. A szabványban specifikált rutinkönyvtár
implementációi két csoportba oszthatók. Az első csoportba a hardvergyártók által készített,
az általuk gyártott hardverre erősen optimalizált változatok tartoznak. Ezzel ellentétes
nézőpontot tükröznek a második csoport implementációi, ugyanis ezek fejlesztésének
elsődleges célja a lehető legtöbb architektúra támogatása egy függvénykönyvtárban. Sajnos
a különböző implementációival készített MPI programok egymással kommunikálni nem
képesek, így a programok csak forrásszinten hordozhatóak. Az MPI erősségei közé tartoznak a
kommunikációs primitívek nagy száma, az erősen típusos üzenetküldés és a kollektív
kommunikációs lehetőségek.[1]
6. Alkalmazás központi cache esetében Egy átmeneti tár egyik legfontosabb tulajdonsága a késleltetés. Hiába tudjuk tárolni az
adatot, ha annak lekérdezése ugyanannyi, vagy éppen több időt vesz igénybe, mint az adott
érték kiszámítása. Ezért a tervezés során a késleltetést helyezzük előtérbe a minél nagyobb
sávszélesség elérése helyett.
A központi cache, akkor fog teljesítménynövekedést hozni, ha fennáll a következő:
Az egyenlet jobb oldala a következőt írja le: Lekérjük a cache-ből a számításnak megfelelő
adatot és megállapítjuk, hogy az adat valós-e (kiszámolta-e már valaki ezt az értéket), ez
valószínűséggel igaz lesz, ekkor felhasználjuk. Amennyiben nem áll rendelkezésre a számítás
eredménye (1- valószínűséggel), akkor ki kell számolnunk az adott értéket és vissza kell
írnunk a cache-be, hogy a következő processz már fel tudja használni.
Érdemes megjegyezni, hogy ha az olvasás késleltetés ideje alatt hasznos számításokat tudunk
futtatni (például a következő iteráció számításainak előkészítése), akkor jelentősen javítunk a
központi cache hatékonyságán.
*1+ A PVM és az MPI új elemeinek vizsgálata - Farkas Gergely
5
III. Felmerülő problémák
1. Jobkiosztás Az optimális jobkiosztás az lenne, ha teljes blade-eket tudnánk lefoglalni, amelyeken a BLOCK
processz mellett csak kliensek futnak, így a maximális számú kliens tudja elérni azt lokálisan, a
legkisebb késleltetéssel.
3. ábra optimális kiosztás
Sajnos a condor jobkiosztó párhuzamos futtatás támogatása ilyen problémára nem ad
megoldást. Nem támogatja például egynél több futtatható file indítását, vagy egy teljes blade
lefoglalását.
Az első problémára a megoldás egy külön script file, amely összegyűjti a condor által lefoglalt
node-okat, ezeket hostfile-ba írva és az mpirun-nak átadva el tudjuk indítani a kívánt
processzeket. A hostfilet úgy állítjuk össze, hogy a BLOCK processzek ne kerüljenek ugyanarra a
blade-re (ha nem tudunk teljes blade-eket lefoglalni, akkor nincs rá garancia, hogy ez sikerül).
A teljes blade lefoglalását egy új követelmény-parométer bevezetésével lehet megoldani,
melyhez a condor konfigurációs fájljait kell módosítani.
2. Atomi írás, olvasás blokkon belül Több adat olvasása esetén fontos lehet az atomi művelet biztosítása annak elkerülésére, hogy
inkonzisztens adatokat olvassunk.
Egy listában tároljuk, hogy éppen milyen művelet (olvasás/írás) zajlik és ez melyik területet
érinti. Amikor befut egy új kérés, akkor a listát végignézve megállapítjuk, hogy van-e ütközés.
Ha van, akkor bevárjuk az érintett hozzáférések teljes lefutását és csak ezek után végezzük el
a kért műveletet.
3. Atomi írás, olvasás blokkok közt Ennek megoldására több lehetőségünk is van az írás és olvasás igényektől függően, ezek:
1. Ha egy fix adathosszal dolgozunk, amit atomnak szeretnénk tekinteni (pl. egy adott
struktúra, vagy egy konstans elemű tömb) akkor a memóriadarab méreteit úgy
definiáljuk (PIECE), hogy ennek az adatméretének egész számú többszöröse legyen. Így
sosem fog átlógni egy atomi adat egy blokk határon sem, így a második pontban leírt
módszer értelmezhető rá.
6
2. Ha változó adathosszon szeretnénk alkalmazni, de úgy, hogy ezt mindig az írásnak
megfelelően olvassuk ki (tehát nem olvasunk ki belőle részadatokat), akkor elégséges
csak az első érintett blokkra zárat tenni, majd az összes adat kiírása után
felszabadítani azt.
3. Ha tetszőleges módon szeretnénk atomi írás/olvasás műveletet, akkor minden
érintett blokk megfelelő területét zárolni kell, de ezt nem párhuzamosan tesszük meg
a deadlock elkerülése végett, hanem az adatok írásának sorrendjében, úgy hogy
mindig megvárjuk a sikeres zárolás visszajelzését.
1. Első blokk zárolása és a visszaigazolás megvárása.
2. Kiírjuk az első blokkba szánt adatot, miközben (párhuzamosan) a második
blokk zárolását kérjük. Megvárjuk a sikeres zárolást.
3. Így tovább az összes érintett blokkon.
4. Az összes zár feloldása.
Amennyiben tehetjük, az első megoldást alkalmazzuk, mert ez nem visz plusz késleltetést a
rendszerbe. A második megoldás egy plusz üzenetváltást eredményez (valójában kettőt, de a
feloldás sikerességét nem kell megvárnunk). A harmadik megoldás a leglassabb, itt annyi
többlet üzenetváltás keletkezik, ahány blokkot érint az írás.
7
IV. Tesztelés
1. A teszelés menete A jobkiosztót nem sikerült az optimális kiosztásnak megfelelően beállítani, nagy kliens és
blokk szám mellett bizonytalanná vált a futás és problémák merültek fel a késleltetésben.
Ezért az alábbi tesztekben csak maximálisan 40 kliens és 12 blokk fog szerepelni.
A tesztalkalmazás parométerezése a következő:
BLOCK processzek száma
CLIENT processzek száma
Véletlenszerű írások/olvasások száma
Az adat mérete
A lekérdezések közt eltelt idő (számítási szimuláció)
2. Eredmények Az alábbi tesztben 8 byte (double) értéket olvasunk 0us és 1us számítási szimulációval.
8B, 0us 5 client 15 client 25 client 40 client
8B, 1us 5 client 15 client 25 client 40 client
1 block 8us 163us 381us 785us
1 block 8us 8us 8us 10us
3 block 6us 139us 250us 429us
3 block 3us 4us 6us 8us
6 block 33us 171us 162us 258us
6 block 4us 4us 6us 8us
12 block 36us 111us 180us 278us
12 block 7us 7us 8us 11us
0us
2us
4us
6us
8us
10us
12us
5 client 15 client 25 client 40 client
1 block 3 block 6 block 12 block
0us
100us
200us
300us
400us
500us
600us
700us
800us
900us
5 client 15 client 25 client 40 client
1 block 3 block 6 block 12 block
8
Az alábbi tesztben 1MB méretű adatot olvasunk és írunk 1us és 10ms számítási szimulációval.
1MB, 1us 5 client 15 client 25 client 40 client
1MB, 10ms 5 client 15 client 25 client 40 client
1 block 1 451us 3 686us 6 443us 10 864us
1 block 1 040us 1 122us 1 714us 5 054us
3 block 1 270us 3 313us 5 231us 8 192us
3 block 981us 1 046us 1 199us 1 647us
6 block 1 246us 3 171us 3 777us 5 857us
6 block 876us 1 039us 1 098us 1 314us
12 block 1 177us 2 299us 3 486us 4 992us
12 block 889us 998us 1 078us 1 228us
Elérhető sávszélesség MB/s-ban:
1MB, 1us 5 client 15 client 25 client 40 client
1MB, 10ms 5 client 15 client 25 client 40 client
1 block 753 435 320 239
1 block 1 009 968 701 338
3 block 858 509 398 318
3 block 1 057 1 016 919 743
6 block 899 513 452 355
6 block 1 164 1 017 974 859
12 block 920 589 460 381
12 block 1 149 1 043 969 895
0us
2 000us
4 000us
6 000us
8 000us
10 000us
12 000us
5 client 15 client 25 client 40 client
1 block 3 block 6 block 12 block
0us
1 000us
2 000us
3 000us
4 000us
5 000us
6 000us
5 client 15 client 25 client 40 client
1 block 3 block 6 block 12 block
0
100
200
300
400
500
600
700
800
900
1 000
5 client 15 client 25 client 40 client
1 block 3 block 6 block 12 block
0
200
400
600
800
1 000
1 200
1 400
5 client 15 client 25 client 40 client
1 block 3 block 6 block 12 block
9
3. Adatok értelmezése Látható, hogy több BLOCK process futtatása esetén a terhelés jobban eloszlik, és jobban
teljesít a rendszer. Olyan esetekben ahol a blokkok száma nagyobb, vagy közel annyi mint a
klienseké megfigyelhetünk egy kis késleltetésbeli visszaesést kis mennyiségű adatok olvasása
esetén. Ez részben a nem optimális jobkiosztásnak köszönhető, részben pedig azaz oka, hogy
kevesebb memóriát ér el lokálisan.
Ha az adatméretet növeljük, akkor a sávszélesség is javul, mert közel ugyanazzal az overhead-
del több adatot tudunk írni/olvasni.
10
V. Felhasználói segédlet
1. Program felépítése
.\bin block és admin futtatható fájljai.
.\build átmeneti fájlok, amik program fordítása során keletkeznek.
.\example egy példa kliens programra.
.\include header fájlok.
.\lib kliens könyvtár fájl.
.\src program forrása.
2. Fordítás A fordításhoz szükség van az MPI fordítójára (mpicc). Ez valójában csak egy csomagoló
(wrapper), az alapértelmezett fordítót fogja használni (ami genagriden az icc).
A make parancsot kiadva a következő fontosabb file-ok keletkeznek:
bin/admin
bin/block
lib/libsmclient.a
Kliens alkalmazások fordítása, a következőképpen történhet:
mpicc –Wall testclient.c –lrt –lsmclient –L$(SMLIB) –I$(SMINCLUDE) –o testclient
A kód c++ kompatibilis, így mpic++-al is fordítható, ha az alkalmazás megkívánja.
3. Futtatás Futtatásra három segédscript áll rendelkezésre:
smrun – a szerverre tervezett, manuális futtatás, gyors tesztekre alkalmas.
smrun_desktop – kisebb tesztek futtatására használható desktop gépen.
smrun_condor – ez a condor segédscriptje, a submit file-ban ezt kell megadni.
Parométerezésben megegyeznek, amely a következő:
BLOCK processzek száma
felhasználható memória BLOCK processzenként
CLIENT processzek száma
kliens alkalmazás
[alkalmazás argumentumai]
Példa: smrun_desktop 1 3GB 10 example/testclient
11
4. Kliens függvények Az alábbi függvények az alapjai a típusos függvényeknek (így ezeket ebben a formában
valószínűleg soha nem fogjuk használni):
SM_Handle SM_Alloc(SM_UI64 size)
Tömb allokálása, a méretet byte-okban kell megadni. A memória leíróval tér vissza.
SM_Handle SM_AllocInit(SM_UI64 size, SM_Byte* buff, SM_UI bsize);
Tömb allokálása és egy tetszőleges adattal történő inicializációja.
void SM_Extend(SM_Handle chunkID, SM_UI64 newsize);
Tömb újraméretezése. Az új terület a tömb végén keletkezik.
void SM_ExtendInit(SM_Handle chunkID, SM_UI64 newsize, SM_Byte* buff, SM_UI
bsize);
Tömb újraméretezése és az új terület inicializálása.
void SM_Fill(SM_Byte* buff, SM_UI buffsize, SM_Handle chunkID, SM_UI64
offset, SM_UI64 size);
A megadott terület feltöltése egy adott értékkel.
SM_UI64 SM_ArraySize(SM_UI chunkID);
A tömb méretét adja visza.
SM_UI64 SM_UsedMemSize(void);
A felhasznált memória méretét adja vissza.
void SM_Read(SM_Byte* buff, SM_Handle chunkID, SM_UI64 offset, SM_UI size)
Olvasás a memóriából.
void SM_Write(SM_Byte* buff, SM_Handle chunkID, SM_UI64 offset, SM_UI size)
Írás a memóriába.
A típusos függvények előállítására egy makró áll rendelkezésre, ami a következőket definiálja:
SM_Handle SM_Alloc##name(SM_UI64 count)
SM_Handle SM_AllocInit##name(SM_UI64 count, type* buff)
SM_Handle SM_AllocInitNull##name(SM_UI64 count)
SM_Extend##name(SM_Handle chunkID, SM_UI64 newcount)
SM_ExtendInit##name(SM_Handle chunkID, SM_UI64 newcount, type* buff)
SM_ExtendInitNull##name(SM_Handle chunkID, SM_UI64 newcount)
SM_Read##name(type* buff, SM_Handle chunkID, SM_UI64 from, SM_UI count)
SM_Write##name(type* buff, SM_Handle chunkID, SM_UI64 from, SM_UI count)
SM_Fill##name(type* buff, SM_Handle chunkID, SM_UI64 from, SM_UI64 count)
SM_ValidData##name(type* buff,SM_UI count)
SM_Read##name##1(type* buff, SM_Handle chunkID, SM_UI64 index)
SM_Write##name##1(type* buff, SM_Handle chunkID, SM_UI64 index)
SM_ValidData##name##1(type* buff)
SM_ArraySize##name(SM_Handle chunkID)
Látható hogy a függvények leegyszerűsödtek, a méret helyett az elemszám, az offset helyett
az index került előtérbe.
12
A makró definiál egy NULL elemet is, amit megkülönböztetett elemként kezelhetünk, ezt
használja fel az SM_ValidData függvény is.
Az alap változók makrós függvénydefiniciója a következő:
SM_CREATE_ARRAY_TYPE(Byte,unsigned char,UCHAR_MAX);
SM_CREATE_ARRAY_TYPE(B,unsigned char,UCHAR_MAX);
SM_CREATE_ARRAY_TYPE(Int,int,INT_MAX);
SM_CREATE_ARRAY_TYPE(I,int,INT_MAX);
SM_CREATE_ARRAY_TYPE(UnsignedInt,unsigned int,UINT_MAX);
SM_CREATE_ARRAY_TYPE(UI,unsigned int,UINT_MAX);
SM_CREATE_ARRAY_TYPE(Long,long,LONG_MAX);
SM_CREATE_ARRAY_TYPE(L,long,LONG_MAX);
SM_CREATE_ARRAY_TYPE(UnsignedLong,unsigned long,ULONG_MAX);
SM_CREATE_ARRAY_TYPE(UL,unsigned long,ULONG_MAX);
SM_CREATE_ARRAY_TYPE(UnsignedLongLong,unsigned long long,ULLONG_MAX);
SM_CREATE_ARRAY_TYPE(ULL,unsigned long long,ULLONG_MAX);
SM_CREATE_ARRAY_TYPE_CMP(Double,double,NAN,SM_CMP_NAN);
SM_CREATE_ARRAY_TYPE_CMP(D,double,NAN,SM_CMP_NAN);
SM_CREATE_ARRAY_TYPE_CMP(Float,float,NAN,SM_CMP_NAN);
SM_CREATE_ARRAY_TYPE_CMP(F,float,NAN,SM_CMP_NAN);
A túl hosszú függvénynevek elkerülése érdekében a változónevek rövidített formáját is
definiáljuk.
13
5. Példaalkalmazás Az alkalmazás létrehoz egy 300 millió elemű double tömböt, majd ráébred hogy mégis több
elemre lenne szüksége, és kibővíti 450 millió elemesre. Ezek után véletlenszerűen megnézi
hogy helyesen inicializálódott-e a tömb a definiált NULL (NAN) értékkel. Következő lépésben
véletlenszerűen olvas és ír 50000-szer. Írásnál egy 0 és 100 közötti számot helyez el a
tömbben. Olvasásnál, ha olyan helyről olvas ahová már valaki írt, akkor kiírja a megtalált
értéket.
#include "client.h" #include "arraytypes.h" int main (int argc, char **argv) { SM_UI64 i; SM_Handle a1; double wbuff; double buff; int iter = 50000; SM_UI nDouble = 300*1000*1000; // Init SM_ClientInit(&argc, &argv); srand((unsigned int)time(0)*SM_GetRank()); a1 = SM_AllocInitNullDouble(nDouble); // Shared // Oops, we need more doubles nDouble *= 1.5; SM_ExtendInitNullDouble(a1,nDouble); // Random data check test if array initialized correctly for (i=0; i < iter; i++) { SM_UI64 off = ((double)rand()/RAND_MAX) * (nDouble); SM_ReadD1(&buff,a1,off); if (SM_ValidDataDouble1(&buff)) { SM_Print(SM_PL_ERROR,"Ouch, %f is not NAN at %llu",buff,off); SM_Abort(-1); } } // Wait for clients to finish the first test SM_Barrier(); // Random read/write test for (i=0; i < iter; i++) { SM_UI64 off = ((double)rand()/RAND_MAX) * (nDouble); if (rand() > RAND_MAX/2) { wbuff = ((double)rand()/RAND_MAX)*100; SM_WriteDouble1(&wbuff,a1,off); } else { SM_ReadDouble1(&buff,a1,off); if (SM_ValidDataDouble1(&buff)) { SM_Print(SM_PL_INFO,"Wow, I found something: %3.2f at %llu",buff,off); } } } SM_ClientFinalize(); return 0; }
14
VI. További lépések Adatfájlok memóriába töltésének támogatása.
A jobkiosztó megfelelő konfigurálása után az elkészült kódot felhasználni az MCMC
szimulációban. Amennyiben sikerrel jár az alkalmazása, úgy a nagyobb memóriát igénylő
számítások során is fel lehet használni.