2. Direktivy
Direktivy jsou založené na #pragma
direktivách definovaných v standardech C a C++. Kompilátory, které podporují rozhraní OPENMP C a C++ API, budou obsahovat možnost příkazového řádku, která aktivuje a umožňuje interpretaci všech direktiv kompilátoru OpenMP.
2.1 Formát direktivy
Syntaxe direktivy OpenMP je formálně určena gramatikou v dodatku C a neformálním způsobem:
#pragma omp directive-name [clause[ [,] clause]...] new-line
Každá direktiva začíná s direktivou #pragma omp
pragma jiné direktivy pragma (bez openMP nebo rozšíření dodavatelů) se stejnými názvy. Zbytek direktivy se řídí konvencí standardů jazyka C a C++ pro direktivy kompilátoru. Konkrétně lze použít prázdné znaky před a za znakem #
, a někdy musí být prázdné znaky použity k oddělení slov v direktivě. Předzpracování tokenů, které následují, #pragma omp
podléhají nahrazení maker.
Direktivy rozlišují malá a velká písmena. Pořadí, ve kterém se klauzule zobrazují ve direktivách, není významné. Doložky o direktivách mohou být podle potřeby opakované, s výhradou omezení uvedených v popisu každé klauzule. Pokud se seznam proměnných zobrazí v klauzuli, musí zadat pouze proměnné. Pro každou direktivu lze zadat pouze jeden název direktivy. Například následující direktiva není povolená:
/* ERROR - multiple directive names not allowed */
#pragma omp parallel barrier
Direktiva OpenMP se vztahuje na maximálně jeden úspěšný příkaz, což musí být strukturovaný blok.
2.2 Podmíněná kompilace
Název _OPENMP
makra je definován implementacemi kompatibilními s OpenMP jako desetinná konstanta yyyymm, což bude rok a měsíc schválené specifikace. Toto makro nesmí být předmětem direktivy předběžného #define
#undef
zpracování ani předzpracování.
#ifdef _OPENMP
iam = omp_get_thread_num() + index;
#endif
Pokud dodavatelé definují rozšíření openMP, můžou zadat další předdefinovaná makra.
2.3 paralelní konstrukce
Následující direktiva definuje paralelní oblast, což je oblast programu, která se má paralelně spouštět mnoha vlákny. Tato direktiva je základním konstruktorem, který spouští paralelní spouštění.
#pragma omp parallel [clause[ [, ]clause] ...] new-line structured-block
Klauzule je jednou z následujících možností:
if(
skalární výraz)
private(
seznam proměnných)
firstprivate(
seznam proměnných)
default(shared | none)
shared(
seznam proměnných)
copyin(
seznam proměnných)
reduction(
operátor:
seznam proměnných)
num_threads(
integer-expression)
Když se vlákno dostane do paralelního konstruktoru, vytvoří se tým vláken, pokud platí jeden z následujících případů:
- Není k dispozici žádná
if
klauzule. - Výraz
if
se vyhodnotí jako nenulová hodnota.
Toto vlákno se stane hlavním vláknem týmu, s číslem vlákna 0 a všemi vlákny v týmu, včetně hlavního vlákna, provede oblast paralelně. Pokud je hodnota výrazu if
nula, oblast je serializována.
Chcete-li určit počet požadovaných vláken, budou následující pravidla považována za v daném pořadí. Použije se první pravidlo, jehož podmínka je splněna:
num_threads
Pokud klauzule existuje, pak hodnota celočíselného výrazu je počet požadovaných vláken.omp_set_num_threads
Pokud byla volána funkce knihovny, pak hodnota argumentu v naposledy provedeném volání je počet požadovaných vláken.Pokud je proměnná
OMP_NUM_THREADS
prostředí definovaná, hodnota této proměnné prostředí je počet požadovaných vláken.Pokud se nepoužívá žádná z výše uvedených metod, je definován počet vláken požadovaných implementací.
num_threads
Pokud je klauzule přítomná, nahrazuje počet vláken požadovaných omp_set_num_threads
funkcí knihovny nebo OMP_NUM_THREADS
proměnnou prostředí pouze pro paralelní oblast, na kterou se vztahuje. Na pozdější paralelní oblasti to nemá vliv.
Počet vláken, která spouští paralelní oblast, závisí také na tom, zda je povolena dynamická úprava počtu vláken. Pokud je dynamická úprava zakázaná, spustí se požadovaný počet vláken paralelní oblasti. Pokud je povolena dynamická úprava, požadovaný počet vláken je maximální počet vláken, která mohou spouštět paralelní oblast.
Pokud je zjištěna paralelní oblast, zatímco dynamické úpravy počtu vláken je zakázané a počet vláken požadovaných pro paralelní oblast je větší než počet, které může systém za běhu poskytnout, chování programu je definováno implementací. Implementace může například přerušit provádění programu nebo může serializovat paralelní oblast.
Funkci omp_set_dynamic
knihovny a proměnnou OMP_DYNAMIC
prostředí lze použít k povolení a zakázání dynamické úpravy počtu vláken.
Počet fyzických procesorů, které v daném okamžiku hostují vlákna, je definován implementací. Po vytvoření zůstane počet vláken v týmu konstantní po dobu trvání této paralelní oblasti. Uživatel ho může změnit buď explicitně, nebo automaticky systémem za běhu z jedné paralelní oblasti na jinou.
Příkazy obsažené v dynamickém rozsahu paralelní oblasti se provádějí jednotlivými vlákny a každé vlákno může spouštět cestu příkazů, které se liší od ostatních vláken. Direktivy, ke kterým došlo mimo lexikální rozsah paralelní oblasti, se označují jako osamocené direktivy.
Na konci paralelní oblasti je implicitní bariéra. Na konci paralelní oblasti pokračuje pouze hlavní vlákno týmu.
Pokud vlákno v týmu, který spouští paralelní oblast, narazí na jiný paralelní konstruktor, vytvoří nový tým a stane se hlavním týmem tohoto nového týmu. Ve výchozím nastavení jsou vnořené paralelní oblasti serializovány. Ve výchozím nastavení je ve výchozím nastavení spuštěná vnořená paralelní oblast týmem složeným z jednoho vlákna. Výchozí chování může být změněno pomocí funkce omp_set_nested
knihovny modulu runtime nebo proměnné OMP_NESTED
prostředí . Počet vláken v týmu, který spouští vnořenou paralelní oblast, je však definovaný implementací.
Omezení direktivy parallel
jsou následující:
Ve většině případů může být v direktivě uvedena jedna
if
klauzule.Není zadáno, zda dojde k nějakým vedlejším efektům uvnitř výrazu if nebo
num_threads
výrazu.Spuštění
throw
uvnitř paralelní oblasti musí způsobit obnovení v dynamickém rozsahu stejného strukturovaného bloku a musí být zachyceno stejným vláknem, které vyvolalo výjimku.V direktivě se může objevit pouze jedna
num_threads
klauzule. Výraznum_threads
se vyhodnotí mimo kontext paralelní oblasti a musí se vyhodnotit na kladnou celočíselnou hodnotu.Pořadí vyhodnocení
if
klauzulí anum_threads
klauzulí není zadané.
Křížové odkazy
private
,firstprivate
, ,shared
default
, ,copyin
areduction
klauzule (oddíl 2.7.2)- OMP_NUM_THREADS proměnná prostředí
- funkce knihovny omp_set_dynamic
- OMP_DYNAMIC proměnná prostředí
- omp_set_nested
- OMP_NESTED proměnná prostředí
- funkce knihovny omp_set_num_threads
2.4 Konstrukce sdílení práce
Konstruktor sdílení práce distribuuje provádění přidruženého příkazu mezi členy týmu, kteří na něj narazí. Direktivy sdílení práce nespouštějí nová vlákna a neexistuje žádná předpokládaná překážka pro vstup do konstruktoru sdílení práce.
Posloupnost konstruktorů a barrier
direktiv sdílení práce musí být stejná pro každé vlákno v týmu.
OpenMP definuje následující konstrukce sdílení práce a tyto konstrukce jsou popsány v následujících částech:
- for – direktiva
- sections – direktiva
- jednoduchá direktiva
2.4.1 pro konstruktor
Direktiva for
identifikuje iterativní konstruktor sdílení práce, který určuje, že iterace přidružené smyčky se spustí paralelně. Iterace smyčky se distribuují for
mezi vlákna, která již existují v týmu, který spouští paralelní konstruktor, se kterým je svázán. Syntaxe konstruktoru for
je následující:
#pragma omp for [clause[[,] clause] ... ] new-line for-loop
Klauzule je jednou z následujících možností:
private(
seznam proměnných)
firstprivate(
seznam proměnných)
lastprivate(
seznam proměnných)
reduction(
operátor:
seznam proměnných)
ordered
schedule(
druh [,
chunk_size])
nowait
Direktiva for
omezuje strukturu odpovídající for
smyčky. Konkrétně musí mít odpovídající for
smyčka kanonický tvar:
for (
init-expr ;
var logical-op b ;
incr-expr )
init-expr
Jeden z následujících:
- var = lb
- integer-type var = lb
incr-expr
Jeden z následujících:
++
Var- Var
++
--
Var- Var
--
- var
+=
incr - var
-=
incr - var
=
var+
incr - var
=
incr+
var - var
=
var-
incr
var
Podepsaná celočíselná proměnná. Pokud by tato proměnná jinak byla sdílena, je implicitně soukromá po dobu trvání for
. Tuto proměnnou v těle for
příkazu neupravujte. Pokud není zadaná lastprivate
proměnná, její hodnota po neurčité smyčce.
logical-op
Jeden z následujících:
<
<=
>
>=
lb, b a incr
Invariantní celočíselné výrazy smyčky Během vyhodnocování těchto výrazů není žádná synchronizace, takže všechny vyhodnocené vedlejší účinky produkují nedeterminované výsledky.
Kanonický formulář umožňuje vypočítat počet iterací smyčky při vstupu do smyčky. Tento výpočet se provádí s hodnotami v typu var po celočíselné povýšení. Konkrétně pokud hodnota blb +
-
incr nemůže být reprezentována v daném typu, je výsledek neurčitý. Dále platí, že pokud je <
logický provoz nebo <=
, musí incr-expr způsobit, aby se při každé iteraci smyčky zvýšily hodnoty var. Pokud je >
logický provoz nebo >=
, musí incr-expr způsobit, že se při každé iteraci smyčky zmenší var.
Klauzule schedule
určuje, jak jsou iterace smyčky for
rozděleny mezi vlákna týmu. Správnost programu nesmí záviset na tom, které vlákno provádí konkrétní iteraci. Hodnota chunk_size, pokud je zadána, musí být invariantní celočíselná hodnota výrazu smyčky s kladnou hodnotou. Během vyhodnocování tohoto výrazu není žádná synchronizace, takže žádné vyhodnocené vedlejší účinky produkují nedeterminované výsledky. Druh plánu může být jedna z následujících hodnot:
Tabulka 2–1: schedule
hodnoty typu klauzule
Hodnota | Popis |
---|---|
static | Při schedule(static, zadání chunk_size ) jsou iterace rozděleny do bloků velikosti určené chunk_size. Bloky dat jsou staticky přiřazeny vláknům v týmu v pořadí čísla vlákna. Pokud není zadán žádný chunk_size , iterační prostor je rozdělen do bloků, které jsou přibližně stejné velikosti, s jedním blokem dat přiřazeným ke každému vláknu. |
dynamic | Při schedule(dynamic, zadání chunk_size ) jsou iterace rozděleny do řady bloků dat, z nichž každý obsahuje chunk_size iterace. Každý blok dat je přiřazen k vláknu, které čeká na přiřazení. Vlákno provede blok iterací a potom počká na jeho další přiřazení, dokud nebudou přiřazeny žádné bloky dat. Poslední blok dat, který se má přiřadit, může mít menší počet iterací. Pokud není zadán žádný chunk_size , použije se výchozí hodnota 1. |
řízený | Pokud schedule(guided, je zadán chunk_size) , iterace se přiřazují podprocesům v blocích se zmenšenými velikostmi. Když vlákno dokončí přiřazený blok iterací, dynamicky se mu přiřadí další blok dat, dokud nezůstane žádný. Pro chunk_size 1 je velikost každého bloku přibližně počet nepřiřazených iterací dělených počtem vláken. Tyto velikosti se zmenšují téměř exponenciálně na 1. U chunk_size s hodnotou k větší než 1 se velikost téměř exponenciálně sníží na k, s tím rozdílem, že poslední blok dat může mít méně než k iterace. Pokud není zadán žádný chunk_size , použije se výchozí hodnota 1. |
modul runtime | Po schedule(runtime) zadání se rozhodnutí týkající se plánování odloží do doby běhu. Typ plánu a velikost bloků dat lze vybrat za běhu nastavením proměnné OMP_SCHEDULE prostředí . Pokud tato proměnná prostředí není nastavená, je výsledný plán definovaný implementací. Pokud schedule(runtime) je zadán, nesmí být zadán chunk_size . |
Pokud neexistuje explicitně definovaná schedule
klauzule, výchozí schedule
hodnota je definována implementací.
Program kompatibilní s OpenMP by neměl spoléhat na konkrétní plán správného provádění. Program by neměl spoléhat na druh plánu, který přesně odpovídá popisu uvedenému výše, protože je možné mít variace v implementacích stejného typu plánu v různých kompilátorech. Popisy lze použít k výběru plánu, který je vhodný pro konkrétní situaci.
Klauzule ordered
musí být přítomna při ordered
direktivách svázání s konstruktorem for
.
Na konci for
konstruktoru je implicitní bariéra, pokud nowait
není zadána klauzule.
Omezení direktivy for
jsou následující:
Smyčka
for
musí být strukturovaný blok a kromě toho nesmí být jeho spuštění ukončeno příkazembreak
.Hodnoty řídicích výrazů smyčky smyčky
for
přidruženéfor
k direktivě musí být stejné pro všechna vlákna v týmu.Proměnná
for
iterace smyčky musí mít typ signed integer.V direktivě
for
se může objevit pouze jednaschedule
klauzule.V direktivě
for
se může objevit pouze jednaordered
klauzule.V direktivě
for
se může objevit pouze jednanowait
klauzule.Není zadáno, jestli nebo jak často dochází k vedlejším účinkům v rámci výrazů chunk_size, lb, b nebo incr .
Hodnota výrazu chunk_size musí být stejná pro všechna vlákna v týmu.
Křížové odkazy
private
,firstprivate
,lastprivate
areduction
klauzule (oddíl 2.7.2)- OMP_SCHEDULE proměnná prostředí
- uspořádaný konstruktor
- schedule – klauzule
2.4.2 sections – konstrukce
Direktiva sections
identifikuje neiterativní konstruktor sdílení práce, který určuje sadu konstruktorů, které mají být rozděleny mezi vlákna v týmu. Každý oddíl se spustí jednou vláknem v týmu. Syntaxe direktivy sections
je následující:
#pragma omp sections [clause[[,] clause] ...] new-line
{
[#pragma omp section new-line]
structured-block
[#pragma omp section new-linestructured-block ]
...
}
Klauzule je jednou z následujících možností:
private(
seznam proměnných)
firstprivate(
seznam proměnných)
lastprivate(
seznam proměnných)
reduction(
operátor:
seznam proměnných)
nowait
Každému section
oddílu předchází direktiva, i když section
je direktiva pro první oddíl nepovinná. Směrnice section
musí být uvedeny v lexikálním rozsahu sections
směrnice. Na konci konstruktoru sections
je implicitní bariéra, pokud nowait
není zadána.
Omezení direktivy sections
jsou následující:
Směrnice
section
nesmí být uvedena mimo lexikální rozsahsections
směrnice.V direktivě
sections
se může objevit pouze jednanowait
klauzule.
Křížové odkazy
private
,firstprivate
,lastprivate
areduction
klauzule (oddíl 2.7.2)
2.4.3 single construct
Direktiva single
identifikuje konstruktor, který určuje, že přidružený strukturovaný blok je spuštěn pouze jedním vláknem v týmu (ne nutně hlavním vláknem). Syntaxe direktivy single
je následující:
#pragma omp single [clause[[,] clause] ...] new-linestructured-block
Klauzule je jednou z následujících možností:
private(
seznam proměnných)
firstprivate(
seznam proměnných)
copyprivate(
seznam proměnných)
nowait
Pokud není zadaná klauzule, je za single
konstruktorem nowait
implicitní bariéra.
Omezení direktivy single
jsou následující:
- V direktivě
single
se může objevit pouze jednanowait
klauzule. - Klauzule
copyprivate
nesmí být použita s klauzulínowait
.
Křížové odkazy
private
,firstprivate
acopyprivate
klauzule (oddíl 2.7.2)
2.5 Kombinované konstrukce paralelního sdílení práce
Kombinované konstrukce paralelního sdílení práce jsou zkratky pro zadání paralelní oblasti, která má pouze jeden konstruktor sdílení práce. Sémantika těchto direktiv je stejná jako explicitní parallel
určení direktivy následované jedinou konstruktorem sdílení práce.
Následující části popisují kombinované konstrukce paralelního sdílení práce:
- paralelně pro direktivu
- parallel sections – direktiva
2.5.1 parallel for construct
Direktiva parallel for
je zkratka parallel
pro oblast, která obsahuje pouze jednu for
direktivu. Syntaxe direktivy parallel for
je následující:
#pragma omp parallel for [clause[[,] clause] ...] new-linefor-loop
Tato směrnice umožňuje všechny klauzule směrnice a for
směrnice s výjimkou nowait
klauzule s identickými významy parallel
a omezeními. Sémantika je stejná jako explicitní určení parallel
direktivy bezprostředně následované direktivou for
.
Křížové odkazy
- parallel – direktiva
- for – direktiva
- Klauzule atributů dat
2.5.2 – konstruktor paralelních oddílů
Direktiva parallel sections
poskytuje zástupce pro určení parallel
oblasti, která má pouze jednu sections
direktivu. Sémantika je stejná jako explicitní určení parallel
direktivy bezprostředně následované direktivou sections
. Syntaxe direktivy parallel sections
je následující:
#pragma omp parallel sections [clause[[,] clause] ...] new-line
{
[#pragma omp section new-line]
structured-block
[#pragma omp section new-linestructured-block ]
...
}
Klauzule může být jednou z klauzulí přijatých direktivami parallel
a sections
direktivami, s výjimkou klauzulenowait
.
Křížové odkazy
- parallel – direktiva
- sections – direktiva
2.6 Hlavní direktivy a direktivy synchronizace
Následující části popisují:
- hlavní konstruktor
- kritická konstrukce
- direktiva bariéry
- atomic construct
- flush – direktiva
- uspořádaný konstruktor
Hlavní konstruktor 2.6.1
Direktiva master
identifikuje konstruktor, který určuje strukturovaný blok spuštěný hlavním vláknem týmu. Syntaxe direktivy master
je následující:
#pragma omp master new-linestructured-block
Jiná vlákna v týmu nespouštějí přidružený strukturovaný blok. Neexistuje žádná implicitní bariéra při vstupu do hlavní konstrukce nebo z ní odejít.
2.6.2 kritický konstruktor
Direktiva critical
identifikuje konstruktor, který omezuje provádění přidruženého strukturovaného bloku na jedno vlákno najednou. Syntaxe direktivy critical
je následující:
#pragma omp critical [(name)] new-linestructured-block
K identifikaci kritické oblasti je možné použít volitelný název . Identifikátory používané k identifikaci kritické oblasti mají externí propojení a jsou v názvovém prostoru odděleném od názvových prostorů používaných popisky, značkami, členy a běžnými identifikátory.
Vlákno čeká na začátku kritické oblasti, dokud žádné jiné vlákno nespouštějí kritickou oblast (kdekoli v programu) se stejným názvem. Všechny nepojmenované critical
direktivy se mapuje na stejný nezadaný název.
2.6.3 direktiva bariéry
Direktiva barrier
synchronizuje všechna vlákna v týmu. Když narazíte, každé vlákno v týmu počká, dokud k tomuto bodu nedosáhli všichni ostatní. Syntaxe direktivy barrier
je následující:
#pragma omp barrier new-line
Jakmile všechny vlákna v týmu narazí na bariéru, každé vlákno v týmu začne provádět příkazy po direktivě bariéry paralelně. Vzhledem k tomu, že direktiva barrier
nemá v rámci své syntaxe příkaz jazyka C, existují určitá omezení pro umístění v rámci programu. Další informace o formální gramatikě najdete v dodatku C. Následující příklad ukazuje tato omezení.
/* ERROR - The barrier directive cannot be the immediate
* substatement of an if statement
*/
if (x!=0)
#pragma omp barrier
...
/* OK - The barrier directive is enclosed in a
* compound statement.
*/
if (x!=0) {
#pragma omp barrier
}
2.6.4 atomic konstruktor
Direktiva atomic
zajišťuje, aby se určité umístění paměti aktualizovalo atomicky, nikoli aby se zpřístupňuje možnosti více souběžných vláken zápisu. Syntaxe direktivy atomic
je následující:
#pragma omp atomic new-lineexpression-stmt
Příkaz výrazu musí mít jeden z následujících formulářů:
- x binop
=
expr - x
++
++
x- x
--
--
x
V předchozích výrazech:
x je výraz lvalue se skalárním typem.
výraz je výraz se skalárním typem a neodkazuje na objekt určený x.
Binop není přetížený operátor a je jedním z
+
,*
,-
, ,/
,&
,^
, ,|
, ,<<
nebo>>
.
Přestože je definována implementací, zda implementace nahrazuje všechny atomic
direktivy direktivami critical
, které mají stejný jedinečný název, atomic
direktiva umožňuje lepší optimalizaci. Často jsou k dispozici hardwarové instrukce, které můžou provádět atomické aktualizace s nejnižší režií.
Pouze zatížení a úložiště objektu určeného x jsou atomické; vyhodnocení výrazu není atomické. Aby se zabránilo podmínkám časování, měly by být všechny aktualizace místa paralelně chráněny direktivou atomic
, s výjimkou těch, o kterých je známo, že nejsou splněny podmínky časování.
Omezení direktivy atomic
jsou následující:
- Všechny atomické odkazy na umístění úložiště x v rámci programu musí mít kompatibilní typ.
Příklady
extern float a[], *p = a, b;
/* Protect against races among multiple updates. */
#pragma omp atomic
a[index[i]] += b;
/* Protect against races with updates through a. */
#pragma omp atomic
p[i] -= 1.0f;
extern union {int n; float x;} u;
/* ERROR - References through incompatible types. */
#pragma omp atomic
u.n++;
#pragma omp atomic
u.x -= 1.0f;
2.6.5 flush – direktiva
Direktiva flush
, ať už explicitní, nebo implicitní, určuje "křížové vlákno" bod sekvence, ve kterém je implementace nutná k zajištění, aby všechna vlákna v týmu měla konzistentní pohled na určité objekty (uvedené níže) v paměti. To znamená, že předchozí vyhodnocení výrazů, které odkazují na tyto objekty, jsou dokončené a následné vyhodnocení ještě nezačalo. Kompilátory například musí obnovit hodnoty objektů z registrů do paměti a hardware může potřebovat vyprázdnit vyrovnávací paměti zápisu do paměti a znovu načíst hodnoty objektů z paměti.
Syntaxe direktivy flush
je následující:
#pragma omp flush [(variable-list)] new-line
Pokud lze všechny objekty, které vyžadují synchronizaci, označit proměnnými, pak je možné tyto proměnné zadat v volitelném seznamu proměnných. Pokud je ukazatel v seznamu proměnných, je samotný ukazatel vyprázdněný, nikoli objekt, na který ukazatel odkazuje.
Direktiva flush
bez seznamu proměnných synchronizuje všechny sdílené objekty s výjimkou nepřístupných objektů s automatickou dobou trvání úložiště. (Pravděpodobně to bude mít větší režii než flush
seznam proměnných.) Direktiva flush
bez seznamu proměnných se předpokládá pro následující direktivy:
barrier
- Při vstupu do a ukončení z
critical
- Při vstupu do a ukončení z
ordered
- Při vstupu do a ukončení z
parallel
- Při ukončení z
for
- Při ukončení z
sections
- Při ukončení z
single
- Při vstupu do a ukončení z
parallel for
- Při vstupu do a ukončení z
parallel sections
Direktiva není implicitní, pokud nowait
existuje klauzule. Je třeba poznamenat, že direktiva flush
není odvozena pro žádnou z následujících možností:
- Při vstupu do
for
- Při vstupu do nebo ukončení z
master
- Při vstupu do
sections
- Při vstupu do
single
Odkaz, který přistupuje k hodnotě objektu s těkavým kvalifikovaným typem, se chová, jako by existoval direktiva flush
určující tento objekt v předchozím bodě sekvence. Odkaz, který upravuje hodnotu objektu s těkavým kvalifikovaným typem, se chová, jako by existoval direktiva flush
určující tento objekt v následujícím sekvenčním bodu.
Vzhledem k tomu, že direktiva flush
nemá v rámci své syntaxe příkaz jazyka C, existují určitá omezení pro umístění v rámci programu. Další informace o formální gramatikě najdete v dodatku C. Následující příklad ukazuje tato omezení.
/* ERROR - The flush directive cannot be the immediate
* substatement of an if statement.
*/
if (x!=0)
#pragma omp flush (x)
...
/* OK - The flush directive is enclosed in a
* compound statement
*/
if (x!=0) {
#pragma omp flush (x)
}
Omezení direktivy flush
jsou následující:
- Proměnná zadaná v direktivě
flush
nesmí obsahovat odkazový typ.
2.6.6 uspořádaný konstruktor
Strukturovaný blok po direktivě ordered
se provede v pořadí, v jakém se iterace spustí v sekvenční smyčce. Syntaxe direktivy ordered
je následující:
#pragma omp ordered new-linestructured-block
Direktiva ordered
musí být v dynamickém rozsahu nebo parallel for
konstruktorufor
. Nebo direktivafor
, na kterou ordered
se konstrukce sváže, musí mít ordered
klauzuli uvedenou v bodě 2.4.1.parallel for
Při provádění for
nebo parallel for
konstruktoru s ordered
klauzulí se konstruktory provádějí výhradně v pořadí, ordered
v jakém by se spouštěly v postupném spuštění smyčky.
Omezení direktivy ordered
jsou následující:
- Iterace smyčky s konstruktorem
for
nesmí spustit stejnou uspořádanou direktivu více než jednou a nesmí spustit více než jednuordered
direktivu.
2.7 Datové prostředí
Tato část představuje direktivu a několik klauzulí pro řízení datového prostředí během provádění paralelních oblastí následujícím způsobem:
Direktiva threadprivate je poskytována tak, aby byly proměnné oboru názvů, oboru názvů nebo statické proměnné oboru bloku místní pro vlákno.
Klauzule, které mohou být zadány ve direktivách pro řízení atributů sdílení proměnných po dobu trvání paralelních nebo pracovních konstruktorů, jsou popsány v části 2.7.2.
2.7.1 threadprivate – direktiva
Direktiva threadprivate
zpřístupňuje pojmenované obory souborů, obor názvů nebo statické proměnné oboru bloku zadané v seznamu proměnných jako soukromé vlákno. variable-list je čárkami oddělený seznam proměnných, které nemají neúplný typ. Syntaxe direktivy threadprivate
je následující:
#pragma omp threadprivate(variable-list) new-line
Každá kopie threadprivate
proměnné se inicializuje jednou, v nespecifikovaném bodu v programu před prvním odkazem na tuto kopii a obvyklým způsobem (tj. hlavní kopie by byla inicializována při sériovém spuštění programu). Všimněte si, že pokud je objekt odkazován v explicitním inicializátoru threadprivate
proměnné a hodnota objektu je změněna před prvním odkazem na kopii proměnné, pak chování není určeno.
Stejně jako u jakékoli privátní proměnné nesmí vlákno odkazovat na kopii objektu threadprivate
jiného vlákna. Během sériových oblastí a hlavních oblastí programu budou odkazy na kopii hlavního vlákna objektu.
Po spuštění první paralelní oblasti je zaručeno, že data v threadprivate
objektech zůstanou zachována pouze v případě, že je mechanismus dynamických vláken zakázán a pokud počet vláken zůstane beze změny pro všechny paralelní oblasti.
Omezení threadprivate
této směrnice jsou následující:
Direktiva
threadprivate
pro proměnné oboru názvů nebo oboru názvů musí být uvedena mimo jakoukoli definici nebo deklaraci a musí lexicky předcházet všem odkazům na kteroukoli z proměnných v seznamu.Každá proměnná v seznamu proměnných
threadprivate
direktivy v oboru souboru nebo oboru názvů musí odkazovat na deklaraci proměnné v oboru souboru nebo oboru názvů, který lexicky předchází direktivě.Direktiva
threadprivate
pro statické proměnné oboru bloku musí být uvedena v oboru proměnné, a ne vnořeném oboru. Direktiva musí lexicky předcházet všem odkazům na kteroukoli z proměnných v seznamu.Každá proměnná v seznamu proměnných
threadprivate
direktivy v oboru bloku musí odkazovat na deklaraci proměnné ve stejném oboru, který lexicky předchází direktivě. Deklarace proměnné musí používat specifikátor statické třídy úložiště.Pokud je proměnná zadaná v direktivě
threadprivate
v jedné jednotce překladu, musí být zadána v direktivěthreadprivate
v každé jednotce překladu, ve které je deklarována.Proměnná
threadprivate
nesmí být uvedena v žádné klauzuli kromě klauzulecopyin
,copyprivate
,schedule
,num_threads
ani klauzuleif
.Adresa
threadprivate
proměnné není konstanta adresy.Proměnná
threadprivate
nesmí obsahovat neúplný typ ani odkazový typ.Proměnná
threadprivate
s typem třídy non-POD musí mít přístupný, jednoznačný konstruktor kopírování, pokud je deklarován explicitním inicializátorem.
Následující příklad ukazuje, jak úprava proměnné, která se zobrazí v inicializátoru, může způsobit nezadané chování a také jak se vyhnout tomuto problému pomocí pomocného objektu a konstruktoru copy.
int x = 1;
T a(x);
const T b_aux(x); /* Capture value of x = 1 */
T b(b_aux);
#pragma omp threadprivate(a, b)
void f(int n) {
x++;
#pragma omp parallel for
/* In each thread:
* Object a is constructed from x (with value 1 or 2?)
* Object b is copy-constructed from b_aux
*/
for (int i=0; i<n; i++) {
g(a, b); /* Value of a is unspecified. */
}
}
Křížové odkazy
- dynamická vlákna
- OMP_DYNAMIC proměnná prostředí
2.7.2 Klauzule atributů sdílení dat
Několik direktiv přijímá klauzule, které uživateli umožňují řídit atributy sdílení proměnných po dobu trvání oblasti. Klauzule atributu sdílení se vztahuje pouze na proměnné v lexikálním rozsahu direktivy, na které se klauzule zobrazuje. Ne všechny následující klauzule jsou povoleny pro všechny direktivy. Seznam klauzulí, které jsou platné pro konkrétní direktivu, jsou popsány v této směrnici.
Pokud je proměnná viditelná při zjištěných paralelních konstruktorech nebo konstruktoru sdílení práce a proměnná není zadaná v klauzuli atributu sdílení nebo threadprivate
direktivě, je proměnná sdílena. Sdílené jsou statické proměnné deklarované v dynamickém rozsahu paralelní oblasti. Sdílená je paměť přidělená haldou (například použití malloc()
v jazyce C nebo C++ nebo new
operátoru v jazyce C++). (Ukazatel na tuto paměť však může být buď soukromý, nebo sdílený.) Proměnné s automatickou dobou trvání úložiště deklarovanou v dynamickém rozsahu paralelní oblasti jsou soukromé.
Většina klauzulí přijímá argument seznamu proměnných , což je čárkami oddělený seznam proměnných, které jsou viditelné. Pokud má proměnná odkazovaná v klauzuli atributu sdílení dat typ odvozený ze šablony a v programu neexistují žádné další odkazy na tuto proměnnou, chování není definováno.
Všechny proměnné, které se zobrazují v klauzulích direktiv, musí být viditelné. Klauzule se můžou podle potřeby opakovat, ale ve více než jedné klauzuli nelze zadat žádnou proměnnou s tím rozdílem, že proměnnou lze zadat v firstprivate
klauzuli i lastprivate
v klauzuli.
Následující části popisují klauzule atributů sdílení dat:
2.7.2.1 private
Klauzule private
deklaruje proměnné v seznamu proměnných jako soukromé pro každé vlákno v týmu. Syntaxe klauzule private
je následující:
private(variable-list)
Chování proměnné zadané v private
klauzuli je následující. Pro konstruktor je přidělen nový objekt s automatickou dobou trvání úložiště. Velikost a zarovnání nového objektu jsou určeny typem proměnné. K tomuto přidělení dochází jednou pro každé vlákno v týmu a výchozí konstruktor je vyvolán pro objekt třídy v případě potřeby; jinak je počáteční hodnota neurčitá. Původní objekt odkazovaný proměnnou má neurčitou hodnotu při vstupu do konstruktoru, nesmí být změněn v dynamickém rozsahu konstruktoru a má nedeterminovanou hodnotu po ukončení konstruktoru.
V lexikálním rozsahu konstruktoru direktivy proměnná odkazuje na nový privátní objekt přidělený vláknem.
Omezení private
klauzule jsou následující:
Proměnná s typem třídy, který je zadaný v
private
klauzuli, musí mít přístupný, jednoznačný výchozí konstruktor.Proměnná zadaná v
private
klauzuli nesmí mítconst
-kvalifikovaný typ, pokud nemá typ třídy smutable
členem.Proměnná zadaná v
private
klauzuli nesmí mít neúplný typ ani odkazový typ.Proměnné, které se zobrazí v
reduction
klauzuli direktivyparallel
direktivy, nelze zadat vprivate
klauzuli na direktivě sdílení práce, která je vázána na paralelní konstruktor.
2.7.2.2 firstprivate
Klauzule firstprivate
poskytuje nadmnožinu funkcí poskytovaných private
klauzulí. Syntaxe klauzule firstprivate
je následující:
firstprivate(variable-list)
Proměnné zadané v seznamu proměnných mají private
sémantiku klauzule, jak je popsáno v oddílu 2.7.2.1. Inicializace nebo konstrukce se stane, jako kdyby byla provedena jednou na vlákno před spuštěním vlákna konstruktoru. firstprivate
Pro klauzuli paralelně konstruktoru je počáteční hodnota nového privátního objektu hodnota původního objektu, který existuje bezprostředně před paralelní konstruktor pro vlákno, které na něj narazí. firstprivate
Pro klauzuli u konstruktoru sdílení práce je počáteční hodnota nového privátního objektu pro každé vlákno, které spouští konstruktor sdílení práce, hodnotu původního objektu, který existuje před bodem v čase, že stejné vlákno narazí na konstruktor sdílení práce. Kromě toho pro objekty C++ je nový privátní objekt pro každé vlákno vytvořen z původního objektu.
Omezení firstprivate
klauzule jsou následující:
Proměnná zadaná v
firstprivate
klauzuli nesmí mít neúplný typ ani odkazový typ.Proměnná s typem třídy, který je určen jako
firstprivate
musí mít přístupný, jednoznačný konstruktor kopírování.Proměnné, které jsou soukromé v rámci paralelní oblasti nebo které se zobrazují v
reduction
klauzuli direktivy, nelze v klauzuliparallel
zadat vfirstprivate
klauzuli direktivy pro sdílení práce, která je vázána na paralelní konstruktor.
2.7.2.3 lastprivate
Klauzule lastprivate
poskytuje nadmnožinu funkcí poskytovaných private
klauzulí. Syntaxe klauzule lastprivate
je následující:
lastprivate(variable-list)
Proměnné zadané v seznamu proměnných mají private
sémantiku klauzule. lastprivate
Když se v direktivě objeví klauzule, která identifikuje konstruktor sdílení práce, přiřadí se k původnímu objektu proměnné hodnota každé lastprivate
proměnné z postupně poslední iterace přidružené smyčky nebo lexikální poslední direktivy oddílu. Proměnné, které nejsou přiřazeny hodnotu poslední iterací for
nebo parallel for
, nebo lexicky poslední částí direktivy sections
nebo parallel sections
direktivy, mají neurčité hodnoty za konstruktorem. Nepřiřazené podobjekty mají za konstruktorem také nedeterminovanou hodnotu.
Omezení lastprivate
klauzule jsou následující:
Všechna omezení pro
private
použití.Proměnná s typem třídy, která je určena tak, aby
lastprivate
měla přístupný, jednoznačný operátor přiřazení kopírování.Proměnné, které jsou soukromé v rámci paralelní oblasti nebo které se zobrazují v
reduction
klauzuli direktivy, nelze v klauzuliparallel
zadat vlastprivate
klauzuli direktivy pro sdílení práce, která je vázána na paralelní konstruktor.
2.7.2.4 shared
Tato klauzule sdílí proměnné, které se zobrazují v seznamu proměnných mezi všemi vlákny v týmu. Všechna vlákna v rámci týmu přistupují ke stejné oblasti úložiště pro shared
proměnné.
Syntaxe klauzule shared
je následující:
shared(variable-list)
2.7.2.5 default
Klauzule default
umožňuje uživateli ovlivnit atributy sdílení dat proměnných. Syntaxe klauzule default
je následující:
default(shared | none)
Určení default(shared)
je ekvivalentní explicitní výpis každé aktuálně viditelné proměnné v shared
klauzuli, pokud není threadprivate
nebo const
-kvalifikovaný. Bez explicitní default
klauzule je výchozí chování stejné, jako kdyby default(shared)
bylo zadáno.
Určení default(none)
vyžaduje, aby pro každý odkaz na proměnnou v lexikálním rozsahu paralelního konstruktoru byla splněna alespoň jedna z následujících podmínek:
Proměnná je explicitně uvedena v klauzuli atributu sdílení dat konstruktoru, který obsahuje odkaz.
Proměnná je deklarována v rámci paralelní konstrukce.
Proměnná je
threadprivate
.Proměnná má
const
-kvalifikovaný typ.Proměnná je řídicí proměnná smyčky pro smyčku
for
, která bezprostředně následuje za direktivoufor
neboparallel for
direktivou, a odkaz na proměnnou se zobrazí uvnitř smyčky.
Určení proměnné v objektu firstprivate
, lastprivate
nebo reduction
klauzuli uzavřené direktivy způsobí implicitní odkaz na proměnnou v uzavřeném kontextu. Na tyto implicitní odkazy se vztahují také výše uvedené požadavky.
Pro direktivu parallel
lze zadat pouze jednu default
klauzuli.
Výchozí atribut sdílení dat proměnné lze přepsat pomocí private
klauzulí , firstprivate
, lastprivate
, reduction
a shared
, jak je znázorněno v následujícím příkladu:
#pragma omp parallel for default(shared) firstprivate(i)\
private(x) private(r) lastprivate(i)
2.7.2.6 reduction
Tato klauzule provádí redukci skalárních proměnných, které se zobrazují v seznamu proměnných s operátorem op. Syntaxe klauzule reduction
je následující:
reduction(
Op :
seznam proměnných )
Redukce je obvykle určena pro příkaz s jedním z následujících formulářů:
- x x
=
op expr - x binop
=
expr - x
=
výraz op x (s výjimkou odčítání) - x
++
++
x- x
--
--
x
kde:
x
Jedna z proměnných redukce zadaných v seznamu
seznam proměnných
Čárkami oddělený seznam skalárních proměnných redukce
výraz
Výraz se skalárním typem, který neodkazuje na x.
Op
Není přetížený operátor, ale jeden z +
, *
, &
-
, ^
, , |
, &&
, nebo ||
.
binop
Není přetíženým operátorem, ale jedním z +
, -
*
, &
, , ^
nebo |
.
Následuje příklad reduction
klauzule:
#pragma omp parallel for reduction(+: a, y) reduction(||: am)
for (i=0; i<n; i++) {
a += b[i];
y = sum(y, c[i]);
am = am || b[i] == c[i];
}
Jak je znázorněno v příkladu, operátor může být skrytý uvnitř volání funkce. Uživatel by měl být opatrní, aby operátor zadaný v reduction
klauzuli odpovídal operaci redukce.
I když správný operand ||
operátoru nemá v tomto příkladu žádné vedlejší účinky, jsou povoleny, ale měly by být použity s opatrností. V tomto kontextu může během paralelního provádění dojít k vedlejšímu efektu, který není zaručen, že během postupného provádění smyčky nedojde. K tomuto rozdílu může dojít, protože pořadí provádění iterací je neurčité.
Operátor slouží k určení počáteční hodnoty všech privátních proměnných používaných kompilátorem pro redukci a k určení operátoru finalizace. Určení operátoru explicitně umožňuje, aby příkaz redukce byl mimo lexikální rozsah konstruktoru. V direktivě lze zadat libovolný počet reduction
klauzulí, ale proměnná se může pro danou direktivu objevit nejvýše v jedné reduction
klauzuli.
Vytvoří se soukromá kopie každé proměnné v seznamu proměnných, jedna pro každé vlákno, jako by private
byla použita klauzule. Privátní kopie se inicializuje podle operátoru (viz následující tabulka).
Na konci oblasti, pro kterou reduction
byla klauzule zadána, se původní objekt aktualizuje tak, aby odrážel výsledek kombinování jeho původní hodnoty s konečnou hodnotou každé soukromé kopie pomocí zadaného operátoru. Operátory redukce jsou všechny asociativní (s výjimkou odčítání) a kompilátor může volně znovu přidružovat výpočet konečné hodnoty. (Částečné výsledky redukce odčítání se přičtou k vytvoření konečné hodnoty.)
Hodnota původního objektu se stane neurčitou, když první vlákno dosáhne klauzule obsahující a zůstane tak, dokud nebude výpočet redukce dokončen. Za normálních okolností bude výpočet dokončen na konci konstrukce; Pokud reduction
se však klauzule používá u konstruktoru, na který nowait
se také používá, zůstane hodnota původního objektu neurčitá, dokud nebude provedena synchronizace bariéry, aby se zajistilo, že všechna vlákna dokončila reduction
klauzuli.
Následující tabulka uvádí operátory, které jsou platné a jejich kanonické inicializační hodnoty. Hodnota skutečné inicializace bude konzistentní s datovým typem proměnné redukce.
Operátor | Inicializace |
---|---|
+ |
0 |
* |
1 |
- |
0 |
& |
~0 |
| |
0 |
^ |
0 |
&& |
1 |
|| |
0 |
Omezení reduction
klauzule jsou následující:
Typ proměnných v
reduction
klauzuli musí být platný pro redukční operátor s tím rozdílem, že typy ukazatelů a odkazové typy nejsou nikdy povoleny.Proměnná zadaná v
reduction
klauzuli nesmí býtconst
kvalifikovaná.Proměnné, které jsou soukromé v rámci paralelní oblasti nebo které se zobrazují v
reduction
klauzuli direktivy, nelze v klauzuliparallel
zadat vreduction
klauzuli direktivy pro sdílení práce, která je vázána na paralelní konstruktor.#pragma omp parallel private(y) { /* ERROR - private variable y cannot be specified in a reduction clause */ #pragma omp for reduction(+: y) for (i=0; i<n; i++) y += b[i]; } /* ERROR - variable x cannot be specified in both a shared and a reduction clause */ #pragma omp parallel for shared(x) reduction(+: x)
2.7.2.7 copyin
Klauzule copyin
poskytuje mechanismus, který přiřadí stejnou hodnotu threadprivate
proměnným pro každé vlákno v týmu, který spouští paralelní oblast. Pro každou proměnnou zadanou copyin
v klauzuli se hodnota proměnné v hlavním vlákně týmu zkopíruje, jako by přiřazením, do privátních kopií vlákna na začátku paralelní oblasti. Syntaxe klauzule copyin
je následující:
copyin(
variable-list
)
Omezení copyin
klauzule jsou následující:
Proměnná zadaná v
copyin
klauzuli musí mít přístupný, jednoznačný operátor přiřazení kopírování.Proměnná zadaná v
copyin
klauzuli musí být proměnnáthreadprivate
.
2.7.2.8 copyprivate
Klauzule copyprivate
poskytuje mechanismus použití privátní proměnné k vysílání hodnoty od jednoho člena týmu ostatním členům. Je to alternativa k použití sdílené proměnné pro hodnotu při poskytování takové sdílené proměnné by byla obtížná (například v rekurzi vyžadující jinou proměnnou na každé úrovni). Klauzule copyprivate
se může objevit pouze v direktivě single
.
Syntaxe klauzule copyprivate
je následující:
copyprivate(
variable-list
)
copyprivate
Účinek klauzule na proměnné v seznamu proměnných nastane po spuštění strukturovaného bloku přidruženého k single
konstruktoru a před tím, než některé z vláken v týmu opustily bariéru na konci konstruktoru. Potom se ve všech ostatních vláknech v týmu pro každou proměnnou v seznamu proměnných tato proměnná definuje (jako by přiřazení) s hodnotou odpovídající proměnné ve vlákně, které spustilo strukturovaný blok konstruktoru.
copyprivate
Omezení klauzule jsou následující:
Proměnná zadaná v
copyprivate
klauzuli nesmí být uvedena vprivate
klauzuli nebofirstprivate
klauzuli pro stejnousingle
direktivu.Pokud je v dynamickém rozsahu paralelní oblasti zjištěna
single
direktiva scopyprivate
klauzulí, musí být všechny proměnné zadané vcopyprivate
klauzuli privátní v nadřazeném kontextu.Proměnná zadaná v
copyprivate
klauzuli musí mít přístupný operátor jednoznačného přiřazení kopírování.
2.8 Vazba direktivy
Dynamické vazby direktiv musí dodržovat následující pravidla:
Direktivy
for
, ,sections
,single
master
abarrier
direktivy jsou vázány na dynamicky uzavřenéparallel
, pokud existuje, bez ohledu na hodnotu jakékoliif
klauzule, která může být přítomna v této směrnici. Pokud se aktuálně nespustí žádná paralelní oblast, direktivy provádí tým složený pouze z hlavního vlákna.Direktiva
ordered
se sváže s dynamicky ohraničujícímifor
.Direktiva
atomic
vynucuje výhradní přístup s ohledem naatomic
direktivy ve všech vláknech, nejen v aktuálním týmu.Direktiva
critical
vynucuje výhradní přístup s ohledem nacritical
direktivy ve všech vláknech, nejen v aktuálním týmu.Direktiva nemůže nikdy svázat s žádnou direktivou mimo nejbližší dynamicky ohraničující
parallel
.
2.9 – vnoření direktiv
Dynamické vnořování direktiv musí dodržovat následující pravidla:
parallel
Direktiva dynamicky uvnitř jinéparallel
logicky vytvoří nový tým, který se skládá pouze z aktuálního vlákna, pokud není povolen vnořený paralelismus.for
,sections
asingle
direktivy, které se sváže se stejnýmiparallel
, nesmí být vnořené do sebe.critical
direktivy se stejným názvem nesmí být vnořené do sebe. Upozorňujeme, že toto omezení nestačí, aby se zabránilo zablokování.for
,sections
asingle
direktivy nejsou povoleny v dynamickém rozsahucritical
ordered
, amaster
oblastí, pokud se direktivy spojují se stejnýmiparallel
oblastmi.barrier
direktivy nejsou povoleny v dynamickém rozsahufor
, ,single
ordered
sections
, ,master
acritical
oblastí, pokud se direktivy spojují se stejnýmiparallel
oblastmi.master
direktivy nejsou povoleny v dynamickém rozsahusections
for
, asingle
direktivy, pokudmaster
se direktivy spojují se stejnýmiparallel
direktivy sdílení práce.ordered
direktivy nejsou povoleny v dynamickém rozsahucritical
oblastí, pokud se direktivy spojují se stejnýmiparallel
oblastmi.Jakákoli direktiva, která je povolená při dynamickém spuštění uvnitř paralelní oblasti, je povolená také při provádění mimo paralelní oblast. Při dynamickém spuštění mimo uživatelem zadanou paralelní oblast se direktiva provede tým složený pouze z hlavního vlákna.