Algoritmi concorrenti
Lezione n°15
Algoritmi Avanzati
a.a.2013/2014
Prof.ssa Rossella Petreschi
Sistemi concorrenti
Sistemi paralleli MIMD asincroni con memoria condivisa
Ogni processore esegue un proprio programma sequenziale (processo),
che può essere uguale o diverso da quello degli altri processori.
I processi scambiano i dati attraverso la memoria comune e, poiché i
processi sono asincroni, bisogna accedere alle variabili comuni in modo
mutuamente esclusivo.
Mutua esclusione
Per garantire la mutua esclusione su gli accessi, si usano le primitive di
sincronizzazione: lock ed unlock.
Se un processo P vuole acquisire una variabile x, si deve verificare se x è libera
o già acquisita da un altro processore Q.
Quando il processo P vuole acquisire x:
• se x è libera, P deve eseguire lock(x). In tal caso nessun altro processore potrà
accedere ad x finché P non la rilascerà, ovvero finché P non eseguirà unlock(x).
• se x è già acquisita da un altro processore Q, allora P deve rimanere in attesa
finché Q non eseguirà unlock(x).
Stallo
Associamo delle parentesi alle operazioni di lock e unlock: lock(x)
(parentesi aperta), unlock(x) (parentesi chiusa).
Chiamiamo sezione critica la porzione di codice comprendente tutte le
istruzioni necessarie per l’elaborazione della variabile x : la sezione
critica sarà quindi compresa fra una parentesi aperta (lock(x)) e una
parentesi chiusa (unlock(x)).
Data la natura asincrona dei sistemi concorrenti, si può creare un
fenomeno di stallo quando uno o più processi rimangono in attesa per un
tempo “infinito”.
Esempio:
P:… lock(x)…lock(y)…unlock(x)…unlock(y)
Q:…lock(y)…lock(x)…unlock(x)…unlock(y)
Analisi delle prestazioni
Una analisi teorica delle prestazioni di una macchina concorrente si può
discostare molto dalle prestazioni reali perché bisogna tener conto delle attese
dovute sia a sincronizzazione su variabili condivise sia a conflitti sul circuito
di indirizzamento della memoria per accessi a variabili non condivise.
Misurando le prestazioni tramite speed-up si può arrivare così al
PARADOSSO: aumentando il numero di processi si peggiora lo speed-up.
Ha senso misurare le prestazioni di una macchina concorrente solo in modo
sperimentale su macchine reali, variando il numero di processori.
Calcolo del massimo
Per calcolare il massimo in modo concorrente, consideriamo che nel programma principale
sia opportunamente inizializzata una variabile massimo globale (max-glo) e che poi ci sia un
ciclo parallelo che faccia cercare ad ogni processore, nel sottoinsieme di n/p valori ad esso
assegnati, il proprio massimo locale(max-lo).
For all 1 ≤ i ≤ p concurdo MAX(i)
In Max(i), ogni processore, dopo aver calcolato sequenzialmente il proprio max-lo(i), chiede
l’esclusività della variabile max-glo (lock(max-glo)) per poterla confrontare con il proprio
max-lo ed eventualmente aggiornarla. Poi max-glo verrà rilasciata (lock(max-glo)).
Lock(max-glo);
If max-lo(i) > max-glo then max-glo =max-lo(i);
Unlock(max-glo).
La complessità teorica di questo algoritmo è O(n/p +p) che diventa O (√n) se p=√n.
In tal caso anche lo speed-up sarà O(√n).
Cammini minimi
Input: G (V,E), grafo pesato con pesi interi su gli archi e r nodo di V.
Output: per ogni nodo v di V, trovare il cammino di costo minimo da r a v.
Condizione: non debbono esistere in G cicli di lunghezza negativa.
Soluzione ammissibile: albero di copertura radicato in r (che quindi include
un cammino da r ad ogni altro nodo di V).
Soluzione ottima (teorema di Bellman):
una soluzione ammissibile T è ottima sse vale che:
per ogni arco (u,v) di T:
dv = du + cu,v
per ogni arco (u,v) di G:
dv ≤ du + cu,v
Procedura concorrente per cammini minimi
Input: G (V,E), grafo pesato con pesi interi su gli archi e r nodo di V.
Output: per ogni nodo v di V, trovare il cammino di costo minimo da r a v.
Procedura Cammini-minimi (G,r)
begin
for all 1 ≤ i ≤ p concurdo Inizializza(i);
poni r in S;
poni stop a false; (S e stop sono variabili globali)
for all 1 ≤ i ≤ p concurdo Ricerca(i)
end;
Inizializza(i)
begin
for v = 1 to n by p do poni dv e pv a 0;
else poni dv ad infinito e pv ad r;
end;
Procedura Ricerca
Ricerca(i)
Ogni processore pone a false la sua variabile locale “attesa”. La variabile
globale “stop” si ottiene dall’end di tutte le attese (i). Attesa(i) è posta a true
quando S è pari all’insieme vuoto, ovvero quando nessun processore trova
più vertici su cui lavorare. Ogni processore lavora in esclusiva su S per
prelevare il vertice u, se ancora c’è, per cui calcolare il cammino minimo
Begin
poni a false attesa(i)
while not stop do lock(S);
if S è vuoto then poni a true attesa(i);
if i=1 then stop = and attesa(i), per ogni i;
unlock(s)
else estrai u dalla testa di S;
poni a false attesa(i); unlock(s)
segue
For each (u,v) in E do…
Ora l’algoritmo applica il teorema di Bellman:
for each (u,v) di E do dist. =du + cu,v;
lock (dv)
if dist.a < dv
then poni dv = dist.a e pv= u;
unlock(dv);
lock (S); inserisci v in S se già non c’è; unlock
(S);
else unlock(dv);
chiudi tutto.