Calcolo in parallelo [Python]

Forum di discussioni su Python
Rispondi
FrancyDotNet
Moderatore
Moderatore
Messaggi: 969
Iscritto il: 01/05/2024, 23:26

Calcolo in parallelo [Python]

Messaggio da FrancyDotNet »

Ci sono diverse opzioni disponibili per eseguire calcoli in parallelo in Python. Ecco alcune delle soluzioni più comuni:
  • multiprocessing: la libreria multiprocessing di Python consente di utilizzare più core del processore per eseguire i calcoli in parallelo. È utile se il programma deve eseguire operazioni costose su grandi quantità di dati.
  • threading: Python ha anche la libreria threading per l'esecuzione di calcoli in parallelo. Tuttavia, poiché Python utilizza un'implementazione GIL (Global Interpreter Lock), i thread in Python non possono essere eseguiti su più core del processore.
  • asyncio: asyncio è una libreria di Python per la programmazione asincrona. È utile per le applicazioni che devono gestire molte richieste simultaneamente, come le applicazioni web.
  • joblib: joblib è una libreria Python che semplifica l'utilizzo di multiprocessing per i calcoli paralleli. È utile quando si deve eseguire la stessa operazione su molte parti di un grande set di dati.
La soluzione più efficiente dipende dal contesto in cui si utilizza il codice. Se si devono eseguire calcoli costosi su grandi quantità di dati, la multiprocessing potrebbe essere la soluzione migliore. Se si sta lavorando su un'applicazione web o su un altro tipo di applicazione che deve gestire molte richieste simultaneamente, asyncio potrebbe essere la scelta migliore. Se si deve eseguire la stessa operazione su molte parti di un grande set di dati, joblib potrebbe essere la soluzione migliore. Per quanto riguarda la libreria threading ho provato ad utilizzarla ma non ho mai riscontrato un vantaggio netto nell'utilizzo rispetto alla libreria asyncio per eseguire calcoli distribuiti su più processi.



:arrowright: Tecnica del MULTIPROCESSING:

In Python, puoi eseguire processi in parallelo utilizzando la libreria multiprocessing. La libreria multiprocessing fornisce un'API simile a quella dei thread e permette di eseguire funzioni in parallelo su più processi.

Per eseguire processi in parallelo con multiprocessing, segui questi passaggi:

1. Importa la libreria multiprocessing:

Codice: Seleziona tutto

import multiprocessing
2. Crea un'istanza dell'oggetto Pool, specificando il numero di processi da utilizzare:

Codice: Seleziona tutto

pool = multiprocessing.Pool(processes=4)
3. Definisci la funzione che deve essere eseguita in parallelo. Ad esempio, la seguente funzione calcola il quadrato di un numero:

Codice: Seleziona tutto

def square(x):
    return x*x
4. Usa il metodo apply_async() dell'oggetto Pool per eseguire la funzione in parallelo. Il metodo apply_async() accetta la funzione da eseguire come primo argomento, seguita dai parametri della funzione

Codice: Seleziona tutto

result = pool.apply_async(square, args=(10,))
5. Usa il metodo get() sull'oggetto restituito dal metodo apply_async() per ottenere il risultato della funzione:

Codice: Seleziona tutto

print(result.get()) # Output: 100
Puoi ripetere i passaggi 4 e 5 per eseguire più funzioni in parallelo.

Di seguito riporto il codice completo:

Codice: Seleziona tutto

import multiprocessing

def square(x):
    return x*x

if __name__ == '__main__':
    # Crea un'istanza dell'oggetto Pool con 4 processi
    with multiprocessing.Pool(processes=4) as pool:
        # Esegue la funzione square in parallelo
        result1 = pool.apply_async(square, args=(10,))
        result2 = pool.apply_async(square, args=(20,))
        result3 = pool.apply_async(square, args=(30,))
        result4 = pool.apply_async(square, args=(40,))

        # Ottiene i risultati
        print(result1.get()) # Output: 100
        print(result2.get()) # Output: 400
        print(result3.get()) # Output: 900
        print(result4.get()) # Output: 1600
:info: Nota bene: è importante includere la clausola if __name__ == '__main__': per evitare problemi con la creazione di nuovi processi in Python.

Un altro esempio di utilizzo del modulo multiprocessing potrebbe essere il seguente: definiamo due funzioni worker1() e worker2() che rappresentano i processi che vogliamo eseguire in parallelo. Nella funzione __main__, creiamo due processi process1 e process2, utilizzando il metodo Process() di multiprocessing e specificando le funzioni che vogliamo eseguire come argomenti.
Successivamente, per eseguire i processi, utilizziamo il metodo start() per avviare ogni processo. Successivamente, utilizziamo il metodo join() per attendere che ogni processo termini prima di terminare il programma.

Codice: Seleziona tutto

import multiprocessing

def worker1():
    print("Worker 1 is running")

def worker2():
    print("Worker 2 is running")

if __name__ == "__main__":
    process1 = multiprocessing.Process(target=worker1)
    process2 = multiprocessing.Process(target=worker2)
    
    process1.start()
    process2.start()
    
    process1.join()
    process2.join()



:arrowright: Tecnica del calcolo si più core:


:pirate4: Ecco un esempio pratico dell'utilizzo del modulo multiprocessing per eseguire in parallelo il calcolo di numeri primi.

Codice: Seleziona tutto

...
FrancyDotNet
Moderatore
Moderatore
Messaggi: 969
Iscritto il: 01/05/2024, 23:26

Re: Calcolo in parallelo [Python]

Messaggio da FrancyDotNet »

:arrowright: Tecnica della programmazione ASINCRONA:


:pirate4: Ecco un esempio pratico dell'utilizzo del modulo asyncio per eseguire in parallelo il calcolo di numeri primi.

Codice: Seleziona tutto

...
FrancyDotNet
Moderatore
Moderatore
Messaggi: 969
Iscritto il: 01/05/2024, 23:26

Re: Calcolo in parallelo [Python]

Messaggio da FrancyDotNet »

:arrowright: Tecnica della programmazione joblib:

:pirate1: Ecco un esempio pratico dell'utilizzo del modulo joblib per eseguire in parallelo il calcolo di numeri primi.

Codice: Seleziona tutto

...



:pirate2: Ecco qui una versione del codice che cerca i numeri primi senza utilizzare alcun modulo per il calcolo in parallelo:

Codice: Seleziona tutto

import time

def is_prime(num):
    if num < 2:
        return False
    for i in range(2, int(num**0.5)+1):
        if num % i == 0:
            return False
    return True

def find_primes(start, end):
    primes = []
    for num in range(start, end):
        if is_prime(num):
            primes.append(num)
    return primes

if __name__ == '__main__':
    start_time = time.time()

    max_num = 1000000
    primes = []
    for num in range(2, max_num):
        if is_prime(num):
            primes.append(num)

    end_time = time.time()
    print(f"Found {len(primes)} prime numbers in {end_time-start_time:.2f} seconds.")
In questa versione, abbiamo eliminato la classe Pool e il ciclo for utilizzato per creare i processi. Abbiamo invece utilizzato un semplice ciclo for per cercare i numeri primi nell'intervallo specificato.

Noterai che questa versione utilizza solo un singolo processo, quindi potrebbe impiegare più tempo per completare il calcolo dei numeri primi rispetto alla versione precedente. Tuttavia, poiché non c'è alcun overhead per la creazione dei processi e la comunicazione tra di essi, questa versione potrebbe essere più veloce su macchine con un singolo processore.
Rispondi

Torna a “Python”