Docsity
Docsity

Prepara i tuoi esami
Prepara i tuoi esami

Studia grazie alle numerose risorse presenti su Docsity


Ottieni i punti per scaricare
Ottieni i punti per scaricare

Guadagna punti aiutando altri studenti oppure acquistali con un piano Premium


Guide e consigli
Guide e consigli


Appunti di Parallel Computing, Appunti di Programmazione Avanzata

Appunti completi del corso di Parallel Computing (2023/2024) tenuto dal professore Marco Bertini. Gli appunti contengono: introduzione, concetti di parallelizzazione, threads, profiling e timing, CPU, vettorizzazione, OpenMP, GPU e CUDA.

Tipologia: Appunti

2022/2023

In vendita dal 01/10/2024

Delba98
Delba98 🇮🇹

5

(3)

16 documenti

1 / 242

Toggle sidebar

Questa pagina non è visibile nell’anteprima

Non perderti parti importanti!

bg1
Parallel Computing
2023-2024
pf3
pf4
pf5
pf8
pf9
pfa
pfd
pfe
pff
pf12
pf13
pf14
pf15
pf16
pf17
pf18
pf19
pf1a
pf1b
pf1c
pf1d
pf1e
pf1f
pf20
pf21
pf22
pf23
pf24
pf25
pf26
pf27
pf28
pf29
pf2a
pf2b
pf2c
pf2d
pf2e
pf2f
pf30
pf31
pf32
pf33
pf34
pf35
pf36
pf37
pf38
pf39
pf3a
pf3b
pf3c
pf3d
pf3e
pf3f
pf40
pf41
pf42
pf43
pf44
pf45
pf46
pf47
pf48
pf49
pf4a
pf4b
pf4c
pf4d
pf4e
pf4f
pf50
pf51
pf52
pf53
pf54
pf55
pf56
pf57
pf58
pf59
pf5a
pf5b
pf5c
pf5d
pf5e
pf5f
pf60
pf61
pf62
pf63
pf64

Anteprima parziale del testo

Scarica Appunti di Parallel Computing e più Appunti in PDF di Programmazione Avanzata solo su Docsity!

Parallel Computing

Indice

  • 1 INTRODUZIONE
    • 1.1 Tipi di parallelismo
      • 1.1.1 Tassonomia di Flynn
    • 1.2 Architetture di parallelizzazione
    • 1.3 Metriche per la valutazione delle prestazioni
      • 1.3.1 Legge di Amdahl
      • 1.3.2 Legge di Gustafson
      • 1.3.3 Performance
    • 1.4 Creare un programma parallelo
  • 2 PARALLELIZZAZIONE
    • 2.1 Modelli di progettazione per programmi paralleli
      • 2.1.1 Decomposizione dei task
      • 2.1.2 Decomposizione dei dati
      • 2.1.3 Design data-oriented
      • 2.1.4 Comunicazioni
    • 2.2 Esempi di strutture dati
    • 2.3 Proprietà importanti
    • 2.4 Design patterns
      • 2.4.1 Sequenza superscalare
      • 2.4.2 Parallelismo a livello di loop
      • 2.4.3 Parallelismo dei task
      • 2.4.4 Fork-Join
      • 2.4.5 SPMD
      • 2.4.6 Master-Worker
      • 2.4.7 Client-Server
      • 2.4.8 Task pool
      • 2.4.9 Producer-Consumer
      • 2.4.10 Work Queue
      • 2.4.11 Pipeline
      • 2.4.12 Map
      • 2.4.13 Riduzione
      • 2.4.14 Scan
      • 2.4.15 Stencil
      • 2.4.16 Recurrence
    • 2.5 Scambio di informazioni
    • 2.6 Regole per la progettazione di applicazioni multi-thread
    • 2.7 Metodologie di progettazione
  • 3 THREADS INDICE ii
    • 3.1 Processo
    • 3.2 Thread
      • 3.2.1 Stati di un thread
      • 3.2.2 Visibilità dei dati
      • 3.2.3 Meccanismi di sincronizzazione
      • 3.2.4 Controllo dell’esecuzione dei thread
      • 3.2.5 Numero di thread e sequenzializzazione
      • 3.2.6 Deadlock
      • 3.2.7 Accesso alla memoria
  • 4 TIMING & PROFILING
    • 4.1 Timing
    • 4.2 Profiling
      • 4.2.1 Google Perftools
      • 4.2.2 Callgrind
      • 4.2.3 VTune
    • 4.3 Bottleneck
  • 5 CPU MODERNE
  • 6 VETTORIZZAZIONE
  • 7 OpenMP
    • 7.1 Consistenza della memoria
      • 7.1.1 Coerenza sequenziale
      • 7.1.2 Coerenza rilassata
      • 7.1.3 Coerenza della memoria in OpenMP
    • 7.2 Panoramica di OpenMP in C e C++
    • 7.3 Direttive
      • 7.3.1 parallel
      • 7.3.2 for
      • 7.3.3 sections
      • 7.3.4 task
      • 7.3.5 single
      • 7.3.6 master
      • 7.3.7 critical
      • 7.3.8 atomic
      • 7.3.9 barrier
      • 7.3.10 Locking
      • 7.3.11 flush
      • 7.3.12 threadprivate
    • 7.4 Producer/Consumer
    • 7.5 Thread sanitizer
    • 7.6 Ottimizzazione in OpenMP
      • 7.6.1 Processo di ottimizzazione
  • INDICE
  • 8 GPU E CUDA
    • 8.1 Modello di programmazione GPU
    • 8.2 CUDA
      • 8.2.1 Gestione della memoria
      • 8.2.2 Organizzazione dei thread
      • 8.2.3 Kernel
    • 8.3 Memoria CUDA
      • 8.3.1 Tiling
      • 8.3.2 Coalescence
      • 8.3.3 Sovrapposizione nel trasferimento dei dati
    • 8.4 Atomic CUDA
    • 8.5 Privatizzazione CUDA
    • 8.6 Pattern CUDA
      • 8.6.1 Convoluzione
      • 8.6.2 Map
      • 8.6.3 Gather
      • 8.6.4 Scatter
      • 8.6.5 Riduzione
      • 8.6.6 Scan
    • 8.7 Librerie CUDA
      • 8.7.1 Thrust
      • 8.7.2 CuBLAS
      • 8.7.3 Fast Math
      • 8.7.4 NVIDIA libcu++

Capitolo 1

INTRODUZIONE

Un sistema si dice concorrente se può supportare due o più azioni in progresso allo stesso momento. Ovvero, un’applicazione concorrente avrà due o più thread in pro- gresso in un determinato momento (ad esempio, due thread vengono continuamente scambiati dal sistema operativo su un processore a singolo core per avanzare progressi- vamente). Un sistema si dice parallelo se può supportare due o più azioni in esecuzione allo stes- so momento. Ovvero, un’applicazione parallela avrà due o più thread in esecuzione si- multanea se il calcolatore possiede più core disponibili (ad esempio, due o più thread vengono assegnati a core separati per essere eseguiti contemporaneamente). N.B : possiamo considerare il parallelismo come un sottoinsieme della concorrenza, ovvero Parallelismo ⊆ Concorrenza.

Un programma concorrente diventa parallelo se sono disponibili abbastanza core ( unità di elaborazione ) per eseguire thread (o processi multipli) simultaneamente. Quindi, la programmazione concorrente è caratterizzata dalla composizione di pro- cessi in esecuzione indipendente. Invece, la programmazione parallela è caratterizzata dall’esecuzione simultanea di calcoli. Sotto questo aspetto, la concorrenza rappresenta la gestione di molte azioni contempo- raneamente. Invece, il parallelismo consiste nel fare molte azioni allo stesso momento. Quindi, la concorrenza fornisce un modo di strutturare la risoluzione di un problema che può essere parallelizzabile (ma non necessariamente).

Un algoritmo concorrente è un algoritmo strutturato in modo tale da poter eseguire i suoi passaggi in modo indipendente. Un algoritmo concorrente può essere eseguito in serie. Tuttavia, è necessaria una comunicazione per coordinare le esecuzioni indipenden- ti. Un algoritmo parallelo è progettato per eseguire più operazioni nello stesso momento. Il parallelismo di un algoritmo può migliorare le prestazioni su diversi tipi di computer. N.B : il termine parallelizzazione indica la traduzione di un codice seriale in un codice concorrente.

necessaria per far funzionare la CPU ad una determinata frequenza è approssimativa- mente proporzionale alla frequenza (ovvero Vf ). Perciò, il consumo di potenza di una CPU sarà proporzionale al cubo della frequenza, cioè Pf^3. Quindi, aumentando la frequenza, possiamo elaborare in un tempo minore la stessa quantità di lavoro, ma l’energia richiesta dal sistema di calcolo dipenderà in modo qua- dratico dalla frequenza, ovvero Ef^2 (questa energia verrà dissipata sotto forma di calore). Il parallelismo, può essere utilizzato per conservare l’energia richiesta dalla CPU. L’idea è quella di incrementare il numero di core disponibili, ognuno dei quali opera ad una frequenza minore. In questo modo, ogni singolo core può eseguire un numero di istru- zioni minore in un’unita temporale, ma essi opereranno simultaneamente riducendo il tempo di esecuzione dei task.

Le GPU hanno bisogno di molta potenza, ma grazie al parallelismo possiamo ridurre il consumo.

�����ES: Il processore Xeon E5-4660 a 16 core di Intel ha una potenza termica di 120 W. Supponiamo che l’applicazione venga completata utilizzando 20 pro- cessori di questo tipo per 24 ore. Il consumo energetico stimato per l’esecu- zione dell’applicazione sarà:

P = (20 CPU) ×

( 120

W

CPU

) × (24ore) = 57_._ 60 kWh

Supponiamo adesso di eseguire l’applicazione su quattro GPU NVIDIA Te- sla V100 per 24 ore. Una GPU Tesla V100 di NVIDIA ha una potenza ter- mica di 300 W. Il consumo energetico stimato per l’esecuzione dell’applica- zione sarà:

P = (4 GPU) ×

( 300

W

GPU

) × (24ore) = 28_._ 80 kWh

In generale, le GPU hanno una potenza termica superiore alle CPU, ma possono potenzialmente ridurre il tempo di esecuzione o richiedere solo po- che GPU per ottenere lo stesso risultato.

Osserviamo che alcuni programmi paralleli possono essere peggiori delle loro contropar- ti sequenziali. Questo è dovuto principalmente a due fattori:

  • Rallentamenti causati dall’overhead della comunicazione tra le unità di elabora- zione.
  • Alcuni algoritmi paralleli sono più veloci solo quando le dimensioni del problema sono molto grandi.

1.1 Tipi di parallelismo

Analizziamo i principali tipi di parallelismo:

  • Bit-Level : rappresenta la forma di calcolo parallelo che si basa sull’aumento del- la dimensione delle parole del processore. In questo tipo di parallelismo, l’aumen- to della dimensione della parola riduce il numero di istruzioni che il processore deve eseguire per compiere un’operazione su dati di grandi dimensioni (ovvero, le cui dimensioni sono maggiori della lunghezza della parola). Ad esempio, consideriamo uno scenario in cui un processore a 8 bit deve calcolare la somma di due numeri interi a 32 bit. A tale scopo è necessario effettuare una sequenza di operazioni ad 8 bit, richiedendo quindi quattro istruzioni per eseguire l’operazione (si sommano in modo sequenziale parti di 8 bit dei due numeri). Al contrario, un processore a 32 bit può eseguire la somma in un solo passaggio.
  • Instruction-Level : rappresenta la forma di calcolo parallelo che si basa sull’e- secuzione simultanea di più istruzioni di un programma. Alcune soluzioni che implementano questo tipo di parallelismo sono: - Pipelining : le istruzioni vengono eseguite in modo sovrapposto (si eseguono istruzioni diverse in modo simultaneo, ciascuna in una fase diversa). - Esecuzione Out-of-Order (O-o-O): le istruzioni vengono eseguite in qual- siasi ordine che non violi le dipendenze dai dati. - Esecuzione speculativa : le istruzioni vengono programmate prima di de- terminare la necessità della loro esecuzione. Deve tenere conto della possibi- lità che il comportamento previsto non sia corretto.
  • Data-Level : rappresenta la forma di calcolo parallelo che si basa sull’esecuzio- ne simultanea della stessa istruzione su un insieme di dati (ovvero, si esegue una singola istruzione su una grande quantità di dati in parallelo). Questo tipo di pa- rallelizzazione è conosciuta anche come SIMD ( Single Instruction Multiple Data ).
  • Task-Level : rappresenta la forma di calcolo parallelo che si basa sulla scompo- sizione di un task in sotto-task. Ciascuno di essi viene allocato per essere ese- guito in modo simultaneo. Esistono due varianti importanti, legate al modello di memoria sottostante: - Memoria Condivisa : i processori condividono lo stesso spazio degli indiriz- zi (ovvero, ogni CPU accede a qualsiasi posizione di memoria), semplificando la programmazione. In questo caso, la comunicazione tra i processi ( Inter- Process Communication o IPC ) avviene attraverso la memoria e quindi risulta essere rapida. Tuttavia, questa soluzione introduce potenziali conflitti di memoria, con conseguenti problemi di correttezza e prestazioni (infat- ti, sincronizzare l’accesso alla memoria e i valori tra le CPU è complicato e

di istruzioni e dati in operazioni seriali o multiple. In particolare, si distinguono quattro categorie:

  • SISD ( Single Instruction Single Data ): rappresenta l’architettura sequen- ziale che non sfrutta il parallelismo né nei flussi di istruzioni né in quelli di dati. Una singola unità di controllo applica una singola istruzione su un singolo flus- so di dati (cioè esegue un’operazione alla volta). Quindi, un singolo elemento di elaborazione ha accesso a un singolo programma e ad una memoria dati. In ogni fase, l’elemento di elaborazione carica un’istruzione e un flusso di dati, esegue l’istruzione e memorizza il risultato.
  • MISD ( Multiple Instruction Single Data ): più istruzioni operano su un uni- co flusso di dati. Tale architettura prevede più elementi di elaborazione, ognuno dei quali ha una memoria privata (per le istruzioni), ma con accesso comune ad una singola memoria dati globale. In ogni fase, ogni elemento di elaborazione ot- tiene lo stesso flusso di dati dalla memoria dati e carica le istruzioni dalla sua memoria privata. Queste istruzioni, eventualmente diverse, vengono eseguite in parallelo dagli elementi di elaborazione sul dato precedentemente ottenuto. Que- sto modello di esecuzione è molto restrittivo e quindi non è mai stato costruito un computer parallelo commerciale di questo tipo.
  • SIMD ( Single Instruction Multiple Data ): una singola istruzione viene ap- plicata contemporaneamente a più flussi di dati diversi. Tale architettura preve- de più elementi di elaborazione, ognuno dei quali ha un accesso privato ad una memoria dati (condivisa o distribuita). Tuttavia, è presente una sola memoria di programma (per le istruzioni), dalla quale uno speciale processore di control- lo preleva e distribuisce le istruzioni. In ogni fase, ogni elemento di elaborazione ottiene dal processore di controllo la stessa istruzione e carica un flusso di dati separato attraverso il suo accesso privato ai dati. In questo modo, l’istruzione vie- ne eseguita in modo sincrono e parallelo da tutti gli elementi di elaborazione su diversi elementi di dati.
  • MIMD ( Multiple Instruction Multiple Data ): più processori autonomi ese- guono simultaneamente istruzioni diverse su dati diversi. Tale architettura pre- vede più elementi di elaborazione, ognuno dei quali ha un accesso separato alle istruzioni ed ai dati attraverso una memoria di programma e di dati (condivisa o distribuita). In ogni fase, ogni elemento di elaborazione carica un’istruzione sepa- rata e un flusso di dati separato, applica l’istruzione al flusso di dati e memoriz- za il risultato nella memoria dati. Gli elementi di elaborazione lavorano in modo asincrono tra loro.

Possiamo riassumere questa categorizzazione attraverso la seguente tabella che rappre- senta la tassonomia di Flynn:

N.B : la tassonomia è stata ampliata con SPMD (Single Program Multiple Data), ov- vero un’architettura in cui i task vengono suddivisi ed eseguiti simultaneamente su più processori con input diversi, per ottenere risultati più rapidi (SPMD potrebbe essere considerato un programma parallelo su un computer MIMD).

Figura 1.2: La figura mostra il modello di computer parallelo CTA. Tale modello è costituito da P computer sequenziali, collegati da una rete di interconnessione (o rete di comunicazione). Inoltre viene mostrato in dettaglio come è costituito un processore Pi.

Un aspetto fondamentale dei computer paralleli è che i riferimenti alla memoria locale e non locale richiedono tempi diversi per essere completati. Il ritardo necessario per fare un riferimento alla memoria è chiamato Memory Latency (o latenza di memoria ) ed è indicato con la lettera λ. La Memory Latency non può essere specificata in secondi, perché il modello si gene- ralizza su molte architetture diverse, ognuna costruita con elementi di progettazione diversi provenienti da tecnologie diverse. Pertanto, viene specificata rispetto alla laten- za della memoria locale del processore, che viene considerata di tempo unitario. Questa convenzione implica che la latenza della memoria locale segue approssimativamente la velocità del processore e si ipotizza che la memoria locale possa essere referenziata alla velocità di una parola per istruzione. I riferimenti alla memoria non locale sono molto più costosi, avendo valori λ di 2 − 5 ordini di grandezza superiori ai tempi di riferimento della memoria locale.

1.3 Metriche per la valutazione delle prestazioni

Quando si parla di calcolo parallelo, la nozione di prestazioni è complessa perché ci so- no molteplici aspetti da considerare, tra cui l’uso della memoria, il tempo di elabora- zione e l’overhead. Un’applicazione può operare su valori organizzati come flussi o come singoli valori di dati. N.B : un flusso è una sequenza possibilmente infinita di valori dello stesso tipo (ad esempio, matrici di dimensioni note che rappresentano immagini).

Prendiamo in considerazione diverse metriche di performance:

  • Tempo di servizio ( service time ): rappresenta l’inverso della larghezza di ban- da di elaborazione ( processing bandwidth ) o del throughput medio del flusso. Indica quanto velocemente il sistema è in grado di elaborare i dati. Un tempo di servizio più breve denota una maggiore velocità di elaborazione ed una migliore distribuzione dei carichi di lavoro tra i nodi paralleli.
  • Tempo di completamento ( completion time ): rappresenta il tempo medio necessario per completare il calcolo su tutti gli elementi di un flusso. Indica il tempo totale necessario per completare un determinato compito o un insieme di operazioni. Questa misura tiene conto non solo del tempo di esecuzione effettivo delle operazioni, ma anche di tutti i ritardi, le sincronizzazioni e le comunicazioni necessarie tra i nodi paralleli.

N.B : quando si tratta di singoli valori di dati, il tempo di completamento è signi- ficativo al contrario del tempo di servizio.

  • Latenza ( latency ): rappresenta il tempo medio di elaborazione di un elemen- to del flusso. Indica il ritardo medio per l’elaborazione di ciascun elemento nel sistema parallelo. Una latenza ridotta indica una maggiore tempestività nell’ela- borazione di singoli elementi.

�����ES (programma sequenziale): Consideriamo un singolo modulo sequenziale Σ che opera su un flusso di dati di lunghezza m. In particolare, il modulo Σ implementa k operazioni, ciascuna con tempo medio di servizio ti e probabilità di occorrenza pi. Allo- ra possiamo analizzare le seguenti metriche:

  • Il tempo di servizio medio di Σ è ottenuto come la media pesata sulla probabilità che si verifichi l’operazione i per il tempo che essa richieda, ovvero:

t =

∑^ k

i =

pi · ti

  • La larghezza di banda di elaborazione (throughput) è ottenuta come il numero medio di operazioni eseguite da Σ nell’unità di tempo, ovvero:

B =

t

  • Il tempo di completamento medio sul flusso è ottenuto come il tempo medio per processare i singoli valori del flusso, ovvero:

tc = m · t

  • La latenza è ottenuta come il tempo medio necessario per elaborare un elemento del flusso. Poiché Σ è sequenziale, allora la latenza coincide con il tempo di servizio.

�����ES (programma parallelo): Consideriamo una trasformazione di Σ in una versione parallela Σ n^ di n moduli. Pertanto, il grado di parallelismo di Σ n n è sempre n , indipenden- temente dalla capacità effettiva di tutti i moduli di operare in parallelo in qualsiasi momento. Allora possiamo analizzare le seguenti metriche:

  • Il tempo di servizio medio tn^ è ottenuto come l’intervallo di tempo medio tra l’inizio delle esecuzioni su due elementi consecutivi del flusso.

Tale metrica mette in relazione il tempo di completamento ts del programma se- quenziale con il tempo di completamento tP del relativo programma parallelo, dove P è il numero di processori. Ovvero:

SP =

ts tP In particolare, la speedup viene detta:

- Ideale se SP = P. - Lineare se SP ∼= P. - Superlineare se SP > P , per qualche P.

N.B : quando i dati sono partizionati e distribuiti su P processori, i singoli da- ti sono (molto) più piccoli e possono essere inseriti interamente nella cache dei dati di ciascun processore. Pertanto, la riduzione delle miss nella cache per un al- goritmo con speedup lineare può portare ad una speedup superlineare. Questo è anche uno dei motivi per cui la scalabilità è più difficile da valutare rispetto alla speedup. N.B : alcuni fattori possono limitare la speedup, come la possibile inattività dei processori, l’esecuzione di calcoli aggiuntivi nella versione parallela e l’overhead associato alle operazioni di comunicazione e sincronizzazione tra i moduli.

Figura 1.3: Tipi di speedup. 1. Speedup ideale o lineare. 2. Speedup sublineare. 3. Speedup con un numero ottimale di processori. 4. No speedup. 5. Speedup superlineare.

  • Speedup relativa : viene definita come il rapporto tra il tempo di completamen- to t 1 per un algoritmo parallelo eseguito su un processore ed il tempo di comple- tamento tP per un algoritmo parallelo eseguito su P processori. Ovvero:

S P^1 =

t 1 tP

Analogamente, se consideriamo k processori, con k < P , allora possiamo definire la speedup relativa rispetto a k processori come:

SPk =

tk tP

La speedup relativa SPk viene utilizzata quando k rappresenta il numero più pic- colo di processori su cui può essere eseguito il problema.

  • Efficienza ( efficiency ): l’efficienza di un algoritmo parallelo che utilizza P pro- cessori viene definita come il rapporto tra la sua speedup ed il numero di proces- sori utilizzati. Ovvero: EP =

SP

P

L’efficienza fornisce una stima del grado di utilizzo dei processori per la risoluzio- ne del problema, in relazione alla quantità di sforzi sprecati a causa di inattività dei processori e delle operazioni di comunicazione e sincronizzazione. N.B : molti algoritmi difficilmente parallelizzabili hanno un’efficienza che si avvi- cina a zero all’aumentare del numero di processori P utilizzati. N.B : una speedup ideale comporta un’efficienza del 100%, ovvero EP = 1.

�����ES (speedup): Consideriamo un problema di ricerca all’interno di una struttura dati. Una possibile implementazione di un algoritmo parallelo per risolvere questo pro- blema coinvolge la suddivisione dello spazio di ricerca in P parti (o chunks ). Quindi, si assegna a ciascuno dei P processori un chunk specifico su cui ef- fettuare la ricerca. In questo contesto, la speedup viene valutata come se- gue: SP =

( x · t Ps ) + ∆ tt dove x rappresenta il numero di chunks visitati prima di individuare il dato cercato e ∆ t rappresenta il tempo aggiuntivo richiesto per eseguire la ricerca all’interno del chunk in cui è presente il dato.

Nel caso peggiore per la ricerca sequenziale, in cui l’elemento cercato si tro- va nell’ultimo chunk, la speedup tende all’infinito ( SP −→ ∞) quando ∆ t tende a zero. Questo scenario sottolinea il vantaggio significativo dell’ap- proccio parallelo quando il dato da cercare si trova all’interno dell’ultimo chunk.

Di conseguenza, la speedup massima possibile SP è limitata e tende a un valore (^) f^1 quan-

do il numero di processori P tende all’infinito (ovvero SP P −→∞ −−−−→ f −^1 ). Questo implica che anche se il 95% di un programma è parallelizzabile, il miglioramen- to della speedup non supererà mai un fattore di 20 volte. In altre parole, ci sono limiti intrinseci al grado di miglioramento che può essere ottenuto attraverso il parallelismo, e questi limiti sono fortemente influenzati dalla frazione di codice che non può essere parallelizzata.

La legge di Amdahl si applica principalmente nei contesti in cui la dimensione del pro- blema è fissa. Tuttavia, nella pratica, quando si dispone di risorse computazionali ag- giuntive, queste tendono ad essere impiegate per affrontare problemi di dimensioni maggiori (ovvero, insiemi di dati più grandi). In questa dinamica, il tempo dedica- to all’elaborazione della parte parallelizzabile può crescere in modo significativo più rapidamente rispetto al lavoro intrinsecamente sequenziale.

�����ES: Consideriamo un programma che deve effettuare due operazioni, ovvero la somma di 10 scalari (operazione sequenziale) e la somma di matrici di di- mensione 10 × 10 (operazione parallelizzabile). Se un singolo processore esegue queste operazioni (programma sequenziale), il tempo di completamento totale sarà:

t 1 = (10 + 100) · t sum = 110 · t sum

Se consideriamo l’utilizzo di 10 processori in grado di eseguire in parallelo il calcolo della somma matriciale, il tempo di completamento sarà:

t 10 = 10 · t sum +

· t sum = 20 · t sum

In questo caso, la speedup ottenuta utilizzando 10 processori sarà:

S 10 =

Ovvero, stiamo sfruttando il 55% del potenziale dei processori utilizzati. Invece, se consideriamo 100 processori, il tempo di completamento sarà:

t 100 = 10 · t sum +

· t sum = 11 · t sum

In questo caso, la speedup ottenuta utilizzando 100 processori sarà:

S 100 =

Ovvero, stiamo sfruttando il 10% del potenziale dei processori utilizzati. N.B : stiamo assumendo una distribuzione equa del carico tra i processori.

La legge di Amdahl descrive un fatto che si applica a un’istanza di calcolo. Una volta fissata l’istanza, considera gli effetti dell’aumento del parallelismo. Tuttavia, la mag- gior parte dei calcoli paralleli fissa il parallelismo ed espande le dimensioni dell’istan- za. In questo caso la percentuale di codice sequenziale spesso diminuisce man mano che si considerano istanze più grandi. Aumentando le dimensioni del problema, la par- te sequenziale può aumentare in modo trascurabile, rendendo una parte maggiore del problema adatta al parallelismo.

�����ES: Consideriamo l’esercizio precedente, ma aumentiamo la dimensione delle matrici a 100 × 100.