Sdílet prostřednictvím


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 omppragma 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:

  1. num_threads Pokud klauzule existuje, pak hodnota celočíselného výrazu je počet požadovaných vláken.

  2. 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.

  3. 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.

  4. 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_NESTEDprostř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ýraz num_threads se vyhodnotí mimo kontext paralelní oblasti a musí se vyhodnotit na kladnou celočíselnou hodnotu.

  • Pořadí vyhodnocení if klauzulí a num_threads klauzulí není zadané.

Křížové odkazy

  • private, firstprivate, , shareddefault, , copyina reduction 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:

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á lastprivatepromě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_SCHEDULEprostř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říkazem break .

  • 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 jedna schedule klauzule.

  • V direktivě for se může objevit pouze jedna ordered klauzule.

  • V direktivě for se může objevit pouze jedna nowait 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

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í rozsah sections směrnice.

  • V direktivě sections se může objevit pouze jedna nowait klauzule.

Křížové odkazy

  • private, firstprivate, lastprivatea reduction 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 jedna nowait klauzule.
  • Klauzule copyprivate nesmí být použita s klauzulí nowait .

Křížové odkazy

  • private, firstprivatea copyprivate 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:

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

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

2.6 Hlavní direktivy a direktivy synchronizace

Následující části popisují:

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ž jednu ordered 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ě klauzule copyin, copyprivate, schedule, num_threadsani klauzule if .

  • 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

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ít const-kvalifikovaný typ, pokud nemá typ třídy s mutable členem.

  • Proměnná zadaná v private klauzuli nesmí mít neúplný typ ani odkazový typ.

  • Proměnné, které se zobrazí v reduction klauzuli direktivy parallel direktivy, nelze zadat v private 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 klauzuli parallel zadat v firstprivate 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 klauzuli parallel zadat v lastprivate 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 direktivou for nebo parallel for direktivou, a odkaz na proměnnou se zobrazí uvnitř smyčky.

Určení proměnné v objektu firstprivate, lastprivatenebo 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í privateklauzulí , firstprivate, lastprivate, reductiona 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ýt constkvalifikovaná.

  • Proměnné, které jsou soukromé v rámci paralelní oblasti nebo které se zobrazují v reduction klauzuli direktivy, nelze v klauzuli parallel zadat v reduction 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 v private klauzuli nebo firstprivate klauzuli pro stejnou single direktivu.

  • Pokud je v dynamickém rozsahu paralelní oblasti zjištěna single direktiva s copyprivate klauzulí, musí být všechny proměnné zadané v copyprivate 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, singlemastera barrier direktivy jsou vázány na dynamicky uzavřené parallel, pokud existuje, bez ohledu na hodnotu jakékoli if 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ími for.

  • Direktiva atomic vynucuje výhradní přístup s ohledem na atomic direktivy ve všech vláknech, nejen v aktuálním týmu.

  • Direktiva critical vynucuje výhradní přístup s ohledem na critical 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, sectionsa single direktivy, které se sváže se stejnými parallel , 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, sectionsa single direktivy nejsou povoleny v dynamickém rozsahu criticalordered, a master oblastí, pokud se direktivy spojují se stejnými parallel oblastmi.

  • barrierdirektivy nejsou povoleny v dynamickém rozsahu for, , singleorderedsections, , mastera critical oblastí, pokud se direktivy spojují se stejnými parallel oblastmi.

  • masterdirektivy nejsou povoleny v dynamickém rozsahu sectionsfor, a single direktivy, pokud master se direktivy spojují se stejnými parallel direktivy sdílení práce.

  • ordered direktivy nejsou povoleny v dynamickém rozsahu critical oblastí, pokud se direktivy spojují se stejnými parallel 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.