Dato un vettore di 10 interi, dire se la somma degli elementi di posto (indice) pari è maggiore della somma degli elementi di posto dispari. Implementare il precedente algoritmo con una programmazione concorrente che preveda esattamente due sotto-processi in parallelo fornendo il grafo delle precedenze.
Vediamo un esercizio semplice ma molto pratico di come usare i nostri processi al di la delle semplice operazioni algebriche viste su un polinomio. Siamo di fronte ad un classico esercizio che si lascia risolvere agli alunni meno esperti della programmazione, qualunque sia il linguaggio di riferimento, con l’uso dei vettori. Nel nostro caso lo rendiamo più interessante parallelizzando la procedura richiesta. Siamo di fronte ad un vettore, che assumiamo “precaricato” con un certo numero di elementi interi, ad esempio dieci. Nulla toglie di poter generalizzare la grandezza del vettore in questione con una dimensione N ed inserire i record fino al raggiungimento del numero N richiesto sempre da tastiera. L’algoritmo deve scorrere il vettore in questione e andare a fare due somme: i numeri corrispondenti alle posizioni del vettore con indice pari e, similmente, una somma per i numeri corrispondenti agli indici dispari. Assumiamo da bravi informatici che i vettori comincino sempre dalla posizione zero.
Indice dei contenuti
L’algoritmo imperativo
Per rinfrescare la memoria dell’alunno meno attento, il seguente è il codice imperativo, eseguito in maniera sequenziale da un unico processo.
vettore = [10,20,11,5,15,32]
indice = 0
sommapospari = 0
sommaposdispari = 0
for num in vettore:
if indice % 2 == 0:
#posizione pari
sommapospari += num
else :
#poizione dispari
sommaposdispari += num
indice+=1
if sommapospari >= sommaposdispari :
print ("La somma dei numeri di posizione pari è maggiore")
else:
print ("La somma dei numeri di posizione dispari è maggiore")
Per il lettore l’unica parte critica potrebbe essere ricordare uno dei cicli for del python che usa un buffer per il valore prelevato da vettore o lista e la relativa interazione con una variabile indice che semplicemente tiene conto della posizione che stiamo processando ad ogni giro. La parte conclusiva stampa un messaggio su quale delle due somme processate sia maggiore.
L’algoritmo parallelo
Veniamo alla parallelizzazione. Ci troviamo di fronte ad una struttura dati piuttosto semplice e statica: gli indici pari e dispari sono ben definiti e distinti tra loro, non c’è sovrapposizione tra due processi che debbano occuparsi di interagire con uno dei due. Non ci sono scritture simultanee sul vettore ma solo letture che non richiedono alcuna attività di blocco esclusivo. Si tratta di un esempio sostanzialmente simile a quello visto con i polinomi e il loro calcolo (guarda qui Grafo delle precedenze – Esercizi (svolti e non))
- Un processo principale scorrerà gli indici dispari ed un figlio con opportuno fork sommerà pari.
- Ricongiunto il figlio alla fine delle rispettive iterazioni, il controllo e messaggio finale da parte del processo padre.
Abbiamo deliberatamente scelto una soluzione che semplifica le operazioni di somma con due funzioni distinte che in realtà possono anche essere ridotte ad una sola parametrizzata e decisamente più efficiente a livello di codice scritto, ma lasciamo al lettore le dovute migliorie.
import os
vettore = [100,20,11,5,15,32]
indice = 0
sommapospari = 0
sommaposdispari = 0
def sommaindicipari():
somma = 0
indice = 0
for num in vettore:
if indice % 2 == 0:
#posizione pari
somma += num
indice+=1
return somma
def sommaindicidispari():
somma = 0
indice = 0
for num in vettore:
if indice % 2 != 0:
#poizione dispari
somma += num
indice+=1
return somma
if __name__ == "__main__":
pid = os.fork()
if pid == 0:
somma = sommaindicipari()
#uso lo stato per tornare un valore
os._exit(somma)
else :
sommaposdispari = sommaindicidispari()
pid,status = os.wait() #la join
sommapospari = os.WEXITSTATUS(status)
if sommapospari >= sommaposdispari :
print ("La somma dei numeri di posizione pari è maggiore")
else:
print ("La somma dei numeri di posizione dispari è maggiore")
Misuriamo le performance
Un esercizio interessante per lo studente potrebbe essere quello di creare un vettore molto grande con numeri interi casuali, anche con 10.000.000 e confrontare con un timer come l’implementazione parallela sia più veloce di quella mono-processo. Nei miei test con una CPU Intel I5 8600 si passa da circa 1,7 sec per l’esecuzione parallela a 2,6 sec per quella a processo singolo. Una bella evidenza per un esercizio così semplice!
Modificate i vostri script con queste righe all’inizio e alla fine per aggiungere una funzionalità che ci crea l’array casuale e un semplice timer di inizio fine.
import numpy
import time
vettore = numpy.random.rand(10000000)
vettore = numpy.array(vettore,dtype=int)
start = time.time()
#.... resto del programma
end = time.time()
print (end - start)
Ultima modifica 28 Gennaio 2024