1. Introduzione
Questo documento specifica una raccolta di direttive del compilatore, funzioni di libreria e variabili di ambiente che è possibile usare per specificare il parallelismo in memoria condivisa nei programmi C e C++. La funzionalità descritta in questo documento è nota collettivamente come Api (Application Program Interface) OpenMP C/C++. L'obiettivo di questa specifica è fornire un modello per la programmazione parallela che consente a un programma di essere portabile tra architetture di memoria condivisa di fornitori diversi. I compilatori di molti fornitori supportano l'API C/C++ OpenMP. Altre informazioni su OpenMP, inclusa l'interfaccia del programma applicativo OpenMP Fortran, sono disponibili nel seguente sito Web:
Le direttive, le funzioni di libreria e le variabili di ambiente definite in questo documento consentono di creare e gestire programmi paralleli, consentendo al tempo stesso la portabilità. Le direttive estendono il modello di programmazione sequenziale C e C++ con costrutti SPMD (Single Program Multiple Data), costrutti di condivisione di lavoro e costrutti di sincronizzazione. Supportano anche la condivisione e la privatizzazione dei dati. I compilatori che supportano l'API OpenMP C e C++ includono un'opzione della riga di comando per il compilatore che attiva e consente l'interpretazione di tutte le direttive del compilatore OpenMP.
1.1 Ambito
Questa specifica riguarda solo la parallelizzazione diretta dall'utente, in cui si definiscono in modo esplicito le azioni eseguite dal compilatore e dal sistema di runtime per eseguire il programma in parallelo. Le implementazioni di OpenMP C e C++ non sono necessarie per verificare la presenza di dipendenze, conflitti, deadlock, race condition o altri problemi che causano un'esecuzione non corretta del programma. L'utente è responsabile di garantire che l'applicazione che usa i costrutti dell'API C e C++ OpenMP venga eseguita correttamente. La parallelizzazione e le direttive automatiche generate dal compilatore per facilitare tale parallelizzazione non sono descritte in questo documento.
1.2 Definizione dei termini
In questo documento vengono utilizzati i termini seguenti:
barrier
Punto di sincronizzazione che tutti i thread in un team devono raggiungere. Ogni thread attende che tutti i thread del team arrivino a questo punto. Esistono barriere esplicite identificate dalle direttive e dalle barriere implicite create dall'implementazione.
construct
Un costrutto è un'istruzione . È costituito da una direttiva, seguita da un blocco strutturato. Alcune direttive non fanno parte di un costrutto. Vedere la direttiva openmp nell'appendice C.
direttiva
C o C++
#pragma
seguito dall'identificatore, dall'altroomp
testo e da una nuova riga. La direttiva specifica il comportamento del programma.extent dinamico
Tutte le istruzioni nell'extent lessicale, oltre a qualsiasi istruzione all'interno di una funzione eseguita come risultato dell'esecuzione di istruzioni all'interno dell'extent lessicale. Un extent dinamico viene definito anche area.
extent lessicale
Istruzioni lessicalmente contenute all'interno di un blocco strutturato.
thread master
Thread che crea un team quando viene immessa un'area parallela.
area parallela
Le istruzioni che si associano a un costrutto parallelo OpenMP e possono essere eseguite da molti thread.
private
Una variabile privata assegna un nome a un blocco di archiviazione univoco per il thread che effettua il riferimento. Esistono diversi modi per specificare che una variabile è privata: una definizione all'interno di un'area parallela, una direttiva, una
threadprivate
private
clausola ,lastprivate
firstprivate
, oreduction
l'uso della variabile comefor
variabile di controllo del ciclo in unfor
ciclo immediatamente successivo a unafor
direttiva oparallel for
.area geografica
Extent dinamico.
area seriale
Istruzioni eseguite solo dal thread master all'esterno dell'extent dinamico di qualsiasi area parallela.
serialize
Per eseguire un costrutto parallelo con:
un team di thread costituito da un solo thread (che è il thread master per quel costrutto parallelo),
ordine seriale di esecuzione per le istruzioni all'interno del blocco strutturato (lo stesso ordine di se il blocco non faceva parte di un costrutto parallelo) e
nessun effetto sul valore restituito da
omp_in_parallel()
(a parte gli effetti di eventuali costrutti paralleli annidati).
Condiviso
Una variabile condivisa assegna un nome a un singolo blocco di archiviazione. Tutti i thread di un team che accedono a questa variabile accedono anche a questo singolo blocco di archiviazione.
blocco strutturato
Un blocco strutturato è un'istruzione (singola o composta) con una singola voce e una singola uscita. Se è presente un passaggio o un'uscita da un'istruzione, tale istruzione è un blocco strutturato. Questa regola include una chiamata a
longjmp
(3C) o l'uso dithrow
, anche se è consentita una chiamata aexit
. Se l'esecuzione inizia sempre all'apertura{
e termina sempre alla chiusura}
, un'istruzione composta è un blocco strutturato. Un'istruzione di espressione, un'istruzione di selezione, un'istruzione di iterazione otry
un blocco strutturato è un blocco strutturato se l'istruzione composta corrispondente ottenuta racchiudendola in{
e}
sarebbe un blocco strutturato. Un'istruzione jump, un'istruzione etichettata o un'istruzione di dichiarazione non è un blocco strutturato.team
Uno o più thread che collaborano nell'esecuzione di un costrutto.
thread
Un'entità di esecuzione con un flusso seriale di controllo, un set di variabili private e l'accesso alle variabili condivise.
Variabile
Identificatore, facoltativamente qualificato dai nomi degli spazi dei nomi, che assegna un nome a un oggetto.
1.3 Modello di esecuzione
OpenMP usa il modello fork join di esecuzione parallela. Anche se questo modello di fork join può essere utile per risolvere vari problemi, è personalizzato per applicazioni basate su array di grandi dimensioni. OpenMP è progettato per supportare programmi che vengono eseguiti correttamente sia come programmi paralleli (molti thread di esecuzione e una libreria di supporto OpenMP completa). È anche per i programmi che vengono eseguiti correttamente come programmi sequenziali (direttive ignorate e una semplice libreria di stub OpenMP). Tuttavia, è possibile e permesso di sviluppare un programma che non si comporta correttamente quando viene eseguito in sequenza. Inoltre, diversi gradi di parallelismo possono comportare risultati numerici diversi a causa delle modifiche apportate all'associazione di operazioni numeriche. Ad esempio, una riduzione dell'addizione seriale può avere un modello diverso di associazioni di addizione rispetto a una riduzione parallela. Queste diverse associazioni possono modificare i risultati dell'addizione a virgola mobile.
Un programma scritto con l'API C/C++ OpenMP inizia l'esecuzione come un singolo thread di esecuzione denominato thread master. Il thread master viene eseguito in un'area seriale fino a quando non viene rilevato il primo costrutto parallelo. Nell'API C/C++ OpenMP la parallel
direttiva costituisce un costrutto parallelo. Quando viene rilevato un costrutto parallelo, il thread master crea un team di thread e il master diventa master del team. Ogni thread del team esegue le istruzioni nell'extent dinamico di un'area parallela, ad eccezione dei costrutti di condivisione di lavoro. Tutti i thread del team devono incontrare costrutti di condivisione di lavoro nello stesso ordine e uno o più thread eseguono le istruzioni all'interno del blocco strutturato associato. La barriera implicita alla fine di un costrutto di condivisione di lavoro senza una nowait
clausola viene eseguita da tutti i thread del team.
Se un thread modifica un oggetto condiviso, influisce non solo sul proprio ambiente di esecuzione, ma anche su quelli degli altri thread nel programma. È garantito che la modifica sia completa, dal punto di vista di un altro thread, al punto di sequenza successivo (come definito nel linguaggio di base) solo se l'oggetto è dichiarato volatile. In caso contrario, la modifica verrà completata dopo il primo thread di modifica. Gli altri thread visualizzano quindi (o simultaneamente) una flush
direttiva che specifica l'oggetto (in modo implicito o esplicito). Quando le flush
direttive implicite da altre direttive OpenMP non garantiscono l'ordinamento corretto degli effetti collaterali, è responsabilità del programmatore fornire direttive aggiuntive esplicite flush
.
Al termine del costrutto parallelo, i thread del team vengono sincronizzati in corrispondenza di una barriera implicita e solo il thread master continua l'esecuzione. In un singolo programma è possibile specificare un numero qualsiasi di costrutti paralleli. Di conseguenza, un programma può creare un fork e un join molte volte durante l'esecuzione.
L'API C/C++ OpenMP consente ai programmatori di usare direttive nelle funzioni chiamate da costrutti paralleli. Le direttive che non vengono visualizzate nell'estensione lessicale di un costrutto parallelo, ma che possono trovarsi nell'extent dinamico sono denominate direttive orfane . Con le direttive orfane, i programmatori possono eseguire parti principali del programma in parallelo, con solo modifiche minime al programma sequenziale. Con questa funzionalità, è possibile codificare costrutti paralleli ai livelli principali dell'albero delle chiamate del programma e usare direttive per controllare l'esecuzione in una delle funzioni chiamate.
Le chiamate non sincronizzate alle funzioni di output C e C++ che scrivono nello stesso file possono comportare l'output in cui i dati scritti da thread diversi vengono visualizzati in ordine non deterministico. Analogamente, le chiamate non sincronizzate alle funzioni di input che leggono dallo stesso file possono leggere i dati in ordine non deterministico. L'uso non sincronizzato di I/O, in modo che ogni thread acceda a un file diverso, produa gli stessi risultati dell'esecuzione seriale delle funzioni di I/O.
1.4 Conformità
Un'implementazione dell'API OpenMP C/C++ è conforme a OpenMP se riconosce e mantiene la semantica di tutti gli elementi di questa specifica, come descritto nei capitoli 1, 2, 3, 4 e Appendice C. Le appendici A, B, D, E e F sono solo a scopo informativo e non fanno parte della specifica. Le implementazioni che includono solo un subset dell'API non sono conformi a OpenMP.
L'API OpenMP C e C++ è un'estensione del linguaggio di base supportato da un'implementazione. Se il linguaggio di base non supporta un costrutto di linguaggio o un'estensione visualizzato in questo documento, l'implementazione openMP non è necessaria per supportarla.
Tutte le funzioni della libreria C e C++ standard e le funzioni predefinite (ovvero le funzioni di cui il compilatore dispone di conoscenze specifiche) devono essere thread-safe. L'uso non sincronizzato di funzioni thread-safe da thread diversi all'interno di un'area parallela non produce un comportamento non definito. Tuttavia, il comportamento potrebbe non essere uguale a quello di un'area seriale. Una funzione di generazione di numeri casuali è un esempio.
L'API OpenMP C/C++ specifica che un determinato comportamento è definito dall'implementazione. In questi casi è necessaria un'implementazione OpenMP conforme per definire e documentarne il comportamento. Per un elenco dei comportamenti definiti dall'implementazione, vedere appendice E.
1.5 Riferimenti normativi
ISO/IEC 9899:1999, Information Technology - Linguaggi di programmazione - C. Questa specifica dell'API OpenMP fa riferimento a ISO/IEC 9899:1999 come C99.
ISO/IEC 9899:1990, Information Technology - Linguaggi di programmazione - C. Questa specifica dell'API OpenMP fa riferimento a ISO/IEC 9899:1990 come C90.
ISO/IEC 14882:1998, Information Technology - Linguaggi di programmazione - C++. Questa specifica dell'API OpenMP fa riferimento a ISO/IEC 14882:1998 come C++.
Dove questa specifica dell'API OpenMP fa riferimento a C, viene fatto riferimento al linguaggio di base supportato dall'implementazione.