2. 指示詞
指示詞是以 #pragma
C 和 C++ 標準中定義的指示詞為基礎。 支援 OpenMP C 和 C++ API 的編譯器會包含命令列選項,可啟動並允許解譯所有 OpenMP 編譯器指示詞。
2.1 指示詞格式
OpenMP 指示詞的語法是由附錄 C 中的 文法正式指定,並非正式地指定如下:
#pragma omp directive-name [clause[ [,] clause]...] new-line
每個指示詞的開頭 #pragma omp
都是 ,以減少與其他 (非 OpenMP 或 OpenMP 廠商延伸模組) pragma 指示詞具有相同名稱的潛在衝突。 指示詞的其餘部分會遵循編譯器指示詞的 C 和 C++ 標準慣例。 特別是,空白字元可以在 前後 #
使用,有時空白字元必須用來分隔指示詞中的單字。 後續 #pragma omp
的前置處理權杖受限於宏取代。
指示詞會區分大小寫。 子句出現在指示詞中的順序並不重要。 指示詞上的子句可能會視需要重複,但受限於每個子句描述中所列的限制。 如果 variable-list 出現在 子句中,它只能指定變數。 每個指示詞只能指定一個 指示詞名稱 。 例如,不允許下列指示詞:
/* ERROR - multiple directive names not allowed */
#pragma omp parallel barrier
OpenMP 指示詞最多會套用至一個成功的語句,該語句必須是結構化區塊。
2.2 條件式編譯
_OPENMP
宏名稱是由符合 OpenMP 規範的實作所定義,做為 decimal 常數 yyyymm ,這會是核准規格的年份和月份。 這個宏不得是 或 #undef
前置處理指示詞的主 #define
旨。
#ifdef _OPENMP
iam = omp_get_thread_num() + index;
#endif
如果廠商定義 OpenMP 的延伸模組,他們可能會指定其他預先定義的宏。
2.3 平行建構
下列指示詞會定義平列區域,這是程式的區域,由許多執行緒平行執行。 這個指示詞是啟動平行執行的基本建構。
#pragma omp parallel [clause[ [, ]clause] ...] new-line structured-block
子 句 是下列其中一項:
if(
scalar-expression)
private(
variable-list)
firstprivate(
variable-list)
default(shared | none)
shared(
variable-list)
copyin(
variable-list)
reduction(
operator:
variable-list)
num_threads(
integer-expression)
當執行緒進入平行建構時,如果下列其中一個案例成立,就會建立執行緒小組:
- 沒有
if
子句存在。 - 運算式
if
會評估為非零值。
此執行緒會成為小組的主執行緒,執行緒數目為 0,且小組中的所有線程,包括主要執行緒,以平行方式執列區域。 如果運算式的值 if
為零,則會序列化區域。
若要判斷要求的執行緒數目,將會依序考慮下列規則。 符合條件的第一個規則將會套用:
num_threads
如果子句存在,整數運算式的值就是要求的執行緒數目。如果已呼叫連結
omp_set_num_threads
庫函式,則最近執行呼叫中引數的值是要求的執行緒數目。如果已定義環境變數
OMP_NUM_THREADS
,則此環境變數的值就是要求的執行緒數目。如果未使用上述任何方法,則所要求的執行緒數目是實作定義的。
num_threads
如果 子句存在,則會取代程式庫函式所要求的 omp_set_num_threads
執行緒數目,或 OMP_NUM_THREADS
只針對其套用至的平列區域,取代環境變數數目。 稍後的平列區域不會受到其影響。
執行平列區域的執行緒數目也取決於是否啟用執行緒數目的動態調整。 如果停用動態調整,則要求的執行緒數目將會執行平列區域。 如果啟用動態調整,則要求的執行緒數目是可執行平列區域的執行緒數目上限。
如果在停用執行緒數目的動態調整時遇到平列區域,而要求平列區域的執行緒數目大於執行時間系統所能提供的數目,則程式的行為是實作定義的。 例如,實作可能會中斷程式的執行,或可能會序列化平列區域。
連結 omp_set_dynamic
庫函式和 OMP_DYNAMIC
環境變數可用來啟用和停用執行緒數目的動態調整。
任何指定時間實際裝載執行緒的實體處理器數目都是實作定義的。 建立之後,小組中的執行緒數目會在該平列區域的持續時間維持不變。 它可以由使用者明確變更,或由執行時間系統從一個平列區域自動變更為另一個。
平列區域動態範圍中包含的語句是由每個執行緒執行,而且每個執行緒都可以執行與其他執行緒不同的語句路徑。 在平列區域的語彙範圍之外遇到的指示詞稱為孤立的指示詞。
平列區域結尾有隱含屏障。 只有小組的主要執行緒會在平列區域的結尾繼續執行。
如果執行平列區域的小組中的執行緒遇到另一個平行建構,則會建立新的小組,並成為該新小組的主圖形。 巢狀平列區域預設會序列化。 因此,根據預設,由由一個執行緒組成的小組會執行巢狀平列區域。 您可以使用執行時間程式庫函 omp_set_nested
式或環境變數 OMP_NESTED
來變更預設行為。 不過,執行巢狀平列區域的小組中的執行緒數目是實作定義的。
parallel
指示詞的限制如下:
最多可以有一個
if
子句出現在 指示詞上。如果未指定運算式或
num_threads
運算式內是否有任何副作用。在
throw
平列區域內執行的 ,必須在相同結構化區塊的動態範圍內繼續執行,而且必須由擲回例外狀況的相同執行緒攔截。指示詞上只能顯示單
num_threads
一子句。 運算式num_threads
是在平列區域的內容之外評估,而且必須評估為正整數值。未指定 和
num_threads
子句的評估if
順序。
交叉參考
private
、firstprivate
、default
、shared
、copyin
和reduction
子句 ( 第 2.7.2 節 )- OMP_NUM_THREADS環境變數
- omp_set_dynamic 程式庫函式
- OMP_DYNAMIC環境變數
- omp_set_nested函式
- OMP_NESTED環境變數
- omp_set_num_threads程式庫函式
2.4 工作共用建構
工作共用建構會將相關聯語句的執行散發給遇到該語句的小組成員。 工作共用指示詞不會啟動新的執行緒,而且工作共用建構的進入沒有隱含障礙。
針對小組中的每個執行緒,所遇到的工作共用建構和 barrier
指示詞順序必須相同。
OpenMP 會定義下列工作共用建構,下列各節會說明這些建構:
建構的 2.4.1
指示 for
詞會識別反復的工作共用建構,這個建構會指定相關聯迴圈的反復專案將會以平行方式執行。 迴圈的 for
反復專案會分散到小組中已存在的執行緒,執行其系結的平行建構。 建構的 for
語法如下所示:
#pragma omp for [clause[[,] clause] ... ] new-line for-loop
子句是下列其中一項:
private(
variable-list)
firstprivate(
variable-list)
lastprivate(
variable-list)
reduction(
operator:
variable-list)
ordered
schedule(
kind [,
chunk_size ])
nowait
指示 for
詞會將限制放在對應 for
迴圈的結構上。 具體來說,對應的 for
迴圈必須具有標準圖形:
for (
init-expr ;
var logical-op b ;
incr-expr)
init-expr
下列其中一項:
- var = lb
- integer-type var = lb
incr-expr
下列其中一項:
++
var- var
++
--
var- var
--
- var
+=
incr - var
-=
incr - var var
+
=
incr - var
=
incr+
var - var var
-
=
incr
var
帶正負號的整數變數。 否則,如果共用此變數,則會在 的持續時間內隱含地將它設為私用 for
。 請勿在 語句主體 for
內修改此變數。 除非指定 lastprivate
變數,否則在迴圈之後的值是不確定的。
logical-op
下列其中一項:
<
<=
>
>=
lb 、 b 和 incr
迴圈非變異整數運算式。 這些運算式的評估期間沒有同步處理,因此任何評估的副作用都會產生不確定的結果。
標準形式允許在進入迴圈時計算迴圈反復專案的數目。 此計算是在整數升階之後,以 var 類型 中的值進行。 特別是,如果 b -
lb +
incr 的值 無法以該類型表示,則結果不確定。 此外,如果 logical-op 為 <
或 <=
,則 incr-expr 必須在迴圈的每個反復專案上造成 var 增加。 如果 logical-op 為 >
或 >=
,則 incr-expr 必須在迴圈的每個反復專案上使 var 變小。
子 schedule
句會指定迴圈反復 for
專案在小組的執行緒之間如何分割。 程式的正確性不得取決於哪個執行緒執行特定反復專案。 如果指定,chunk_size 的值 必須是具有正值的迴圈不變異整數運算式。 此運算式評估期間沒有任何同步處理,因此任何評估的副作用都會產生不確定的結果。 排程 種類 可以是下列其中一個值:
表 2-1: schedule
子句 種類 值
值 | Description |
---|---|
static | 指定chunk_size ) 時 schedule(static, ,反復專案會分割成chunk_size 所 指定大小的區塊。 區塊會以迴圈配置資源的方式,以迴圈配置資源的方式,以靜態方式指派給小組中的執行緒,以執行緒編號的順序。 未 指定任何chunk_size 時,反覆運算空間會分成大小大致相等的區塊,並為每個執行緒指派一個區塊。 |
dynamic | 指定chunk_size ) 時 schedule(dynamic, ,反復專案會分成一系列區塊,每個區塊都包含 chunk_size 反復專案。 每個區塊都會指派給正在等候指派的執行緒。 執行緒會執行反復專案區塊,然後等候其下一個指派,直到未指派任何區塊為止。 要指派的最後一個區塊可能會有較少的反復專案。 未 指定chunk_size 時,預設為 1。 |
引導 | 指定chunk_size ) 時 schedule(guided, ,反復專案會以遞減大小來指派給區塊中的執行緒。 當執行緒完成其指派的反復專案區塊時,它會動態指派另一個區塊,直到沒有任何區塊為止。 對於 1 的chunk_size ,每個區塊的大小大約是未指派的反復專案數目除以執行緒數目。 這些大小幾乎以指數方式減少到 1。 對於值 k 大於 1 的chunk_size ,大小會以指數方式減少到 k ,但最後一個區塊可能少於 k 個反覆運算。 未 指定chunk_size 時,預設為 1。 |
執行階段 | 指定 時 schedule(runtime) ,有關排程的決策會延後到執行時間。 設定環境變數 OMP_SCHEDULE ,即可在執行時間選擇區塊的排程 種類 和大小。 如果未設定此環境變數,產生的排程會定義實作。 指定 時 schedule(runtime) , 不得指定chunk_size 。 |
如果沒有明確定義的 schedule
子句,預設值 schedule
為實作定義。
符合 OpenMP 規範的程式不應該依賴特定排程來正確執行。 程式不應該依賴符合上述描述的排程種類 ,因為在不同的編譯器之間,可以有相同排程 類型 實作的變化。 描述可用來選取適合特定情況的排程。
當 ordered
指示詞系結至 for
建構時 ordered
,子句必須存在。
除非指定 子句,否則 nowait
建構結尾 for
會有隱含屏障。
for
指示詞的限制如下:
迴圈
for
必須是結構化區塊,此外,其執行不得由break
語句終止。與 指示詞相關聯
for
之迴圈之迴圈控制項運算式for
的值,對於小組中的所有線程都必須相同。for
迴圈反覆運算變數必須具有帶正負號的整數類型。只有單
schedule
一for
子句可以出現在 指示詞上。只有單
ordered
一for
子句可以出現在 指示詞上。只有單
nowait
一for
子句可以出現在 指示詞上。未指定chunk_size 、lb 、 b 或 incr 運算式內 是否有任何副作用。
小組中所有線程的 chunk_size 運算式值 必須相同。
交叉參考
private
、firstprivate
、lastprivate
和reduction
子句 ( 第 2.7.2 節 )- OMP_SCHEDULE環境變數
- 已排序 的建構
- schedule 子句
2.4.2 區段建構
指示 sections
詞會識別非反復的工作共用建構,這個建構會指定要在小組中的執行緒之間分割的一組建構。 每個區段都會由小組中的執行緒執行一次。 指示詞的 sections
語法如下所示:
#pragma omp sections [clause[[,] clause] ...] new-line
{
[#pragma omp section new-line]
structured-block
[#pragma omp section new-linestructured-block ]
...
}
子句是下列其中一項:
private(
variable-list)
firstprivate(
variable-list)
lastprivate(
variable-list)
reduction(
operator:
variable-list)
nowait
每個區段前面都有 指示詞,不過第一個 section
區段的 section
指示詞是選擇性的。 指示 section
詞必須出現在 指示詞的 sections
語彙範圍內。 除非指定 ,否則 nowait
建構結尾 sections
會有隱含屏障。
sections
指示詞的限制如下:
指示
section
詞不得出現在 指示詞的sections
語彙範圍之外。只有單
nowait
一sections
子句可以出現在 指示詞上。
交叉參考
private
、firstprivate
、lastprivate
和reduction
子句 ( 第 2.7.2 節 )
2.4.3 單一建構
指示 single
詞會識別建構,指定小組中只有一個執行緒執行相關聯的結構化區塊(不一定是主執行緒)。 指示詞的 single
語法如下所示:
#pragma omp single [clause[[,] clause] ...] new-linestructured-block
子句是下列其中一項:
private(
variable-list)
firstprivate(
variable-list)
copyprivate(
variable-list)
nowait
除非 nowait
指定 子句, single
否則建構之後會有隱含屏障。
single
指示詞的限制如下:
- 只有單
nowait
一single
子句可以出現在 指示詞上。 - 子
copyprivate
句不得與 子句搭配nowait
使用。
交叉參考
private
、firstprivate
和copyprivate
子句 ( 第 2.7.2 節 )
2.5 合併的平行工作共用建構
合併的平行工作共用建構是指定只有一個工作共用建構之平列區域的快捷方式。 這些指示詞的語意與明確指定 指示詞相同,後面接著單一 parallel
工作共用建構。
下列各節說明合併的平行工作共用建構:
- parallel for 指示詞
- parallel sections 指示詞
2.5.1 平行建構
指示 parallel for
詞是只包含單 for
一 parallel
指示詞之區域的快捷方式。 指示詞的 parallel for
語法如下所示:
#pragma omp parallel for [clause[[,] clause] ...] new-linefor-loop
這個指示詞允許 指示詞和 for
指示詞的所有子句 parallel
,但 子句除外 nowait
,具有相同的意義和限制。 語意與明確指定 parallel
指示詞緊接著 for
指示詞相同。
交叉參考
2.5.2 平行區段建構
指示 parallel sections
詞會提供快捷方式表單,指定只有單 sections
一 parallel
指示詞的區域。 語意與明確指定 parallel
指示詞緊接著 sections
指示詞相同。 指示詞的 parallel sections
語法如下所示:
#pragma omp parallel sections [clause[[,] clause] ...] new-line
{
[#pragma omp section new-line]
structured-block
[#pragma omp section new-linestructured-block ]
...
}
子 句 可以是 和 sections
指示詞所 parallel
接受的其中一個子句,但 子句除外 nowait
。
交叉參考
2.6 主要和同步處理指示詞
下列各節說明:
2.6.1 主要建構
指示 master
詞會識別建構,指定小組主要執行緒所執行的結構化區塊。 指示詞的 master
語法如下所示:
#pragma omp master new-linestructured-block
小組中的其他執行緒不會執行相關聯的結構化區塊。 在進入或離開主要建構時,沒有隱含的屏障。
2.6.2 關鍵建構
指示 critical
詞會識別一個建構,該建構會一次將相關聯的結構化區塊執行限制為單一執行緒。 指示詞的 critical
語法如下所示:
#pragma omp critical [(name)] new-linestructured-block
選擇性 名稱 可用來識別重要區域。 用來識別重要區域的識別碼具有外部連結,且位於與標籤、標籤、成員和一般識別碼所使用的名稱空間分開的名稱空間中。
執行緒會在重要區域的開頭等候,直到沒有其他執行緒執行具有相同名稱的重要區域(程式中的任何位置)。 所有未命名 critical
的指示詞都會對應至相同的未指定名稱。
2.6.3 屏障指示詞
指示 barrier
詞會同步處理小組中的所有線程。 遇到時,小組中的每個執行緒都會等到其他所有線程都達到這個點。 指示詞的 barrier
語法如下所示:
#pragma omp barrier new-line
在小組中的所有線程都遇到屏障之後,小組中的每個執行緒都會在屏障指示詞平行執行語句。 barrier
因為 指示詞沒有 C 語言語句作為其語法的一部分,因此程式內的位置有一些限制。 如需正式文法的詳細資訊,請參閱 附錄 C 。下列範例說明這些限制。
/* 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
詞可確保特定記憶體位置會以不可部分完成的方式更新,而不是將其公開給多個同時寫入執行緒的可能性。 指示詞的 atomic
語法如下所示:
#pragma omp atomic new-lineexpression-stmt
expression 語句必須具有下列其中一種形式:
- x binop
=
expr - x
++
++
x- x
--
--
x
在上述運算式中:
x 是純量類型的左值運算式。
expr 是具有純量類型的運算式,而且不會參考 x 所 指定的物件。
binop 不是多載運算子,而且是 、
*
、&
^
/
|
-
、<<
或>>
的+
其中一個。
雖然實作定義實作是否以具有相同唯一名稱 的指示詞取代所有 atomic
critical
指示詞,但 指示 atomic
詞允許更好的優化。 通常可以使用硬體指示,以最少的額外負荷執行不可部分完成的更新。
只有 x 所 指定物件的載入和存放區是不可部分完成的;expr 的評估不是不可部分完成的 。 為了避免競爭條件,應使用 atomic
指示詞保護位置的所有平行更新,但已知沒有競爭條件的更新除外。
atomic
指示詞的限制如下:
- 在整個程式中,所有對儲存位置 x 的不可部分完成參考都必須具有相容的類型。
範例
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 指示詞
指示 flush
詞,無論是明確或隱含,都會指定實作所需的「跨執行緒」序列點,以確保小組中的所有線程都有記憶體中特定物件的一致檢視。。 這表示參考這些物件的運算式先前評估已完成,後續評估尚未開始。 例如,編譯器必須將物件的值從暫存器還原到記憶體,而硬體可能需要將寫入緩衝區排清至記憶體,並從記憶體重載物件的值。
指示詞的 flush
語法如下所示:
#pragma omp flush [(variable-list)] new-line
如果所有需要同步處理的物件都可以由變數指定,則可以在選擇性 變數清單中 指定那些變數。 如果變數清單中 有 指標,則指標本身會排清,而不是指標所參考的物件。
flush
沒有變數清單 的 指示詞會同步處理所有共用物件,但無法存取的物件具有自動儲存持續時間。 (這可能會有比 flush
變數清單 更多的額外負荷。 flush
下列指示詞隱含不含 變數清單 的指示詞:
barrier
- 進入和離開
critical
- 進入和離開
ordered
- 進入和離開
parallel
- 離開時
for
- 離開時
sections
- 離開時
single
- 進入和離開
parallel for
- 進入和離開
parallel sections
如果 nowait
子句存在,則不會隱含 指示詞。 請注意, flush
下列任何一項都不會隱含 指示詞:
- 在 專案到
for
- 進入或離開時
master
- 在 專案到
sections
- 在 專案到
single
存取具有揮發性限定型別之物件值的參考,就像在上一個 flush
序列點指定該物件的指示詞一樣。 修改具有 volatile 限定型別之物件值的參考,就像在後續序列點指定 flush
該物件的指示詞一樣。
flush
因為 指示詞沒有 C 語言語句作為其語法的一部分,因此程式內的位置有一些限制。 如需正式文法的詳細資訊,請參閱 附錄 C 。下列範例說明這些限制。
/* 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)
}
flush
指示詞的限制如下:
- 指示詞中指定的
flush
變數不能有參考型別。
2.6.6 已排序的建構
指示詞後面的 ordered
結構化區塊會依反復專案在循序迴圈中執行的順序來執行。 指示詞的 ordered
語法如下所示:
#pragma omp ordered new-linestructured-block
指示 ordered
詞必須位於 或 parallel for
建構的 for
動態範圍內。 建 for
構系結的 ordered
或 parallel for
指示詞必須指定 ordered
子句,如 2.4.1 節 所述 。 使用 子句執行 for
或 parallel for
建構時, ordered
建構 ordered
會嚴格按照迴圈的循序執行的順序來執行建構。
ordered
指示詞的限制如下:
- 具有
for
建構之迴圈的反復專案不能多次執行相同的已排序指示詞,而且不能執行一個ordered
以上的指示詞。
2.7 資料環境
本節提供指示詞和數個子句,用於控制平列區域執行期間的資料環境,如下所示:
提供 threadprivate 指示詞,讓檔案範圍、命名空間範圍或靜態區塊範圍變數成為執行緒的本機變數。
2.7.2 一節會 說明可在指示詞上指定,以控制平行或工作共用建構期間變數的共用屬性。
2.7.1 threadprivate 指示詞
指示 threadprivate
詞會將具名的 file-scope、namespace-scope 或 static 區塊範圍變數指定于 變數清單 私用至執行緒。 variable-list 是沒有不完整類型的變數逗號分隔清單。 指示詞的 threadprivate
語法如下所示:
#pragma omp threadprivate(variable-list) new-line
變數的每個複本 threadprivate
都會初始化一次,在程式第一次參考該複本之前的程式未指定點,並以一般方式初始化(亦即,因為主要複本會在程式的序列執行中初始化)。 請注意,如果在變數的 threadprivate
明確初始化運算式中參考物件,而且物件的值是在變數複本的第一個參考之前修改,則行為不會指定。
如同任何私用變數,執行緒不得參考另一個 threadprivate
執行緒的物件複本。 在程式的序欄區域和主要區域期間,參考會是主執行緒的 物件複本。
第一個平列區域執行之後,只有在停用動態執行緒機制,而且所有平列區域的執行緒數目保持不變時,物件中的資料 threadprivate
才保證會保存。
指示詞的限制 threadprivate
如下:
threadprivate
檔案範圍或命名空間範圍變數的指示詞必須出現在任何定義或宣告之外,而且必須在其清單中任何變數的所有參考前面加上語彙。在檔案或命名空間範圍之指示詞的
threadprivate
變數清單中 ,每個變數 都必須參考語彙在 指示詞之前之檔案或命名空間範圍的變數宣告。threadprivate
靜態區塊範圍變數的指示詞必須出現在變數的範圍中,而不是在巢狀範圍中。 指示詞在清單中所有變數的參考之前,必須具有語彙。區塊範圍中 指示詞變數
threadprivate
清單中的 每個變數都必須參考在語彙在 指示詞之前相同範圍的變數宣告。 變數宣告必須使用靜態儲存類別規範。如果在一個
threadprivate
轉譯單位的指示詞中指定變數,則必須在宣告它的每個轉譯單位的 指示詞中threadprivate
指定變數。threadprivate
變數不得出現在 、copyprivate
、schedule
num_threads
或if
子句以外的copyin
任何子句中。變數的
threadprivate
位址不是位址常數。threadprivate
變數不能有不完整的類型或參考型別。threadprivate
具有非 POD 類別類型的變數必須具有可存取且明確的複製建構函式,如果它是使用明確初始化運算式宣告的。
下列範例說明如何修改初始化運算式中顯示的變數可能會導致未指定的行為,以及如何使用輔助物件和 copy-constructor 來避免這個問題。
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. */
}
}
交叉參考
- 動態執行緒
- OMP_DYNAMIC環境變數
2.7.2 資料共用屬性子句
數個指示詞接受子句,可讓使用者控制區域期間變數的共用屬性。 共用屬性子句僅適用于子句出現之指示詞語系範圍中的變數。 並非所有指示詞都允許下列所有子句。 特定指示詞上有效的子句清單會使用 指示詞來描述。
如果遇到平行或工作共用建構時可見變數,而且未在共用屬性子句或 threadprivate
指示詞中指定變數,則會共用變數。 在平列區域的動態範圍內宣告的靜態變數會共用。 共用堆積配置的記憶體(例如,在 C 或 C++ 中使用 malloc()
或 C++ 中的 new
運算子)。 (不過,這個記憶體的指標可以是私人或共用的。在平列區域的動態範圍內宣告自動儲存持續時間的變數是私用的。
大部分子句都接受 variable-list 引數,這是可見的以逗號分隔的變數清單。 如果資料共用屬性子句中所參考的變數具有衍生自範本的類型,而且程式中沒有該變數的其他參考,則行為是未定義的。
所有出現在指示詞子句內的變數都必須可見。 子句可以視需要重複,但多個子句中不能指定任何變數,不同之處在于可以在 和 lastprivate
子句中 firstprivate
指定變數。
下列各節說明資料共用屬性子句:
2.7.2.1 private
子 private
句會將變數清單中的變數宣告為小組中每個執行緒的私人變數。 子句的 private
語法如下所示:
private(variable-list)
子句中指定的 private
變數行為如下所示。 具有自動儲存持續時間的新物件會配置給 建構。 新物件的大小和對齊方式取決於變數的類型。 此配置會針對小組中的每個執行緒執行一次,並在必要時為類別物件叫用預設建構函式;否則,初始值是不確定的。 變數所參考的原始物件在建構專案時具有不確定的值,不得在建構的動態範圍內修改,而且在從建構結束時具有不確定的值。
在指示詞建構的語彙範圍中,變數會輔助線程所配置的新私用物件。
子句的限制 private
如下:
具有 子句中所
private
指定類別類型的變數,必須具有可存取且明確的預設建構函式。子句中指定的
private
變數不得有const
-qualified 類型,除非它有具有成員的mutable
類別類型。子句中指定的
private
變數不得具有不完整的類型或參考型別。在系結至平行建構的工作共用指示詞上,無法在 子句中指定
private
出現在reduction
指示詞子parallel
句中的變數。
2.7.2.2 firstprivate
子 firstprivate
句提供 子句所 private
提供功能的超集合。 子句的 firstprivate
語法如下所示:
firstprivate(variable-list)
variable-list 中指定的 變數具有 private
子句語意,如 2.7.2.1 節 所述 。 初始化或建構會如同線上程執行建構之前,每個執行緒完成一次。 firstprivate
針對平行建構上的 子句,新私用物件的初始值是原始物件的值,該物件會緊接在遇到它之執行緒的平行建構之前存在。 firstprivate
針對工作共用建構上的 子句,執行工作共用建構之每個執行緒的新私用物件初始值是相同執行緒遇到工作共用建構之前存在的原始物件值。 此外,針對 C++ 物件,每個執行緒的新私用物件都會從原始物件複製。
子句的限制 firstprivate
如下:
子句中指定的
firstprivate
變數不得具有不完整的類型或參考型別。具有指定為
firstprivate
之類別類型的變數必須具有可存取且明確的複製建構函式。系結至平行建構之工作共用指示詞的
parallel
子句中,無法指定在平列區域內或出現在reduction
指示詞子句中的firstprivate
變數。
2.7.2.3 lastprivate
子 lastprivate
句提供 子句所 private
提供功能的超集合。 子句的 lastprivate
語法如下所示:
lastprivate(variable-list)
變數清單 中指定的 變數具有 private
子句語意。 lastprivate
當子句出現在識別工作共用建構的 指示詞上時,會從相關聯迴圈的循序最後一個反復專案或語彙最後一個區段指示詞中的每個變數值 lastprivate
指派給變數的原始物件。 未由 或 parallel for
的最後一個反復專案 for
或 指示詞的語彙最後一個區段 sections
parallel sections
指派值的變數,在 建構之後具有不確定的值。 未指派的子物件在 建構之後也有不確定的值。
子句的限制 lastprivate
如下:
適用的所有限制
private
。具有指定為
lastprivate
之類別類型的變數,必須具有可存取且明確的複製指派運算子。系結至平行建構之工作共用指示詞的
parallel
子句中,無法指定在平列區域內或出現在reduction
指示詞子句中的lastprivate
變數。
2.7.2.4 shared
這個子句會共用出現在小組中所有線程之變數清單中的 變數 。 小組內的所有線程都會存取變數的相同儲存區域 shared
。
子句的 shared
語法如下所示:
shared(variable-list)
2.7.2.5 default
子 default
句可讓使用者影響變數的資料共用屬性。 子句的 default
語法如下所示:
default(shared | none)
指定 default(shared)
相當於明確列出子句中 shared
每個目前可見的變數,除非它是 threadprivate
或 const
限定的。 如果沒有明確 default
子句,預設行為會與 default(shared)
指定的行為相同。
default(none)
指定時,平行建構語彙範圍中的每個變數參考至少都必須是下列其中一個:
變數會明確列在包含參考之建構的資料共用屬性子句中。
變數會在平行建構內宣告。
變數為
threadprivate
。變數具有
const
限定型別。變數是迴圈的迴圈控制項變數
for
,緊接在 或parallel for
指示詞之後for
,而變數參考會出現在迴圈內。
在所封入指示詞的 firstprivate
、 lastprivate
或 reduction
子句上指定變數,會導致在封入內容中隱含參考變數。 這類隱含參考也受限於上述需求。
指示詞上只能指定單 default
一 parallel
子句。
您可以使用 private
、 firstprivate
、 lastprivate
、 reduction
和 shared
子句覆寫變數的預設資料共用屬性,如下列範例所示:
#pragma omp parallel for default(shared) firstprivate(i)\
private(x) private(r) lastprivate(i)
2.7.2.6 reduction
這個子句會使用 運算子 op ,對出現在 變數清單中的 純量變數執行縮減。 子句的 reduction
語法如下所示:
reduction(
op :
variable-list)
對於具有下列其中一種形式的語句,通常會指定縮減:
- x x
=
op expr - x binop
=
expr - x
=
expr op x (除減法除外) - x
++
++
x- x
--
--
x
其中:
x
清單中指定的其中一個縮減變數。
variable-list
純量縮減變數的逗號分隔清單。
expr
具有未參考 x 之純量類型的運算式。
op
不是多載運算子,而是 、 *
、 &
-
、、 ^
、 |
、 &&
或 ||
的 +
其中一個 。
binop
不是多載運算子,而是 、、 *
-
、 &
、 ^
或 |
的 +
其中一個 。
以下是 子句的 reduction
範例:
#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];
}
如範例所示,運算子可能會隱藏在函式呼叫內。 使用者應該小心子句中指定的 reduction
運算子符合縮減作業。
雖然運算子的 ||
右運算元在此範例中沒有副作用,但允許使用,但應該小心使用。 在此內容中,保證不會在迴圈循序執行期間發生的副作用可能會在平行執行期間發生。 因為反覆運算的執行順序不確定,因此可能會發生這種差異。
運算子可用來判斷編譯器用於縮減之任何私用變數的初始值,以及判斷最終化運算子。 明確指定 運算子可讓縮減語句超出建構的語彙範圍。 任何數目的 reduction
子句都可以在 指示詞上指定,但該指示詞的變數最多可能會出現一個 reduction
子句。
系統會建立變數清單 中 每個變數的私人複本,每個執行緒各一個,就好像使用了 子句一樣 private
。 私用複本會根據運算子初始化(請參閱下表)。
在指定 子句的區域 reduction
結尾,會更新原始物件,以反映使用指定的運算子,將原始值與每個私人複本的最終值結合的結果。 縮減運算子都是關聯運算子(除減法除外),編譯器可以自由地重新關聯最終值的計算。 (減法減法的部分結果會加成最終值。
當第一個執行緒到達包含子句時,原始物件的值會變得不確定,並維持在縮減計算完成之前。 一般而言,計算會在建構結束時完成;不過,如果在 reduction
同時套用 的 nowait
建構上使用 子句,原始物件的值會維持不確定狀態,直到執行屏障同步處理,以確保所有線程都已完成 reduction
子句。
下表列出有效運算子及其標準初始化值的運算子。 實際的初始化值會與縮減變數的資料類型一致。
運算子 | 初始化 |
---|---|
+ |
0 |
* |
1 |
- |
0 |
& |
~0 |
| |
0 |
^ |
0 |
&& |
1 |
|| |
0 |
子句的限制 reduction
如下:
子句中的
reduction
變數類型必須對縮減運算子有效,但不允許指標類型和參考型別。子句中指定的
reduction
變數不得為const
-qualified。系結至平行建構之工作共用指示詞的
parallel
子句中,無法指定在平列區域內或出現在reduction
指示詞子句中的reduction
變數。#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
子 copyin
句會提供一個機制,為執行平列區域之小組中的每個執行緒指派相同的值 threadprivate
給變數。 針對 子句中指定的 copyin
每個變數,小組主要執行緒中的變數值會複製至平列區域開頭的執行緒私用複本,就像透過指派一樣。 子句的 copyin
語法如下所示:
copyin(
variable-list
)
子句的限制 copyin
如下:
子句中指定的
copyin
變數必須具有可存取且明確的複製指派運算子。子句中指定的
copyin
變數必須是threadprivate
變數。
2.7.2.8 copyprivate
子 copyprivate
句提供一種機制,以使用私用變數將某個小組成員的值廣播至其他成員。 提供這類共用變數時,這是使用共用變數做為值的替代方案(例如,在每個層級需要不同變數的遞迴中)。 子 copyprivate
句只能出現在 指示詞上 single
。
子句的 copyprivate
語法如下所示:
copyprivate(
variable-list
)
子句對其變數清單中的變數的影響 copyprivate
會在與 建構相關聯的 single
結構化區塊執行之後發生,而且在小組中的任何執行緒都離開建構結尾的屏障之前。 然後,在小組中的所有其他執行緒中,針對變數清單中的 每個變數 ,該變數會以執行建構之結構化區塊的執行緒中對應的變數值來定義(如同透過指派)。
copyprivate
子句的限制如下:
子句中指定的
copyprivate
變數不得出現在private
相同single
指示詞的 或firstprivate
子句中。single
如果在平列區域的動態範圍中遇到具有copyprivate
子句的指示詞,子句中指定的copyprivate
所有變數都必須在封入內容中為私用。子句中指定的
copyprivate
變數必須具有可存取的明確複製指派運算子。
2.8 指示詞系結
指示詞的動態系結必須遵循下列規則:
for
、sections
、single
、master
和barrier
指示詞會系結至動態封parallel
入 ,如果存在,則不論該指示詞上可能存在之任何if
子句的值為何。 如果目前未執行任何平列區域,指示詞會由只由主要執行緒組成的小組執行。指示
ordered
詞會系結至動態封入for
的 。指示
atomic
詞會針對所有線程中的指示詞強制執行獨佔存取atomic
權,而不只是目前的小組。指示
critical
詞會針對所有線程中的指示詞強制執行獨佔存取critical
權,而不只是目前的小組。指示詞永遠不能系結至最接近動態封
parallel
入 以外的任何指示詞。
2.9 指示詞巢狀
指示詞的動態巢狀必須遵循下列規則:
parallel
指示詞會以動態方式在另一個parallel
邏輯內部建立新的小組,除非已啟用巢狀平行處理原則,否則只有目前的執行緒所組成。for
系結至相同parallel
的 、sections
和single
指示詞不允許彼此巢狀。critical
不允許具有相同名稱的指示詞彼此巢狀。 請注意,這項限制不足以防止死結。for
sections
如果 指示詞系結至與區域相同的parallel
,則 、 和single
區域的動態範圍critical
不允許 、ordered
和master
指示詞。barrier
如果 指示詞系結至與區域相同的parallel
,則不允許在 、ordered
、、sections
single
、master
和critical
區域的動態範圍內for
使用 指示詞。master
如果master
指示詞系結至與工作共用指示詞相同的parallel
,則不允許在 、sections
和single
指示詞的for
動態範圍內使用 指示詞。ordered
如果指示詞系結至與區域相同的parallel
,則區域動態範圍critical
不允許指示詞。在平列區域內動態執行時,也允許在平列區域內執行時允許的任何指示詞。 以動態方式在使用者指定的平列區域外部執行時,指示詞是由只由主要執行緒組成的小組執行。
意見反應
https://aka.ms/ContentUserFeedback。
即將登場:在 2024 年,我們將逐步淘汰 GitHub 問題作為內容的意見反應機制,並將它取代為新的意見反應系統。 如需詳細資訊,請參閱:提交並檢視相關的意見反應