Sunteți pe pagina 1din 11

Universitatea Tehnică “Gheorghe Asachi” Iaşi

Facultatea de Automatică şi Calculatoare


Calculatoare şi Tehnologia Informaţiei

Map Reduce

Proiect la disciplina
Algoritmi Paraleli Distribuiti

Student: Iordan Alexandru


Grupa: 1409B

1
Capitolul 1. Enunţul temei de casă
În cadrul oricărui sistem de regăsire a informaţiilor, colecţia de date ţintă este re-
organizată (sau re-modelată) pentru a optimiza funcţia de căutare. Un exemplu în acest sens este
dat chiar de motoarele de căutare a informaţiilor pe Web: colecţia de documente este stocată sub
forma unui index invers.
Paşii implicaţi în construirea unui astfel de index invers sunt următorii:
1. fiecare document din cadrul colecţiei ţintă (identificat printr-un docID) va fi
parsat şi spart în cuvinte unice (sau termeni unici); se obţine în finalul acestui pas
o listă de forma < docIDx , {term1 : count1, term2 : count2 , . . . , term n : countn} >
(index direct – countk înseamnă numărul de apariţii al termenului k);
1. fiecare listă obţinută în pasul anterior este spartă în perechi de forma: < docID x ,
{termk : countk} >; pentru fiecare astfel de pereche, se realizează o inversare de
valori, astfel încât să obţinem: < termk , {docIDx : countk} >;
2. perechile obţinute în pasul anterior sunt sortate după termk (cheie primară),
docIDx (cheie secundară);
3. pentru fiecare termk se reunesc < termk , {docIDx : countk} >, astfel încât să
obţinem: < termk , {docIDk1 : countk1, docIDk2 : countk2, . . . , docIDkm : countkm} >
(indexul invers).
Tema de casă constă în implemenatrea unei soluţii MPI de tip MapReduce pentru
problema construirii unui index invers pentru o colecţie de documente text.
Aplicaţia de test va primi ca parametrii de intrare numele unui director ce conţine fişiere
text (cu extensia ”.txt”) şi un nume de director pentru stocarea datelor de ieşire şi va genera pe
post de răspuns un set de fişiere text ce conţin indexul invers corespunzător colecţiei de
documente de intrare.

2
Capitolul 2. Ce este MapReduce?

MapReduce este o paradigmă de programare folosită pentru procesarea unor cantități


mari de date în mod paralel și distribuit pe un cluster.

Un program MapReduce este compus dintr-o procedură Map() care selectează și sortează
datele și o procedură Reduce() care îndeplinește operația de însumare a rezultatelor. Sistemul
MapReduce administrează serverele distribuite, se asigură că diferitele sarcini sunt rulate în
paralel, administrează comunicarea și transferul de date dintre diferitele componente ale
sistemului și oferă suport în cazul în care apar erori în sistem.

Fluxul de date de intrare format dintr-o cheie și o valoare asociată este transformat de
Map într-o altă pereche de cheie și valoare, dar de ieșire. Nodul master preia input-ul, îl
segmentează în subtask-uri pentru a le distribui celorlalte noduri. Celelalte noduri pot replica
comportamentul într-o strctură sub formă de arbore, pe mai multe niveluri. După ce este procesat
taskul, se trimite un răspuns către nodul master. Operațiile de mapare sunt independente, aspect
care explică paralelizarea, dar aceatsa este dependentă de numărul de procesoare din vecintatea
fiecărei surse.

Tranformarea Reduce are rolul de a prelua toate valorile unei chei specificate și de a
genera rezultatul redus, dar cu o nouă listă.Reducerea se poate realiza într-o manieră similară
fazei de mapare. Avantajul folosirii acestui tip de algoritm este ilustrat de faptul că un set de
servere este capabil să sorteze un petabyte dedate doar în câteva ore, spre deosebire de algoritmii
secvențiali.

2.1. Etape
Algoritmul MapReduce conţine 2 sarcini de lucru importante: Maparea şi Reducerea:

1. Maparea – preia informațiile din fișierele care sunt datele de intrare. Procesul
MASTER trimite la procesele WORKER fișierul din care sunt preluate cuvintele.
Procesele WORKER creează fișiere pentru fiecare cuvânt din fișierul input.
Fișierele create au urmatoarea denumire: (cuvant)_(fisierul din care se afla).txt.
Aceste fișiere conțin un număr de linii egal cu numărul de apariții in fișierele de
input.

2. Reducerea - după ce procesul MASTER primește confirmarea că procesele


WORKER sunt libere acesta le spune ca fiecare proces WORKER sa ia câte un
cuvânt, să vadă în ce fișiere se află, de câte ori se află în fiecare apoi să creeze un
fișier de tipul (cuvant).txt care are conținutul de tipul rezultatului final din figura
urmatoare:

Figura 1.1: Paradigma MapReduce

3
2.2. Avantajele algoritmului
Avantajul major al algoritmului MapReduce îl reprezintă uşurinţa în scalarea procesării
de date peste mai multe noduri computaţionale. Sub modelul „MapReduce”, primitivele de
procesare de date sunt denumite mappers („mapatori”) şi reducers („reductori”). Descompunerea
unei aplicaţii pentru procesare de date în mapatori şi reductori este, uneori, non-trivială. Dar,
odată ce scriem o aplicaţie în forma MapReduce scalarea acelei aplicaţii pentru a se putea
executa peste sute, mii, sau chiar zeci de mii de maşini într-un cluster reprezintă doar o chestiune
de modificarea unei configurări. Această scalabilitate simplă a atras atenţia multor programatori
în a folosi modelul MapReduce. [1]
MapReduce are o toleranţă mare la defecte. Ca un sistem de tip MapReduce să pice,
trebuie să „pice” toţi workerii la o anumită fază a algoritmului. [2]

4
Capitolul 3. Prezentarea soluției
Tema de casă constă în implementarea unei soluții MPI de tip MapReduce pentru
problema construirii unui index invers pentru o colecție de documente text. Aplicația primește ca
parametru de intrare numele unui director ce conține fișiere text (cu extensia „.txt”) și un nume
de director pentru stocarea datelor de ieșire și va genera pe post de răspuns un set de fișiere text
ce conține indexul invers corespunzător colecției de documente de intrare.
La rularea aplicației, un proces prestabilit, creat de MPI, își asumă rol de coordonator, iar
celelalte sunt workeri. În cadrul soluției, metoda MapReduce este aplicată de două ori: pentru
crearea indexului direct și, mai apoi, a indexului indirect. Astfel, procesarea este organizată în 4
etape: mapare și reducere pentru indexul direct, mapare și reducere pentru indexul indirect. A
doua reducere, pentru obținerea indexului indirect, este precedată de sortare și amestecare,
operații efectuate de master.
Cât timp mai sunt fișiere de prelucrat, procesul Master trimite sarcini la procesele Worker
care sunt libere.
Procesele Worker se află într-o buclă setată de către Master. Acestea vor ieși din buclă
când procesul Master le spune că toate sarcinile au fost îndeplinite.

5
Capitolul 4. Implementare și pseudocod
MASTER

printf(" --- Etapa de MAPARE --- \n");


int i;
statusProcese = (int*)calloc(numarProcese, sizeof(int));
for (i = 0; i < pachet.numarFisiere; ++i) {
MPI_Recv(&mesaj, 1, MPI_INT, MPI_ANY_SOURCE, LIBER,
MPI_COMM_WORLD, &status);
MPI_Send(fisiereValide[i], 100, MPI_CHAR, status.MPI_SOURCE,
MAPARE, MPI_COMM_WORLD);
statusProcese[status.MPI_SOURCE] = 1;
}
printf("Verific daca toate procesele WORKER sunt libere: ");
while(CateProceseLucreaza(statusProcese, numarProcese) != 0){
MPI_Recv(&mesaj, 1, MPI_INT, MPI_ANY_SOURCE, LIBER,
MPI_COMM_WORLD, &status);
statusProcese[status.MPI_SOURCE] = 0;
}

// Deblochez procesele MUNCITOR pentru urmatoarea etapa


for(i = 0; i < numarProcese; ++i){
MPI_Send(final, 100, MPI_CHAR, i, DEBLOCARE, MPI_COMM_WORLD);

Cât timp sunt fișiere de prelucrat procesul MASTER așteaptă ping-uri de la procesele
WORKER, trimite ca răspuns fișierele de input și setează procesele respective ca fiind active.
Procesul MASTER așteaptă ca toate procesele WORKER să termine activitatea.
După aceasta, procesul MASTER trimite un flag proceselor WORKER care indică faptul
că urmează noua etapă.

6
// Etapa de REDUCERE
if (CateProceseLucreaza(statusProcese, numarProcese) == 0)
{
struct dirent **cuvinte;
int n = scandir(directorIntermediar, &cuvinte, 0, alphasort);
char* cuvant_vechi = (char *) malloc (100 * sizeof(char));
strcpy(cuvant_vechi, " ");
for(i = 0; i < n; ++i)
{
char* cuvant = strtok(cuvinte[i]->d_name, "_");
if((strcmp(cuvant, cuvant_vechi) != 0) && (strlen(cuvant) >
2)){
MPI_Recv(&mesaj, 1, MPI_INT, MPI_ANY_SOURCE, LIBER,
MPI_COMM_WORLD, &status);
MPI_Send(cuvant, 100, MPI_CHAR, status.MPI_SOURCE,
REDUCERE, MPI_COMM_WORLD);
strcpy(cuvant_vechi, cuvant);
}
free(cuvinte[i]);
}
free(cuvinte);
}
for(i = 0; i < numarProcese; ++i){
MPI_Send(final, 100, MPI_CHAR, i, FINAL, MPI_COMM_WORLD);
}if

După ce se asigură că toate procesele WORKER și-au terminat sarcina, procesul


MASTER preia fișierele prelucrate anterior de către acestea și trimite la fiecare proces
WORKER câte un cuvânt (toate cuvintele sunt diferite).
La final procesul MASTER transmite tuturor proceselor că nu mai este nimic de prelucrat
și că toate sarcinile au fost îndeplinite

7
WORKER

Cât timp mai sunt sarcini de îndeplinit, procesele WORKER așteaptă un mesaj de la
procesul MASTER cu etapa pe care trebuie să o facă.

// Etapa de MAPARE

if(status.MPI_TAG == MAPARE){
printf("MAPARE \n");
char *continutFisier = CitesteDinFisier(Concat(directorIntrare,
Concat("/", informatiePrimita)));
if (continutFisier) {
char *cuvant = strtok(continutFisier, splitPattern);
StergeNewLine(cuvant);
StergeSpatiu(cuvant);
while ( (cuvant != NULL) ) {
char* titlu = Concat(cuvant, Concat("_",
informatiePrimita));
if ( strlen(cuvant) > 2 )
ScrieInFisier(directorIntermediar, titlu, "line");
cuvant = strtok(NULL, splitPattern);
}
free(continutFisier);
}
}

8
// Etapa de REDUCERE
else if(status.MPI_TAG == REDUCERE)
{
FILE *fisierIntermediar;
char buffer[1024];
char* calea = Concat (Concat(directorIntermediar, informatiePrimita),
"_*");
fisierIntermediar = popen(Concat("ls ", calea), "r");
if (fisierIntermediar == NULL){
printf("[Etapa de REDUCERE] Eroare la executia comenzii ls ");
printf(" %s ]\n", calea);
}
else{
while (fgets(buffer, sizeof(buffer), fisierIntermediar) != NULL){
StergeNewLine(buffer);
char *continutFisier = CitesteDinFisier(buffer);
int count = 0;
char *cuvant = strtok(continutFisier, " \n");
while(cuvant != NULL){
count++;
cuvant = strtok(NULL, " \n");
}
char *numeFisier = strtok(buffer, "_");
numeFisier = strtok(NULL, ".");
char str[] = " ";
sprintf(str, "%d", count);
char* output = Concat(numeFisier, Concat(",", str));
ScrieInFisier(directorIesire, Concat(informatiePrimita,

9
".txt"), output);
}
pclose(fisierIntermediar);
}

Procesul WORKER cauta cuvantul primit. Acestea cauta toate fisierele text care contin
cuvantul respectiv ( ls cuvant_* ). Dupa ce fisierele text sunt gasite, procesul WORKER citeste
cate linii se afla in fisierul respectiv (numarul de linii da numarul de aparitii al cuvantului intr-un
anumit document/fisier text), pe urmă creeaza un fisier cu numele cuvantului care contine in ce
documente apare cuvantul respectiv si de cate ori apare (fisier1, 55 ; fisier2, 213).

La final procesul MASTER trimite către toate procesele că numai este nicio sarcină de
îndeplinit iar acestea nu mai așteaptă niciun răspuns.

// Master
for(i = 0; i < numarProcese; ++i){
MPI_Send(final, 100, MPI_CHAR, i, FINAL, MPI_COMM_WORLD);
}
// Worker
else if(status.MPI_TAG == FINAL){
work = FALSE;
}

10
Capitolul 5. Bibliografie
[1] https://www.tutorialspoint.com/hadoop/hadoop_mapreduce.htm.
[2] https://github.com/hdaikoku/mapreduce-mpi
[3] http://man7.org/linux/man-pages/man3/scandir.3.html.
[4] https://hadoop.apache.org/docs/r1.2.1/mapred_tutorial.html.

11