共用方式為


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 為零,則會序列化區域。

若要判斷要求的執行緒數目,將會依序考慮下列規則。 符合條件的第一個規則將會套用:

  1. num_threads如果子句存在,整數運算式的值就是要求的執行緒數目。

  2. 如果已呼叫連結 omp_set_num_threads 庫函式,則最近執行呼叫中引數的值是要求的執行緒數目。

  3. 如果已定義環境變數 OMP_NUM_THREADS ,則此環境變數的值就是要求的執行緒數目。

  4. 如果未使用上述任何方法,則所要求的執行緒數目是實作定義的。

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 順序。

交叉參考

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迴圈反覆運算變數必須具有帶正負號的整數類型。

  • 只有單 schedulefor 子句可以出現在 指示詞上。

  • 只有單 orderedfor 子句可以出現在 指示詞上。

  • 只有單 nowaitfor 子句可以出現在 指示詞上。

  • 未指定chunk_size 、lb b incr 運算式內 是否有任何副作用。

  • 小組中所有線程的 chunk_size 運算式值 必須相同。

交叉參考

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 語彙範圍之外。

  • 只有單 nowaitsections 子句可以出現在 指示詞上。

交叉參考

  • privatefirstprivatelastprivatereduction 子句 ( 第 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指示詞的限制如下:

  • 只有單 nowaitsingle 子句可以出現在 指示詞上。
  • copyprivate 句不得與 子句搭配 nowait 使用。

交叉參考

2.5 合併的平行工作共用建構

合併的平行工作共用建構是指定只有一個工作共用建構之平列區域的快捷方式。 這些指示詞的語意與明確指定 指示詞相同,後面接著單一 parallel 工作共用建構。

下列各節說明合併的平行工作共用建構:

2.5.1 平行建構

指示 parallel for 詞是只包含單 forparallel 指示詞之區域的快捷方式。 指示詞的 parallel for 語法如下所示:

#pragma omp parallel for [clause[[,] clause] ...] new-linefor-loop

這個指示詞允許 指示詞和 for 指示詞的所有子句 parallel ,但 子句除外 nowait ,具有相同的意義和限制。 語意與明確指定 parallel 指示詞緊接著 for 指示詞相同。

交叉參考

2.5.2 平行區段建構

指示 parallel sections 詞會提供快捷方式表單,指定只有單 sectionsparallel 指示詞的區域。 語意與明確指定 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 不是多載運算子,而且是 、 *&^/|-<<>>+ 其中一個。

雖然實作定義實作是否以具有相同唯一名稱 的指示詞取代所有 atomiccritical 指示詞,但 指示 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 構系結的 orderedparallel for 指示詞必須指定 ordered 子句,如 2.4.1 節 所述 。 使用 子句執行 forparallel 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變數不得出現在 、 copyprivateschedulenum_threadsif 子句以外的 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. */
   }
}

交叉參考

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 或 指示詞的語彙最後一個區段 sectionsparallel 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 每個目前可見的變數,除非它是 threadprivateconst 限定的。 如果沒有明確 default 子句,預設行為會與 default(shared) 指定的行為相同。

default(none)指定時,平行建構語彙範圍中的每個變數參考至少都必須是下列其中一個:

  • 變數會明確列在包含參考之建構的資料共用屬性子句中。

  • 變數會在平行建構內宣告。

  • 變數為 threadprivate

  • 變數具有 const 限定型別。

  • 變數是迴圈的迴圈控制項變數 for ,緊接在 或 parallel for 指示詞之後 for ,而變數參考會出現在迴圈內。

在所封入指示詞的 firstprivatelastprivatereduction 子句上指定變數,會導致在封入內容中隱含參考變數。 這類隱含參考也受限於上述需求。

指示詞上只能指定單 defaultparallel 子句。

您可以使用 privatefirstprivatelastprivatereductionshared 子句覆寫變數的預設資料共用屬性,如下列範例所示:

#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 指示詞系結

指示詞的動態系結必須遵循下列規則:

  • forsectionssinglemasterbarrier 指示詞會系結至動態封 parallel 入 ,如果存在,則不論該指示詞上可能存在之任何 if 子句的值為何。 如果目前未執行任何平列區域,指示詞會由只由主要執行緒組成的小組執行。

  • 指示 ordered 詞會系結至動態封入 for 的 。

  • 指示 atomic 詞會針對所有線程中的指示詞強制執行獨佔存取 atomic 權,而不只是目前的小組。

  • 指示 critical 詞會針對所有線程中的指示詞強制執行獨佔存取 critical 權,而不只是目前的小組。

  • 指示詞永遠不能系結至最接近動態封 parallel 入 以外的任何指示詞。

2.9 指示詞巢狀

指示詞的動態巢狀必須遵循下列規則:

  • parallel指示詞會以動態方式在另一個 parallel 邏輯內部建立新的小組,除非已啟用巢狀平行處理原則,否則只有目前的執行緒所組成。

  • for系結至相同 parallel 的 、 sectionssingle 指示詞不允許彼此巢狀。

  • critical 不允許具有相同名稱的指示詞彼此巢狀。 請注意,這項限制不足以防止死結。

  • forsections如果 指示詞系結至與區域相同的 parallel ,則 、 和 single 區域的動態範圍 critical 不允許 、 orderedmaster 指示詞。

  • barrier如果 指示詞系結至與區域相同的 parallel ,則不允許在 、 ordered 、、 sectionssinglemastercritical 區域的動態範圍內 for 使用 指示詞。

  • master如果 master 指示詞系結至與工作共用指示詞相同的 parallel ,則不允許在 、 sectionssingle 指示詞的 for 動態範圍內使用 指示詞。

  • ordered如果指示詞系結至與區域相同的 parallel ,則區域動態範圍 critical 不允許指示詞。

  • 在平列區域內動態執行時,也允許在平列區域內執行時允許的任何指示詞。 以動態方式在使用者指定的平列區域外部執行時,指示詞是由只由主要執行緒組成的小組執行。