Sei sulla pagina 1di 25

Appunti di Calcolo

Parallelo e
Distribuito mod A

Cerrone Mariarosaria

2014/2015

INTRODUZIONE
Il termine supercalcolatore si riferisce ad un sistema che fornisce le prestazioni pi elevate. La
prestazione misurata dal tempo necessario per risolvere un particolare applicazione. La TOP500
la lista dei calcolatori pi veloci nel mondo. Il calcolo ad alte prestazioni serve a risolvere
problemi in "tempo reale" ( tempo utile ) e a risolvere problemi di grandi dimensioni ( a larga
scala).
Il tempo di esecuzione di un software : = k T(n) . Dove k una costante T(n) il numero di
operazioni effettuate dall'algoritmo ossia la complessit di tempo dell'algoritmo ( tecnologia in
senso lato ) e il tempo di esecuzione su un calcolatore di una operazione floating point (
tecnologia in senso stretto ). Per diminuire possiamo pensare di diminuite T(n) e , ma in
entrambi i casi ci sono dei vincoli perch da un lato meglio degli algoritmi ottimali non posso
andare e dall' altro pur volendo ridurre le distanze non posso costruire calcolatori di dimensioni
troppo piccole. Se ancora non ci soddisfa possiamo pensare al calcolo parallelo. Ho un problema
di dimensione N, lo decompongo in P sottoproblemi di dimensione pi piccola N/P e li risolvo
contemporaneamente su diversi processori.
CLASSIFICAZIONE DI FLYNN
Alla classificazione di Flynn ( 1965) appartengono le seguenti classi di architetture parallele:
SISD l'acronimo di Sigle Istruction Single Data, e si usa per intendere la classe delle architetture in
cui si possono eseguire singole istruzioni su singoli dati. A questa categoria appartengono i
monoprocessori.
MISD lacronimo di Multiple Istruction Single Data, e si usa per intendere la classe delle
architetture in cui si possono eseguire flussi di istruzioni diversi sullo stesso dato
contemporaneamente. Presa la definizione alla lettera, sembra non esistere n essere
immaginabile unarchitettura reale che appartenga a questa classe, ma molti fanno rientrare in
questa categoria quelle architetture che implementano la pipeline: questo significa che con questo
tipo di architetture si pu realizzare un parallelismo di tipo temporale, ovvero quello che si pu
esemplificare in una catena di montaggio. Un esempio di implementazione della pipeline in
architettura lo troviamo spesso (oggi ormai possiamo dire sempre) a livello di unit aritmetiche
(ALU) del processore: lALU generalmente divisa in segmenti, ognuno preposto ad una specifica
operazione elementare, e quindi in grado di lavorare concorrentemente agli altri.
SIMD lacronimo di Single Istruction Multiple Data, e si usa per intendere la classe delle
architetture in cui si pu eseguire lo stesso flusso di istruzioni contemporaneamente su dati
diversi. Dunque, a questa categoria appartengono quelle architetture in cui ad una sola unit di
controllo (CU) corrispondono pi unit aritmetiche (ALU), che agiscono su dati diversi: listruzione
decisa dalla CU viene eseguita contemporaneamente dalle diverse ALU, ognuna sul proprio
insieme di dati. Dunque con questo tipo di architetture si pu realizzare un parallelismo di tipo
spaziale. Eventualmente le macchine pipelined possono essere fatte rientrare anche in questa
categoria, ed se teniamo conto di questo con le architetture SIMD possiamo realizzare anche
parallelismo di tipo temporale.
MIMD l'acronimo di Multiple Istruction Multiple Data, e si usa per intende la classe delle
architetture in cui si possono eseguire contemporaneamente diverse istruzioni su dati diversi.
Dunque, a questa categoria appartengono quelle architetture in cui differenti processori ( CPU )
cooperano eseguendo istruzioni diverse su dati diversi in parallelo. Con questo tipo di architetture
si pu realizzare un parallelismo di tipo asincrono. In un calcolatore MIMD, CPU e memorie
possono essere collegate in due diversi modi: si parla di shared memory e distrubuited memory.
I calcolatori MIMD-SM ( a memoria condivisa ) hanno un unica memoria a cui attingono diverse
CPU. Il vantaggio di questo tipo di architettura che le informazioni sono a disposizione di tutte le

CPU quindi non c' necessit di scambio di dati ma c' la necessit di soncronizzare l'accesso ai
dati.
I calcolatori MIMD-DM ( a memoria distribuita ) hanno pi CPU e ognuna di queste ha la sua
memoria. Il vantaggio che non c' necessit di sincronizzare l'accesso ai dati ma c' lo svantaggio
di doversi scambiare le informazioni.
Noi lavoreremo sia su memoria distribuita che su memoria condivisa per risolvere alcuni problemi.
SOMMA DI N NUMERI
Voglio calcolare al somma di N numeri reali su un calcolatore di tipo MIMD con p processori. Su un
calcolatore monoprocessore la somma calcolata eseguendo N-1 addizioni ( algoritmo parallelo ).
In parallelo suddivido il problema in N/p sottoproblemi ognuno dei quali assegnato ad uno dei p
processori che lavorano in parallelo e poi occorre mettere insieme in qualche modo il risultato.
Cominciamo a lavorare con un calcolatore di tipo MIMD a memoria distribuita. Esistono 3 strategie
per il calcolo della somma di N numeri.
I STRATEGIA
Nella prima strategia dopo che ogni processore ha calcolato la propria somma parziale , ad ogni
passo ciascuno di essi invia la propria somma parziale ad un unico processore prestabilito. Tale
processore sar quello che conterr la somma totale. In tale strategia vengono poco sfruttati i
processori perch dopo il primo passo ne lavorer uno solo.

II STRATEGIA
Nella seconda strategia dopo che ogni processore ha calcolato la propria somma parziale, ad ogni
passo coppie distinte di processori comunicano contemporaneamente e uno dei due invia allaltro
la sua somma parziale. Alla fine il risultato si trover in unico processore prestabilito.

III STRATEGIA
Nella terza strategia, dopo che ogni processore ha calcolato la propria somma parziale, ad ogni
passo coppie distinte di processori comunicano contemporaneamente ma, a differenza della
seconda strategia, in ogni coppia i processori si scambiano le proprie somme parziali. Sebbene far

pi operazioni il vantaggio che alla fine il risultato si trover in tutti i processori e quindi se a uno
di queste servisse non c' necessit di comunicazione ( che sappiamo pesare molto sull'efficienza).
Vedremo che lo strumento software utilizzato per lo sviluppo di algoritmi in ambiente di calcolo
MIMD-DM il Message Passing Interface ( MPI ).

VALUTAZIONE EFFICIENZA DI UN ALGORITMO PARALLELO


Sappiamo che per valutare l'efficienza di un algoritmo sequenziale ( monoprocessore ) valutazione
la complessit di tempo ( T(n) numero di operazioni eseguite dall'algoritmo ) e la complessit di
spazio ( S(n) numero di variabili utilizzate ) . In un algoritmo sequenziale il numero di operazioni
determina anche il numero di passi temporali in un algoritmo parallelo non cos perch ci sono
operazioni effettuate contemporaneamente dai diversi processori. In generale con p processori ci
aspettiamo che T(1) sia p volte T(p) ovvero ci aspettiamo di ridurre di p volte il tempo di
esecuzione quando passiamo da 1 a p processori.
Per l'algoritmo della somma di n numeri posto p=2k ( per semplicit) T(p)= ( -1 log2p ) tcalc
DEFINIZIONE Si definisce Speed-up il rapporto:

. Lo Speed up misura la riduzione del

tempo di esecuzione su p processori rispetto a quello impiegato su 1 processore. In generale


S(p)<p.
DEFINIZIONE Si definisce Speed up ideale:
.
Lo Speed up ideale non si raggiunge mai a causa dell'Overhead totale ( che in generale maggiore
di 0 ) che misura quanto lo Speed up differisce da quello ideale.
Al crescere del numero di processori lo Speed up aumenta ma sempre pi lontano dallo speed
up ideale che ci aspettiamo perch l'Overhead cresce al crescere di p. Questa cosa mi fa capire che
non sto sfruttando al massimo i processori impiegati quindi lo speed up non basta a fornire
informazioni sull' efficienza dell' algoritmo parallelo. Per misurare quanto l'algoritmo sfrutta il
parallelismo del calcolatore introduciamo l'EFFICIENZA che dato dal rapporto :
.
L'efficienza ideale pari a 1.
E' possibile ottenere speed up prossimi allo speed up ideale?
Il tempo di esecuzione sequenziale T(1) si pu decomporre in due parti: una parte relativa alle
operazioni che sono eseguite esclusivamente in sequenziale( T s ) e una parte relativa alle
operazioni che possono essere eseguire in parallelo ( Tc ). T(1)= Ts+ Tc .
Il tempo impiegato dall'algoritmo parallelo con p processori, tenendo presente che le operazioni
relative alla parte parallela sono ora eseguite concorrentemente : T(p)= Ts+ Tc/p.

E' comune usare al posto di Ts e Tc la notazione e 1-, dove indica la parte sequenziale (
frazione di operazioni eseguite in sequenziale) . Posto T(1)= 1, Ts=, Tc=1- l'Overhead totale
sar: Oh=(p-1) ossia cresce al crescere di p e di . Quindi l'efficienza:

La LEGGE DI WARE o AMDAHL dice che:

Se la dimensione del problema n fissato, al crescere di p non otteniamo speed up vicini a quello
ideale ma in pi le prestazioni peggiorano perch l'efficienza diminuisce e quindi non conviene
utilizzare un maggior numero di processori.
Fissando il numero di processori p e aumentando la dimensione N del problema ( la parte
sequenziale diminuisce ) si possono ottenere speed up prossimi a quello ideale ma non possibile
aumentare in maniera indefinita la dimensione del problema ( le risorse hardware sono limitate ).
Aumentando sia N che p le prestazioni dell' algoritmo parallelo non degradano e ci aspettiamo
che: T(1,N)=T(2,2N)=.......=T(p,pN) dove T(p,pN) il tempo impiegato da p processori per eseguire
la somma di pN numeri. Pertanto se si assume pT(1,n)=T(1,pn):

Ovvero se si assume che T(1,pN) uguale al tempo che si ottiene moltiplicando per p il tempo per
risolvere su un processore il problema di dimensione N otteniamo lo Speed up ideale che si chiama
pi correttamente speed up scalato.
DEFINIZIONE Nell'algoritmo della somma lo speed uo scalato :
DEFINIZIONE Si definisce efficienza scalata il rapporto tra Speed up scalato e p.
Un algoritmo si dice SCALABILE se l'efficienza rimane costante o non degrada al crescere dei
processori e della dimensione del problema.
E' possibile ottenere speed up scalati prossimi allo speed up ideale?
Il tempo T(p) si pu decomporre in due parti: una parte relativa alle operazioni che sono eseguite
in sequenziale( Ts ) e una parte relativa alle operazioni che sono eseguite in parallelo ( T c' ).
T(p)= Ts+ Tc' .
Il tempo impiegato dall'algoritmo sequenziale, tenendo presente che le operazioni relative alla
parte parallela sono ora eseguite sequennzialmente : T(1)= Ts+ pTc'. Si assume T(p) come tempo
unitario ossia T(p)= Ts+ Tc'=1 e quindi Tc' = 1 - Ts allora T(1)= Ts+ p(1-Ts).
Posto Ts=' , la LEGGE DI GUSTAFSON ( reintrerpretazione della legge di ware) dice che:

Si possono ottenere speed up prossimi a quello ideale. Nell'algoritmo della somma abbiamo
calcolato speed up ed efficienza scalata aumentando la complessit di tempo del problema in
maniera proporzionale al numero di processori ovvero nel passare da p 0 a p1 processori con p1=kp0
siamo passati da T(p0,n0) a T(p1,n1) con n1=kn0. Ovvero
T(1,n1)=I(p0,p1,n0) cio

La funzione I esprime la legge secondo cui deve aumentare la complessit di tempo dell'algoritmo
sequenziale con dimensione n1 su p1 processori a partire da p0 e da T(1,n0) affinch l'efficienza
scalata non degradi. I detta ISOEFFICIENZA. L'isoefficienza calcolata:

Nell'algoritmo della somma nel passare da p0 a p1 la complessit di tempo del problema deve
aumentare rispetto alla dimensione iniziale del fattore

. L'algoritmo della somma

scalabile.
Se si esegue l'algoritmo su un calcolatore MIMD-DM il tempo di esecuzione dipende non solo dal
numero di operazioni eseguite ma anche dal tempo di comunicazione che in genere molto pi
grande di quello di calcolo perch richiedere la velocit della rete etc.
Per l'algoritmo della somma di n numeri posto p=2 k ( per semplicit) considerando le
comunicazioni T(p)= ( -1 log2p ) tcalc + (log2p)tcom
In generale il tempo di esecuzione di un algoritmo parallelo, distribuito su p processori,
comprende 3 componenti:
Ts tempo per eseguire la parte sequenziale
Tc/p tempo per eseguire la parte parallela
T0 costo di comunicazione che maggiore o uguale a 0
T(p)= Ts + Tc/p + T0
L'Overhead di comunicazione unitario dato da:
dove
il tempo di
comunicazione di un dato e
il tempo di esecuzione di una operazione floating point.
L'overhead di comunicazione fornisce una misura del peso della comunicazione sul tempo di
esecuzione dell'algoritmo.
IL PROBLEMA DEL BILANCIAMENTO
Una cattiva ripartizione del carico di lavoro tra i processori induce un tempo di attesa o tempo di
sincronizzazione e quindi un aumento del tempo di esecuzione quindi il tempo di esecuzione pu
dipendere del bilanciamento del carico computazionale tra i processori.
In generale il tempo di esecuzione di un algoritmo parallelo, distribuito su p processori,
comprende 3 componenti:
Ts tempo per eseguire la parte sequenziale
Tc/p tempo per eseguire la parte parallela
T0 costo di comunicazione e sincronizzazione che in genere sempre maggiore di 0.
T(p)= Ts + Tc/p + T0
Lo Speed up

misura la riduzione del tempo di esecuzione su p processori rispetto a

quello impiegato su 1 processore. Come possiamo scegliere T(1)?


Possiamo scegliere T(1) come il tempo di esecuzione dell'algoritmo parallelo su un processore
anche se potrebbe richiedere pi operazioni oppure come il tempo di esecuzione del migliore
algoritmo sequenziale ma nasce di problema di capire chi il migliore algoritmo.

SPEED UP ED EFFICIENZA PER L'ALGORITMO DELLA SOMMA


I STRATEGIA
Dati n ( maggiore o uguale a 2 ) numeri e p processori con p < 2n+1, il numero di dati che diamo ad
ogni processo :

T(1)=(n-1)tcalc

; T(p)=(r-2+p)tcalc

Considerando i passi di comunicazione abbiamo: T(1)=(n-1)tcalc ;


T(p)=(r-2+p)tcalc+(p-1)tcom Si
calcola speed up ed efficienza tenendo conto che tcom=htcalc ( in realt noi lo facciamo per h=2 ).
II STRATEGIA
Dati n ( maggiore o uguale a 2 ) numeri e p=2 k processori con k<log2n, il numero di dati che diamo
ad ogni processo :

T(1)=(n-1)tcalc

; T(p)=(r-1)tcalc+(log2p)tcalc

Considerando i passi di comunicazione abbiamo: T(1)=(n-1)tcalc;T(p)=(r-1)tcalc+(log2p)tcalc+(log2p)tcom


Si calcola speed up ed efficienza tenendo conto che t com=htcalc ( in realt noi lo facciamo per h=2 ).
III STRATEGIA
Dati n ( maggiore o uguale a 2 ) numeri e p=2 k processori con k<log2n, il numero di dati che diamo
ad ogni processo :

T(1)=(n-1)tcalc

; T(p)=(r-1)tcalc+(log2p)tcalc .

Considerando i passi di comunicazione abbiamo: T(1)=(n-1)tcalc;T(p)=(r-1)tcalc+(log2p)tcalc+(log2p)tcom


Si calcola speed up ed efficienza tenendo conto che tcom=htcalc ( in realt noi lo facciamo per h=2 ).
SPEED UP ED EFFICIENZA PER L'ALGORITMO DELLA SOMMA CON LA LEGGE DI WARE-AMDAHL
I STRATEGIA
Dati n ( maggiore o uguale a 2 ) numeri e p processori con p < 2n+1, per le somme parziali il
processore i esegue un numero di operazioni pari a :

per un totale di
operazioni eseguite in parallelo
Seguono altre p -1 fasi di calcolo, in tutte queste fasi un solo processore compie un unica
operazione di somma quindi:
operazioni eseguite da un solo processore. In totale
vengono eseguite
operazioni.
Per la legge di Ware-Admahl:

Nel nostro caso:

Per la legge di Ware-Amdahl generalizzata:

dove
la frazione di operazioni eseguite con k processori.
Nel nostro caso:
Tutti gli altri sono nulli. In questo caso il risultato
corrisponde a quello trovato con la legge precedente.
II STRATEGIA
Dati n ( maggiore o uguale a 2 ) numeri e p=2 k processori con k<log2n, per le somme parziali il
processore i esegue un numero di operazioni pari a :

per un totale di
operazioni eseguite da p processori.
Seguono altre log2p fasi di calcolo. In ogni fase processori eseguono un' operazione di somma.
Per un totale di
operazioni. In totale vengono eseguite:
Per la legge di Ware-Admahl:

Nel nostro caso:

Per la legge di Ware-Amdahl generalizzata:

dove
la frazione di operazioni eseguite con k processori.
Nel nostro caso:

Tutti gli altri sono nulli. In questo caso il risultato non corrisponde a quello trovato con la legge
precedente.
III STRATEGIA
Nel caso della terza strategia per la legge di Ware =0 perch in sequenziale non eseguita
nessuna operazione. Nel caso della legge di Ware generalizzata solo =1.
MPI
MPI lo strumento software utilizzato per lo sviluppo di algoritmi in ambiente di calcolo MIMDDM, un ambiente standard che stato creato per il paradigma del message passing per i sistemi
a memoria distribuita ossia:
ogni processore ha una prorpia memoria locale alla quale accede direttamente in maniera
esclusiva
ogni processore pu conoscere i dati in memoria di un altro processore o far conoscere i propri
attraverso il trasferimento di dati ed per questo che si chiama message passing. Per renderlo
portabile si sviluppato lo standard e quindi abbiamo il message passing interface.
In ambiente MPI un programma visto come un insieme di componenti ( o processi ) concorrenti.
L'MPI li prende, li lancia e li collega attraverso diverse categorie: gruppo, contesto e
communicator. Ogni componente concorrente del programma ( pezzo di codice ) viene affidata ad
un gruppo di processori. In ciascun gruppo ad ogni processore associato un identificativo.
L'identificativo un intero, compreso tra 0 e il numero totale di processori appartenenti ad un
gruppo decrementato di un unit. Processori appartenenti a uno o pi gruppi possono avere
identificativi diversi, ciascuno relativo ad uno specifico gruppo.
A ciascun gruppo di processori viene attribuito un identificativo detto contesto. Il contesto
definisce l'ambito in cui avvengono le comunicazioni tra processori di uno stesso gruppo. Se la
spedizione di un messaggio avviene in un contesto, la ricezione deve avvenire nello stesso
contesto. Ad un gruppo di processori appartenenti ad uno stesso contesto viene assegnato un
identificativo: il communicator.Il communicator racchiude tutte le caratteristiche dell'ambiente di
comunicazione. A seconda del problema che ho da risolvere mi costruisco il communicator pi
comodo. Esistono due tipi di communicator. L'intra-communicator in cui le comunicazioni
avvengono all'interno di un gruppo di processori.
L'inter-communicator in cui le comunicazioni avvengono tra gruppi di processori.
Tutti i processori fanno parte di un unico communicator detto MPI_COMM_WORLD in cui tutti
comunicano con tutti.
L'MPI una libreria che comprende:
funzioni per definire l'ambiente
funzioni per comunicazioni 1 a 1
funzioni per comunicazioni collettive
funzioni per operazioni collettive
FUNZIONI D'AMBIENTE
Nel programma .c occorre includere la libreria mpi.h perch contiene alcuno direttive necessarie al
preprocessore per l'utilizzo dell' MPI ( #include "mpi.h" )
MPI_Init(&argc,&argv)
Deve essere chiamata una sola volta prima di ogni altra routine MPI. Inizzializza l'ambiente di
esecuzione MPI, definisce l'insieme dei processori attivati ( il communicator MPI_COMM_WORLD).
argc il numero di stringhe che ci sono nella riga di comando e argv il vettore di stringhe che

contiene tutte le stringhe della riga di comando e sono gli argomenti del main( sono passate per
indirizzo ).
MPI_Finalize()
Determina la fine del programma MPI e dopo questa routine non possibile richiamare nessun
altra routine di MPI.
MPI_Comm_rank(MPI_COMM_WORLD,&menum)
Questa routine permette al processore chiamante, appartenente al communicator
MPI_COMM_WORLD, di memorizzare il proprio identificativo nella variabile menum
MPI_Comm_size(MPI_COMM_WORLD,&nproc)
Questa routine permette al processore chiamante di memorizzare nella variabile nproc il numero
totale di processori concorrenti appartenente al communicator MPI_COMM_WORLD.
FUNZIONI DI COMUNICAZIONE
Per comunicare un dato in ambiente MPI occorre specificare: l'indirizzo in memoria del dato, la
dimensione del dato e il tipo del dato (MPI_INT, MPI_FLOAT....).
La spedizione o la ricezione di un messaggio da parte di un processore pu essere bloccante o non
bloccante. Se un processore esegue una comunicazione bloccante si arresta fino a conclusione
dell'operazione. Se un processore esegue una comunicazione non bloccante presegue senza
preoccuparsi della conclusione dell' operazione.
COMUNICAZIONI BLOCCANTI IN MPI
MPI_Send(&n,1,MPI_INT,1,tag,MPI_COMM_WORLD)
Con questa routine un processore spedisce l'indirizzo di n di dimensione 1, di tipo intero al
processore 1 il cui identificativo di comunicazione tag nel communicator comm world.
MPI_Recv(&n,1,MPI_INT,0,tag,MPI_COMM_WORLD,&info)
Con questa routine un processore riceve l'indirizzo di n, di dimensione1, di tipo intero, dal
processore 0, il cui identificativo di comunicazione tag, nel communicator comm world. La
variabile info che tipo MPI_status contiene tutte le informazioni sulla comunicazione.
MPI_Status info
E' un tipo di dato strutturato, composto da tre campi: identificativo del processore da cui
ricevere, identificativo del messaggio, identificativo di errore.
Se devo comunicare pi informazioni user un for per le send in cui cambia a chi mandare
l'informazione e il tag. La recv la fanno tutti una sola volta e cambia solo il tag che deve essere,
se in send metto tag+i in recv mi serve tag+menum.
MPI_Get_count(&info,MPI_INT,&num)
Questa routine permette di conoscere il numero, num, di elementi ricevuti, di tipo MPI_INT,
nella spedizione individuata da info.
FUNZIONI NON BLOCCATI PER LA SPEDIZIONE
MPI_Isend(&n,1,MPI_INT,1,tag,MPI_COMM_WORLD,&rqst)
Analogo a quello bloccante. Il parametro rqst di tipo MPI_rqst simile a info, contiene le
informazioni dell'intera spedizione. Dopo che il processore ha inviato il parametro n libero di
procedere nelle successive istruzioni senza aspettare l'ok. La comunicazione termina quando
chi ha inviato svuota il suo buffer e chi ha ricevuto lo riempie.
MPI_Irecv(&n,1,MPI_INT,1,tag,MPI_COMM_WORLD,&rqst)
Analogo a quello bloccante. Il parametro rqst di tipo MPI_rqst simile a info, contiene le
informazioni dell'intera spedizione. Dopo che il processore ha inviato il parametro n libero di
procedere nelle successive istruzioni.

Le operazioni non bloccanti utilizzano l'oggetto request di tipo MPI_request che collega
l'operazione che inizia la comunicazione in esame con l'operazione che la termina.
MPI_Test(&rqst,&flag,&info)
Il processore che riceve verifica se la ricezione di n, individuata da rqst, stata completata; in
tal caso flag=1 altrimenti flag=0.
MPI_Wait(&rqst,&info)
Il processore controlla lo stato della comunicazione non bloccante identificata da rqst, e si
arresta sl quando l'operazione in esame si conclusa. In info ci sono informazioni sul
completamente dell'operazione di Wait.
Le comunicazioni di tipo non bloccante hanno due vantaggi:
1) il processore non obbligato ad aspettare in stato di attesa
2)Pi comunicazioni possono avere luogo contemporaneamente sovrapponendosi almeno in parte
tra loro.
FUNZIONI PER COMUNICAZIONI E OPERAZIONI COLLETTIVE
Per comunicazioni che coinvolgono solo due processori si considerano le funzioni MPI per
comunicazioni uno a uno. Esistono, per, modi per fare comunicazioni collettive.
Le operazioni collettive sono eseguite da tutti i processori appartenenti ad un communicator.
Possono richiedere una sincronizzazione e tutte queste operazioni sono bloccanti. Le operazioni
collettive permettono: la sincronizzazione dei processi, l'esecuzione di operazioni globali e una
gestione ottimizzata degli input/output seguendo uno schema ad albero.
MPI_Barrier (MPI_COMM_WORLD)
Questa routine fornisce un meccanismo di sincronizzazione per tutti i processori del
communicator: ogni processore si ferma fin quando tutti i processori del communicator non la
eseguono.
MPI_Bcast(&n,1,MPI_INT,0,MPI_COMM_WORLD)
Il processore con identificativo 0 spedisce n di dimensione 1 di tipo MPI_INT a tutti i processori
del communicator.
MPI_Gather(&n, num, MPI_INT, &N, NUM, MPI_INT, 1, MPI_COMM_WORLD)
Il processore 1 riceve da tutti gli altri processori del comunicator dei dati n che concatena in N.
num contiene il numero di dati da spedire da ciascun processore e NUM contiene il numero di
dati da ricevere.
MPI_Scatter(&n, num, MPI_INT,&N, NUM, MPI_INT,0,MPI_COMM_WORLD)
Il processore 0 distribuisce a tutti gli altri processori ( se stesso compreso ) del comunicator, i
dati contenuti in n. Il contenuto di n viene suddiviso in nproc segmenti ciascuno di dimensione
num. Il primo segmento viene affidato al processore 0, il secondo al processore 1 e cos via.

MPI possiede una classe di operazioni collettive chiamate operazioni di riduzione. In ciascuna
operazione di riduzione tutti i processori di un communicator contribuiscono al risultato di
un'operazioone. Sono operazioni che da pi dati in input otteniamo solo un dato in output (
somma, max,min...)
MPI_Reduce(&sum,&sumtot,1,MPI_INT,MPI_SUM,0,MPI_COMM_WORLD)
Il processore 0 ottiene la somma degli elementi sum, di tipo MPI_INT, di dimensione 1,
distribuiti tra tutti i processori del communicator. Il risultato lo pone nella propria variabile
sumtot.
MPI_Wtime()
E' una funzione di MPI che restituisce un tempo in secondi ( double ).
es: double t0
t0=MPI_Wtime()
LE TOPOLOGIE E LE GRIGLIE
Una topologia la geometria virtuale in cui si immaginano disposti i processori, pu non avere
alcun nesso con la disposizione reale.
Una griglia un comunicator e la dichiaro:
MPI_Comm comm_grid
Per creare questa griglia virtuale che serve a facilitare le comunicazioni nel caso in cui si lavora con
le matrici user i seguenti comando MPI.
MPI_Cart_Create(MPI_COMM_WORLD,dim,ndim,period,reorder,&comm_grid)
Operazione collettiva che restituisce un nuovo communicator comm_grid. Ogni processo
dell'ambiente MPI_COMM_WORLD ( di partenza ) definisce la griglia che si chiama comm_grid
di dimensione dim ( supponiamo 2 ) e non periodica lungo le due componenti ( period[i]=0 per
ogni i ). Il numero di righe e di colonne sono memorizzare nel vettore ndim, i processi non sono
riordinati secondo un particolare schema ( non permesso riordinare i menum , reorder[i]=0
per ogni i )
MPI_Cart_coords(MPI_Comm comm_grid,menum, dim,coordinate)
Operazione collettiva che restituisce a ciascun processo di comm_grid con identificativo
menum le sue coordinate all'interno della griglia predefinita. Coordinate un vettore di
dimensione dim i cui elementi rappresentano le coordinate di un processo all'interno della
griglia predefinita.
OPEN MP
Se il sistema tale che diverse CPU sono collegate alla stessa memoria fisica opportuno un
parallelismo a memoria condivisa. Cosa cambia rispetto ad un parallelismo di tipo distribuito?
Per un sistema operativo moderno, l'unit di base della CPU il thread ( un flusso di istruzioni
indipendente da altri che deve essere eseguito sequenzialmente su una CPU).
Un processo di definisce come un programma in esecuzione, costituiti da almeno un thread.
Threads diversi possono essere eseguiti indipendentemente su CPU/core diversi. Anche se i core
sono collegati fisicamente alla stessa memoria processi diversi non condividono le stesse aree di

memoria ( che vengono assegnate dal sistema operativo). Perch lavorino insieme bisogna gestire
una comunicazione esplicita. I threads di uno stesso processo condividono la stessa area di
memoria. Si pu pensare di dividere il lavoro tra i threads di uno stesso processo. I threads sono
coordinati attraverso la sincronizzazione degli accessi alle variabili condivise. Ogni thread ha una
locazione privata ma per la comunicazione necessario avere alcune variabili condivise quindi
abbiamo quest'area di memoria condivisa e per accedervi bisogna sincronizzare.
SOMMA
Tutti i threads leggono in contemporanea i numeri da sommare ognuno fa la prorpia somma
parziale e la salva in una variabile diversa. Per fare la somma totale ogni thread aggiorna sumtot
con la propria somma parziale ma bisogna sincronizzare gli accessi a tale variabile con il
meccanismo dei semafori. Quando un thread lavora su sumtot mette un semaforo ( rosso ) agli
altri threads.
#pragma omp parallel for reduction(+: sumtot)
for i=0;i<N;i++
sumtot=sumtot+a(i)
}
Utilizzando OPEN MP l'utilizzo di variabili private di appoggio, la divisione del lavoro tra i threads e
la collezione del risultato in una variabile shared in modo sincronizzato possono essere
completamente trasparenti.
Open Mp sta per Open specification for multi processing. API per gestire il parallelismo
shared.memory multi-thread. E' facile trasformare un codice sequenziale in uni parallelo ed esiste
per tutti i linguaggi ( portabile ). Il modello di esecuzione parallela quello fork-join. Quando un
codice parte c' sempre un unico thread detto master thread ( che esegue in maniera
sequenziale). Poi il master thread genera un team di thread che si disincronizzano quando hanno
finito.
FORK: comincia una regione parallela, viene quindi creato un team di thread che procede
parallelamente
JOIN: tutti i thread del team hanno terminato tutte le istruzioni della regione parallela, si
sincronizzano e terminano, lasciando proseguire solo il master thread.
I thread portano i propri dati in cache. E' bene dare per condivise tutte le variabili che non devono
essere private per risparmiare memoria per per qualche variabile ci pu essere ambiguit e
quindi meglio se privata.
L'Open Mp composto da:
direttive per i compilatore
Si usano per creare un team e stabilire quali istruzioni devono essere eseguite in parallelo e come
queste devono essere distribuite tra i thread del team creato. Le istruzioni sono comprensibili solo
ai compilatori dotati di supporto Open Mp e possono essere innestate per creare un parallelismo
multilivello. Le direttive sono eventualmente completate da clausole che ne dettagliano il
comportamento.
Runtime Library Routine
per intervenire sulle variabili di controllo interne allo standard, a runtime ( deve essere inclusa la
libreria omp.h )

Variabili d'ambiente
Per modificare il valore delle variabili di controllo interne prima dell'esecuzione.
Il primo passo per parallelizzare un codice sequenziale con Open Mp consiste nell'individuare quali
istruzioni possono essere eseguire in parallelo. Una volta individuate le porzioni di codice che
posso essere parallelizzate si introducono opportune direttive con le relative clausole.
Le direttive si applicano al costrutto seguente:
#pragma omp direttive [clausole]
Il costrutto parallel forma un team di thread ed avvia un'esecuzione parallela.
#pragma omp parallel [clausole]
{
blocco istruzioni }
Le clausole possono essere:
if(espressione scalare)
pu comparire pi di una volta
num_thread(espressione intera)
pu comparire al pi una volta
default(shared| none )
private (list)
first private(list)
shared(list)
copyin(list)
reduction(operator:list) equivalente a MPI_Reduce
Non considerato l'ordine delle clausole.
Alla fine del blocco di istruzioni sottintesa una barriera di sincronizzazione: tutti i threads
aspettano che tutti gli altri abbiano completato prima di tornare all'esecuzione sequenziale ( qui
non c' la clausola nowait perch dopo devono morire). Tutto quello che segue questa direttiva
eseguito da ogni thread. Dopo aver generato i threads necessario stabilire anche una
distribuzione del lavoro tra i thread del team per evitare ridondanze.
Quali sono le direttive di WorkSharing?
Ci sono tre tipi di costrutti detti WorkSharing perch si occupano della distribuzione del lavoro al
team di threads : FOR, SECTIONS, SINGLE.
Anche all'uscita di un costrutto WorkSharing sottintesa una barriera di sincronizzazione, se non
diversamente specificato dal programmatore.
Il costrutto FOR specifica che le iterazioni del ciclo contenuto devono essere distribuite tra i thread
del team.
#pragma omp for [clausole]
{
ciclo for }
Le clausole possono essere:

private (list)
first private(list)
last private(list)
schedule(kind[,chunck_ size])
collapse(n)
ordered
nowait
esclusione della barriera in uscita
reduction(operator:list) equivalente a MPI_Reduce

Perch usiamo il for e non il while? perch il while vuole una condizione per terminare ma non la
conosciamo a priori e nel for sono sicura di sapere quante sono le iterazioni fatte.
Il costrutto SECTIONS conterr un insieme di costrutti section ognuno dei quali verr eseguito da
un thread del team.
#pragma omp sections [clausole]
{
[#pragma omp section [clausole]
blocco]
[#pragma omp section [clausole]
blocco]
}
Le clausole possono essere:
private (list)
first private(list)
last private(list)
nowait
reduction(operator:list)
Le diverse sezioni devono poter essere eseguite in ordine arbitrario ( devono essere indipendenti
tra loro ). C' rischio di sbilanciamento del carico se una sezione pi lunga di un ' altra.
Il costrutto SINGLE specifica che il blocco di istruzioni successivo verr eseguito da un solo thread
qualsiasi del team. Gli altri attendono che questo termini la sua porzione di codice.
#pragma omp single [clausole]
{
blocco di istruzioni
}
Le clausole possono essere:

private (list)
first private(list)
copyprivate(list)
nowait
elimina la barriera sottintesa alla fine del costrutto

I costrutti WorkSharing possono essere combinati con il costrutto parallel e le clausole ammesse
sono l'unione di quelle ammesse per ognuno.
Il costrutto MASTER specifica che il blocco di istruzioni successivo verr eseguito dal solo master
thread. Non sono sottintese barriere di sincronizzazione n all'ingresso n all'uscita del costrutto
perch il master thread vive a se stante.
#pragma omp master
{
blocco istruzioni
}
Il costrutto CRITICAL forza l'esecuzione del blocco successivo ad un thread alla volta: utile per
gestire regioni critiche. Si pu assegnare un nome alla regione che sar globale al programma.
#pragma omp critical [(nome)]
{
blocco istruzioni
}
Il costrutto BARRIER forza i thread di uno stesso task ad attendere il completamento di tutte le
istruzioni precedenti da parte di tutti gli altri. Al momento del barrier ( implicito o esplicito ) si crea
una lista consistente dei dati dei thread.
#pragma omp barrier
Non tutte le clausole sono valide per tutte le direttive.
-default(shared| none )
Controlla gli attributi di data-sharing delle variabili in un costrutto. Se non specificata questa
clausola, il compilatore stabilisce quali variabili devono essere private per ogni thread. In genere
tutti i dati sono condivisi da tutti i thread.
shared: tutte le variabili saranno considerate condivise
none: decide tutto il programmatore
- shared(list)
gli argomenti contenuti in list sono condivisi tra i thread del team
- private (list)
gli argomenti contenuti in list sono privati per ogni thread del team. Le variabili private hanno un
valore indefinito all'entrata e all'uscita della regione parallela perch devono essere variabili che
non contengono info importanti.
- first private(list)

gli argomenti contenuti in list sono privati per i thread e vengono inizializzate con il valore che
avevano prima di incontrare la regione parallela.
- last private(list)
gli argomenti contenuti in list sono privati per i thread e all'uscita le variabili manterranno l'ultimo
valore della sezione parallela.
- reduction(operator:list)
Gli argomenti contenuti in list verranno combinati utilizzando l'operatore associativo specificato.
Al termine la variabile sar condivisa.
-nowait
elimina la barriera implicita alla fine del costrutto ( non della regione parallela ). I thread non
aspettano gli altri e continuano.
-schedule(kind[,chunck_ size])
esiste solo per la for, specifica il modo ( kind ) di distribuire le iterazioni del ciclo seguente;
chunk_size il numero di iterazioni contigue da assegnare allo stesso thread, mentre kind pu
essere:
static: secondo uno scheduling round-robin
dynamic:assegnati su richiesta, quando un thread termina il proprio chiede un altro chunk.
guide: variante del dynamic
runtime:decisione presa a runtime attraverso la variabile d'ambiente OMP_SCHEDULE (stabilisce
lo scheduling di defoult da applicare nei costrutti for)
Il modo consigliato per verificare il tempo di esecuzione del programma utilizzare la funzione
int gettimeofday(timeval *tp, NULL) cotenuta in <sys/time.h>. La funzione va chiamata subito
dopo e subito prima la regione parallela.
PRODOTTO MATRICE-VETTORE a memoria distribuita
Quali sono i sotto problemi indipendenti?
In generale il prodotto di una matrice A per un vettore x d luogo ad un vettore le cui componenti
sono i prodotti scalari tra la i-esima riga di A e il vettore x. i prodotti scalari possono essere
calcolati indipendentemente gli uni dagli altri. Ci sono varie strategie!
I STRATEGIA
Suddividiamo la matrice A in blocchi di righe ( in genere il numero di processori pi piccolo del
numero di righe quindi partizioniamo la matrice in blocchi di righe cos da sfruttare il parallelismo
a blocchi ) e assegniamo ogni blocco a ciascun processore. Assegniamo interamente il vettore x a
tutti i processori. Ciascun processore in grado di calcolare n/p componenti del vettore soluzione.
Non sono richieste interazioni tra i processori se non alla fine per ottenere il risultato finale ma il
vettore x assegnato interamente a tutti i processori.

II STRATEGIA
Suddividiamo la matrice A in blocchi di colonne e assegniamo ogni blocco a ciascun processore.
Distribuiamo le componenti del vettore x tra i processori ( tanti elementi quante sono le colonne
di A assegnate ). Ciascun processore in grado di calcolare tutte le componenti parziali del vettore
soluzione. Per ottenere il vettore soluzione bisogna far interagire i processori facendo la somma in
parallelo in modo che tutti i processori hanno il risultato( analogo a quello della somma).

III STRATEGIA
Suddividiamo la matrice A in blocchi quadrati ( di colonne e di righe ) e assegniamo ogni blocco a
ciascun processore. Distribuiamo le componenti del vettore x tra i processori ( tanti elementi
quante sono le colonne di A assegnate ). Ciascun processore in grado di calcolare una parte delle
componenti parziali del vettore soluzione. Per ottenere il vettore soluzione bisogna far interagire i
processori facendo la somma in parallelo in modo che tutti i processori hanno il risultato( analogo
a quello della somma).

Matrice vettore a memoria condivisa


#pragma omp parallel for defoult(none)shared(A,m,n,x,b)private(i,j)
for i=0;i<n; i++
for j=0;j<m;j++
b(i)=Aij*x(j)
}
}

SPEED UP ED EFFICIENZA PER L'ALGORITMO DEL PRODOTTO MATRICE - VETTORE


I STRATEGIA
Ax=b ARnxm xRm b Rn
Suddividiamo la matrice A in blocchi di righe e assegniamo ogni blocco a ciascun processore . Il
vettore x dato intero a tutti i processori. Ogni processore calcola un prodotto matrice vettore pi
piccolo e non si deve ricompilare il risultato.
Dati p processori distribuiti sulle righe della griglia

T(1)=n(2m-1)tcalc

; T(p)=r(2m-1)tcalc

Considerando i passi di comunicazione abbiamo: T(1)=n(2m-1)tcalc ; T(p)=r(2m-1)tcalc +log2p tcom


Si calcola speed up ed efficienza tenendo conto che t com=htcalc ( in realt noi lo facciamo per h=2 ).
II STRATEGIA
Ax=b ARnxm xRm b Rn
Suddividiamo la matrice A in blocchi di colonne e assegniamo ogni blocco a ciascun processore . Il
vettore x distribuito tra i processori. Ogni processore calcola le componenti parziali del vettore
soluzione si. Si deve ricompilare il risultato : b la somma di si.
Dati q processori distribuiti sulle colonne della griglia

T(1)=n(2m-1)tcalc

; T(p)=r(2c-1)tcalc+nlog2q tcalc

Considerando i passi di comunicazione abbiamo: T(1)=n(2m-1)tcalc


; T(p)=r(2c-1)tcalc+nlog2q
tcalc+nlog2q tcom Si calcola speed up ed efficienza tenendo conto che t com=htcalc ( in realt noi lo
facciamo per h=2 ).
III STRATEGIA
Ax=b ARnxm xRm b Rn
Abbiamo processori idealmente disposti in una griglia pxq, distribuiamo la matrice per blocchi
rettangolari. Il vettore x viene distribuito tra i processori sulla stessa colonna della griglia. Bisogna
ricompilare il risultato solo lungo le righe della griglia.
Dati pxq processori

T(1)=n(2m-1)tcalc

; T(p)=r(2c-1)tcalc+rlog2q tcalc

Considerando i passi di comunicazione abbiamo: T(1)=n(2m-1)tcalc


; T(p)=r(2c-1)tcalc+rlog2q
tcalc+rlog2q tcom+ rlog2p tcom Si calcola speed up ed efficienza tenendo conto che t com=htcalc ( in
realt noi lo facciamo per h=2 ).
SPEED UP ED EFFICIENZA PER L'ALGORITMO DELLA SOMMA CON LA LEGGE DI WARE-AMDAHL
I STRATEGIA
Ax=b ARnxm xRm b Rn
Suddividiamo la matrice A in blocchi di righe e assegniamo ogni blocco a ciascun processore . Il
vettore x dato intero a tutti i processori. Ogni processore calcola un prodotto matrice vettore pi
piccolo e non si deve ricompilare il risultato.
Dati p processori , le righe del processore i saranno:

per un totale di
Per la legge di Ware-Admahl:

Nel nostro caso:

Per la legge di Ware-Amdahl generalizzata:

dove
la frazione di operazioni eseguite con k processori.
Nel nostro caso:
Tutti gli altri sono nulli. In questo caso il risultato
corrisponde a quello trovato con la legge precedente.
II STRATEGIA
Ax=b ARnxm xRm b Rn
Suddividiamo la matrice A in blocchi di colonne e assegniamo ogni blocco a ciascun processore . Il
vettore x distribuito tra i processori. Ogni processore calcola le componenti parziali del vettore
soluzione si. Si deve ricompilare il risultato : b la somma di si.
Dati q processori , le colonne del processori i sono:

per un totale di
Per la legge di Ware-Admahl:

Nel nostro caso:

Per la legge di Ware-Amdahl generalizzata:

dove
la frazione di operazioni eseguite con k processori.
Nel nostro caso:

Tutti gli altri sono nulli. In questo caso il risultato corrisponde a


quello trovato con la legge precedente.
III STRATEGIA
Nel caso della terza strategia per la legge di Ware =0 perch in sequenziale non eseguita
nessuna operazione. Nel caso della legge di Ware generalizzata solo =1.

PRODOTTO MATRICE-MATRICE AB=C


Quali sono i sotto problemi indipendenti?
In generale gli elementi di C sono calcolati effettuando i prodotti scalari di ciascuna riga di A per
ciascuna colonna di B e tali prodotti scalari Cij=AiBj sono calcolati in maniera indipendente l'uno
dall'altro.
Ci sono varie strategie!
I STRATEGIA
Decomponiamo A per blocchi di righe e B per blocchi di colonne e li assegniamo a ciascun
calcolatore.

Ciascun calcolatore pu calcolare p prodotti matrice matrice di dimensione pi piccola ottenendo


solo alcune componenti della matrice C. Per calcolare le altre devono interagire scambiandosi i
blocchi di colonne della matrice C. In tal modo siamo riusciti a calcolare le componenti mancanti di
C.

II STRATEGIA
Decomponiamo A per blocchi di colonne e B per blocchi di righe e li assegniamo a ciascun
calcolatore.

Ciascun calcolatore pu calcolare un contributo di ciascun elemento della matrice C: C1...Cp. Per
ottenere C i risultati parziali vanno sommati. In paralleo tutti i processori faranno la somma dei
contributi parziali.

III STRATEGIA
Suddividiamo entrambe le matrici in blocchi di colonne e assegniamoli ai processori. Ciascun
processore pu calcolare un contributo di una parte della matrice C. Per completare i contributi
calcolati devono scambiarsi i blocchi di colonne della matrice A. Dopo lo scambio ciascun
processore ha un blocco di C e basta ricomporli per ottenere C.

IV STRATEGIA - Broadcast Multiply Rolling


Decomponiamo le matrici in blocchi quadrati. I processori vengono disposti in una griglia
cartesiana e le matrici vengono distribuite in modo che al processore Pij vengono assegnati i
blocchi Aij e Bij

La strategia costituita da p passi. Si parte dalla diagonale principale della griglia di processori, ad
ogni passo k, si considera la k-esima diagonale situata al di sopra di quella principale. I processori
situati lungo la diagonale effettuano una comunicazione collettiva dal blocco di A in loro possesso
a tutti i processori della medesima righa. Inoltre ad ogni passo ciascun processore effettua una
comunicazione del proprio blocco di B al processore situato nella stessa colonna e nella righa
precedente.

BITONIC SORT
E' un algoritmo che non di algebra lineare. Vogliamo ordinare un vettore su un calcolatore
MIMD-DM con p processori.
Dobbiamo analizzare 3 punti:
1)Algoritmo per l'ordinamento di un vettore bitonico mediante il bitonic sort
DEFINIZIONE s=(s1 s2 .........sn) un vettore bitonico se e solo se esiste un indice k[1,n] tale che:
s1s2......sk e sk+1....sn oppure s1s2.....sk e sk+1....sn ossia sk l'elemento di separazione
tra le due parti del vettore una ordinata in modo crescente e l'altra in modo decrescente. Se k =n il
vettore detto MONOTONICO.
Ordinare un vettore bitonico. A partire dalle due met gi ordinate si confrontano gli elementi
omologhi scambiando gli elementi di ciascuna coppia che non si trovano nell'ordine desiderato. Si
ripete questo procedimento applicandolo separatamente a ciascuna met. Si prosegue fino a
quando si arriva ad applicarlo a coppie di elementi. Ad ogni passo e per ogni coppia si effettuano
operazioni di COMPARE-EXCHANGE ( confronta e scambia ) che viene applicata ricorsivamente ai
sottovettori di lunghezza uguale alla met di quella precedente. Ci sono due tipo di COMPAREEXCHANGE: Co-Ex-Lo se si vuole ordinare la coppia in senso crescente e Co-Ex-Hi se si vuole
ordinare la coppia in senso decrescente.
Complessit di tempo: Ad ogni passo si dimezza la lunghezza del vettore, l'algoritmo costituito da
log2n passi ad ogni passo si effettuano n/2 Co-Ex allora la complessit di tempo O(n log2n)
confronti che migliore di algoritmi con complessit n 2 ma il vettore di partenza deve essere
bitonico.
2) algoritmo per ordinare un vettore qualsiasi mediante il general bitonic sort
L'idea quella di trasformare il vettore qualsiasi in un vettore bitonico e applicare il bitonic sort.
Partizioniamo il vettore in coppie e ordiniamo le coppie prima a 2 a 2 poi 4 a 4 ....etc con BBS fino
ad avere 2 sottosequenze ordinate in modo bitonico di lunghezza n/2. Ordiniamo ciascuno dei due
sottovettori in modo da avere un unico vettore bitonico a questo punto applico il bitonic sort e
ottengo il vettore ordinato.
Complessit di tempo: l'algoritmo costituito da log2n passi, ad ogni passo si utilizza l'algoritmo
BBS su sequenze di lunghezza minore di n la cui complessit di tempo O(n log2n) allora la
complessit di tempo di GBS O ( nlog22n )
3) algoritmo parallelo per l'ordinamento di un vettore qualsiasi ( parallel general bitonic sort )
L'operazione di confronto-scambio che nell'algoritmo sequenziale applicata a due elementi viene
ora applicata ad una coppia di processori.
DEFINIZIONE p0<p1 se e solo se tutti gli elementi di p0 sono minori di tutti gli elementi di p1.
Se gli elementi di p0 e p1 sono ordinati localmente per effettuare il confronto p0<p1 basta
confrontare il pi grande di p0 con il pi piccolo di p1
Innanzitutto distribuiamo il vettore assegnato equamente tra tutti i processori.
FASE 1 ( SORTING LOCALE ) Ciascun processore ordina il proprio sottovettore utilizzando un
algoritmo di ordinamento
FASE 2 ( MERGING )
L'operazione di compare- exchage viene applicata ai due processori.
Supponiamo di avere due processori : si scambiano il massimo di uno e il minimo dell'altro e si
confrontano. Se se minore il primo processore lo inserisce se maggiore in secondo lo inserisce
eliminando gli elementi scambiati. e cos via finch non si possono pi scambiare elementi e sono
ordinati.