Giovedì, 28 Maggio 2020 14:50

Somme su indici di un vettore con due processi in Python

Scritto da

Dato un vettore di 10 interi, dire se la somma degli elementi di posto 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 che si approcciano al mondo della programmazione, qualunque sia il linguaggio di riferimento, e 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 generica 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.

Per rinfrescare la memoria dell'alunno meno attento:
 
vettore = [10, 20, 11, 5, 15, 32]
#posizione  0   1   2  3   4   5

 

Questa sopra è una "non-dichiarazione" di un vettore in Python che non ha tipizzazione forte. In questo caso l'algoritmo deve fare una somma 10+11+15 che hanno posizione 0, 2, 4 ed una somma 20+5+32 che hanno posizione nel vettore 1,3,5. L'implementazione Python del problema semplice è molto semplice ma vale la pena scriverla per procedere poi a parallelizzare la stessa.

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.

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. 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")   

 

 

Un esercizio interessante per lo studente potrebbe essere quello di creare un vettore molto grande con numeri interi casuali, anche con 10.000.000 o più valori e confrontare con un timer come l'implementazione parallela sia più veloce di quella mono-processo. Nei miei test si passa da circa1,7 sec per l'esecuzione parallela a 2,6 sec per quella a processo singolo. Una bella evidenza per un esercizio così semplice! Per provarlo, modificate i vostri script con queste righe all'inizio e alla fine per aggiungere una funzionalità che ci crea l'array gigante 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)

 

Letto 80 volte Ultima modifica il Giovedì, 28 Maggio 2020 20:42
Prof. Alfredo Centinaro

Docente di "Scienze e tecnologie informatiche", "Tecnologie e progettazione di sistemi informatici", "Sistemi e Reti" presso IIS Alessandrini-Marino (Teramo), consulente e sviluppatore web. Ha collaborato per anni come sviluppatore presso MHT (ora Engineering 365), assistente Sistemi ed elaborazione dell'informazione in UniTE Corso di laurea in Scienze del turismo culturale, tutor presso Telecom Italia Learning Services (L'Aquila)

Joomla SEF URLs by Artio