• Nem Talált Eredményt

Háromszögek számának kiszámítása

Balassi Márton

2. Háromszögek számának kiszámítása

A továbbiakban nagy méretű irányított gráfok háromszögeinek számát szeretnénk meghatározni elosztott környezetben. A programot megvalósítjukHadoopésGiraph3 keretrendszerekben, melyek futási eredményeit összevetjük a szekvenciális algoritmuséval. A programkódok

3Specifikusan gráffeldolgozó elosztott keretrendszer, előnyeit a fejezet során röviden tárgyaljuk.

2. ábra.WordCountpéldafuttatás

és a mérések saját munkámat képezik, melyet egy méréssorozat részeként szerzőtársaimmal publikáltunk [2].

2.1. Gráfalgoritmusok elosztott környezetben

Amennyiben gráfalgoritmusokat elosztott környezetben szeretnénk kezelni feltehetjük, hogy az adat puszta mennyiségénél fogva egy-egy fizikai gép nem fog rendelkezni csak a gráf szerkezetének egy-egy részével és jellemzően ritka gráfokról beszélünk, emiatt a hagyományos szomszédsági és incidenciamátrix alapú reprezentációk nem jönnek szóba. A lista alapú reprezentációk közül a szomszédsági lista megfelelő számunkra, mely során egy csúcshoz eltároljuk a szomszédait, például soronként a következő formátumban:

node, neigh1, . . . neighn

Ebben az esetben a csúcsot kulcsként tudjuk használni a MapRe-duce keretrendszerben. Így viszont egyetlen gép memóriájában sem jelenik meg a gráf teljes egészében, kénytelenek vagyunk módosítani az algoritmusok megvalósításán is. Például egy mélységi keresés során egy csúcs szomszédainak szomszédairól már nincsen információnk, így ahhoz hogy iteratív algoritmusok futtatni tudjunk a gráfon több jobra

lesz szükségünk. Tulajdonképpen aMapfázisban a csúcsok üzenhetnek a szomszédaiknak, mely üzeneteket az egyes csúcsok aReducefázisban aggregálják.

Ahogyan azt az 1.1. alfejezetben kiemeltem,MapReduceprogramok egymás után láncolása esetén minden lépésben aHDFS-ről olvasunk és írunk, ezzel jelentős I/O költséget eredményezve. Ennek nagy része felesleges, ugyanis minden lépésben beolvassuk és kiírjuk a gráf szerkezetét, szerencsésebb lenne, ha legalább ezt a speciális adatot memóriában tartanánk. Ebből a feltételezésből indul ki a Google Pregel rendszer [10], melynek nyílt forráskódú implementációja Apache Giraphnéven elérhető [4]. Utóbbi a gráf memóriában tartásán felül lehetővé teszi, hogy mind a csúcsokat mind az éleket felcímkézzük (akár egészen összetett adatstruktúrákkal, mintegy állapotot adva nekik), programozáskor csak a csúcsok között küldendő üzeneteket kell megvalósítanunk, jelentősen csökkentve munkánkat.

2.2. Szekvenciális referencia implementáció

A keretrendszerek hasznosságának vizsgálata érdekében az algorit-must megvalósítottam szekvenciálisanJAVA nyelven. Itt a korlátozott mélységű keresésre [11] támaszkodva összeszámolom a pontosan 3 mély-ségben előforduló, a kezdőcsúccsal megegyező azonosítójú csúcsokat.

Ekkor minden háromszöget pontosan háromszor számolunk össze.

1: classSeqTriangleCounter

2: functionCountDepthNInsts(nodes, nodet, intn, graphg)

3: if n= 0then

4: if s=t then

5: return 1

6: else

7: return 0

8: for all termt∈linel do

9: functionEmit(t, 1)

10: end class

3. ábra.Korlátozott mélységű keresés

2.3. Háromszögszámolás MapReduce keretrend-szerben

MapReducekeret használatával jelentősen elbonyolódik az algorit-musunk, melynek elsődleges oka, hogy csak lokális információnk van a gráf szerkezetéről, ahogyan azt már a 2.1 tárgyaltam. Így két lépésre kell bontanunk az algoritmust. Használjuk szemléltető példaként a következő gráfot:

Reprezentáció 0 1 2

1 2 2 0 3

0 1

2 3

4. ábra.Példagráf az algoritmus szemléltetéséhez

2.3.1. Kétirányú éllista előállítása

Első lépésben elő kell állítanunk a kettő hosszú utakat, ennek egy lehetséges módja, ha minden csúcsban előállítjuk a ki-szomszédian kívül a be-szomszédait is tartalmazó kétirányú éllistát. Az információáramlást csökkentendő elég, ha minden csúcs csak a nála nagyobb azonosítójú szomszédainak küldi el az azonosítóját4. A példánkon ez a következőt jelentené, a többletinformáció áramlást szaggatott nyíllal jelöljük (5.

ábra).

A pszeudokódot tekintve azonban ezt az ábrán láthatónál némileg bo-nyolultabb módon érhetjük el, ugyanis kénytelenek vagyunk redundáns módon a gráf szerkezetét is tovább küldenünk az új, a fordított éleket tartalmazó információval együtt. Éppen emiatt kénytelenek vagyunk az {in, out} halmazból választott jelölőbittel meghatároznunk, hogy melyik irányú élről van éppen szó.

4Minden háromszöget pontosan egyszer fogunk megszámolni az algoritmus során ezzel a módszerrel.

0 1

2 0

1

5. ábra.BiDirectionMaplépés szemléletesen 1: classBiDirectionMap

2: functionMap(vertexv, neighbors [n1, n2, . . . nk])

3: for all vertexn∈neighbors do

4: functionEmit(v, (n, out))

5: if ID(n) > ID(v)then

6: functionEmit(n, (v, in))

7: end class

6. ábra.BiDirectionMappszeudokód

AReducerszerepe mindössze abban merül ki, hogy a kapott éleket a jelzőbit szerint csoportosítva írja a kimenetre.

2.3.2. Háromszögek ellenőrzése

Ebben a lépésben a 2.3.1 fejezetben leírt módon előállított

”háromszög jelöltekről” döntjük el, hogy valóban zárt vagy nyílt háromszögek-e. Az ellenőrzendő csúcsokat listaként, az információáram-lást nyilakkal vizualizálva a következő feladatot hajtjuk végre a példa-bemeneten:

ATrianglesMapsorán a csúcsok a kapottin jelzőbitű információt továbbküldik, ha az kisebb azonosítót jelöl, mint a sajátuk. Továbbá elküldik a ki-szomszédaikat saját részükre. ATrianglesReduce lépés-ben a csúcsok megnövelnek egy globális számlálót minden olyan kapott azonosítóra, amely szerepel a szomszédsági listájukban.

Így a globális számlálóba éppen a háromszögek száma kerül

0 [] 1 [0]

2 [1]

1

(a)TrianglesMap

0 + + 1

2

(b)TrianglesReduce 7. ábra.A háromszögek ellenőrzésének lépései 1: classTrianglesMap

2: functionMap(vertexv, edges [(n1, in), (n2, in), . . .(nk, out)])

3: outneighbors := [ ]

4: info := [ ]

5: for all edgee∈edgesdo

6: if e[2] =outthen

7: outneighbors.append(e[1])

8: else

9: info.append(e[1])

10: for all vertexn∈outneighorsdo

11: for allvertexi∈info do

12: functionEmit(n, i)

13: end class

8. ábra.TrianglesMappszeudokód

az algoritmus terminálásakor. Megjegyzem ehelyett a lépés helyett használhatnánk egyMapReducejobot, mely egyszerűen a számlálás programozási tételét valósítja meg, de ezzel csak tovább rontanánk ennek az implementációnak a futási idején.

2.4. Háromszögszámolás Pregel keretrendszerben

Annak érdekében, hogy elkerüljük a 2.3 fejezetben tárgyalt kellemet-lenségek jelentős részét az elosztott gráffeldolgozásra inkább alkalmas

Pregelkeretrendszerben is megvalósítjuk az algoritmust.

Ezen keret mottója, miszerint

”gondolkozzunk úgy, mint egy csúcs” jól kiemeli a használatakor alkalmazandó gondolkodási keretet.

Mivel a keretrendszer gondoskodik a gráf szerkezetének memóriában tartásáról programunk írásakor csupán a tényleges üzenetküldésekkel kell foglalkoznunk, miközben jelentős I/O terheléscsökkenést érünk el.

2.5. Implementáció

Láttuk a 2 fejezet során, hogy mennyire eltérőek az egyes imple-mentációk egymástól. Amíg a szekvenciális esetben „felülről nézzük”

a gráfot, globális információnk van róla, ha elosztottan vizsgálódunk kénytelenek vagyunk lokális információra szorítkozni. Az elosztott lehe-tőségek közül aGiraphmodellje lényegesen kényelmesebb gondolkodási sémát biztosít. Ugyan nem feltétlenül mérvadó, de tájékoztatás jelleggel álljon itt az egyes implementációk hossza.

Keret Implementáció hossza

szekvenciális 42 sor

Hadoop 273 sor

Giraph 130 sor

1. táblázat. Az egyes implementációk hossza