Sun Studio 12:C 用户指南

3.8.3.2 并行 Pragma


注 –

Sun 特定的 MP pragma 已过时,并且不再受支持。但是,编译器改为支持 OpenMP 2.5 标准指定的 API。有关标准的指令的迁移信息,请参见《OpenMP API 用户指南》。


有一个并行 pragma:#pragma MP taskloop [options]

MP taskloop pragma 可以根据需要带下列一个或多个参数。

这些 pragma 的作用域以 pragma 开始,并在下列先出现的项结束:下一块的开始部分、当前块内部的下一个 for 循环、当前块的结尾。该 pragma 应用于 pragmas 作用域结束前的下一个 for 循环。

每个 MP taskloop pragma 只能指定一个选项;但是,pragmas 是累积的,并应用于 pragmas 作用域中的下一个 for 循环:


#pragma MP taskloop maxcpus(4)
#pragma MP taskloop shared(a,b)
#pragma MP taskloop storeback(x)

这些选项在其所应用的 for 循环之前可能会多次出现。如果存在冲突选项,编译器将发出警告消息。

for 循环的嵌套

MP taskloop pragma 应用于当前块内部的下一个 for 循环。并行化 C 不存在并行化 for 循环的嵌套。

并行化的合格性

除非另有禁止,否则 MP taskloop pragma 建议编译器应并行化指定的 for 循环。

任何具有不规则控制流和未知循环迭代增量的 for 循环均不能进行并行化。例如,包含 setjmplongjmpexitabortreturngotolabelsbreakfor 循环均不能并行化。

特别重要的是,具有迭代间依赖性的 for 循环可以进行显式并行化。这意味着,如果为此类循环指定了一个 MP taskloop pragma,除非 for 循环被取消资格,否则编译器将完全遵循该 pragma。用户有责任确保此类显式并行化不会导致错误结果。

如果为 for 循环指定了 serial_loop(或 serial_loop_nested)pragma 和 taskloop pragma,则将使用最后指定的 pragma。

请看以下示例:


#pragma MP serial_loop_nested
    for (i=0; i<100; i++) {
   # pragma MP taskloop
      for (j=0; j<1000; j++) {
      ...
 }
}

i 循环将不并行化,但是 j 循环可能并行化。

处理器数

#pragma MP taskloop maxcpus (number_of_processors) 指定要用于此循环的处理器数(如果可能)。

maxcpus 的值必须为正整数。如果 maxcpus 等于 1,指定的循环将串行执行。(请注意,将 maxcpus 设置为 1 相当于指定 serial_loop pragma。)将比较 maxcpus 的值与 PARALLEL 环境变量的解释值,使用较小的值。在未指定环境变量 PARALLEL 的情况下,该 pragma 被解释为具有值 1。

如果为一个 for 循环指定了多个 maxcpus pragma,将使用最后指定的 pragma。

变量分类

循环中使用的变量可分类为 privatesharedreductionreadonly 变量。变量只能属于其中一种分类。通过显式 pragma 只能将变量分类为 reductionreadonly 变量。请参见 #pragma MP taskloop reduction#pragma MP taskloop readonly。通过显式 pragma 或以下缺省作用域规则可将变量分类为 privateshared 变量。

privateshared 变量的缺省作用域规则

private 变量的值是处理 for 循环的某些迭代的每个处理器的私有值。换句话说,在 for 循环的一次迭代中赋给 private 变量的值不会传送给处理该 for 循环的其他迭代的其他处理器。而 shared 变量的当前值可处理 for 循环的迭代的所有处理器访问。处理循环迭代的一个处理器赋给 shared 变量的值可能会被处理循环的其他迭代的其他处理器识别。通过使用 #pragma MP taskloop 指令显式并行化并包含共享变量引用的循环,必须确保值的共享不会导致正确性问题(如竞争情况)。对显式并行化的循环中的共享变量的更新和访问,编译器不提供同步。

在分析显式并行化的循环时,编译器使用以下“缺省作用域规则”来确定变量是 private 变量还是 shared 变量:

强烈建议将显式并行化的 for 循环中使用的所有变量显式分类为 sharedprivatereductionreadonly 变量,以避免使用“缺省作用域规则”。

由于编译器对共享变量的访问不执行同步,因此在对包含数组引用等的循环使用 MP taskloop pragma 之前必须格外谨慎。如果此类显式并行化的循环中存在迭代间数据依赖性,则其并行执行会导致错误结果。编译器不一定能够检测到此类潜在问题情况并发出警告消息。无论如何,编译器不会禁止具有潜在共享变量问题的循环的显式并行化。

private 变量

#pragma MP taskloop private (list_of_private_variables)

使用此 pragma 指定所有应视为此循环私有变量的变量。此循环中使用但未显式指定为 sharedreadonlyreduction 变量的所有其他变量,按缺省作用域规则的定义,为 shared 变量或 private 变量。

private 变量的值是处理循环的某些迭代的每个处理器的私有值。换句话说,处理循环迭代的一个处理器赋给 private 变量的值不会传送给处理该循环的其他迭代的其他处理器。private 变量在循环的每次迭代开始时没有初始值,必须在循环的迭代内部第一次使用它之前,在该迭代内部设置为一个值。如果某个循环包含一个在设置之前使用其值的显式声明 private 变量,则执行包含该循环的程序将导致不确定的行为。

shared 变量

#pragma MP taskloop shared (list_of_shared_variables)

使用此 pragma 指定所有应视为此循环的 shared 变量的变量。循环中使用的但未显式指定为 privatereadonlystorebackreduction 变量的所有其他变量,按缺省作用域规则的定义,为 shared 变量或 private 变量。

shared 变量的当前值可被处理 for 循环的迭代的所有处理器访问。处理循环迭代的一个处理器赋给 shared 变量的值可能会被处理循环的其他迭代的其他处理器识别。

readonly 变量

#pragma MP taskloop readonly (list_of_readonly_variables)

readonly 变量是一类特殊的共享变量,不在循环的任何迭代中进行修改。使用此 pragma 向编译器指示,它可以对处理循环迭代的每个处理器使用该变量值的单独副本。

storeback 变量

#pragma MP taskloop storeback (list_of_storeback_variables)

使用此 pragma 指定所有应视为 storeback 变量的变量。

storeback 变量的值在循环中计算,并且计算的值在循环终止后使用。storeback 变量的最后一个循环迭代值在循环终止后使用。当变量为私有变量时,此类变量可以很好地通过此指令显式声明为 storeback 变量,通过将变量显式声明为私有变量或通过使用缺省作用域规则均可。

请注意,storeback 变量的返回存储操作发生在显式并行化循环的最后一次迭代中,而无论该迭代是否更新 storeback 变量的值。换句话说,处理循环最后一次迭代的处理器可能并不是当前包含 storeback 变量的最后更新值的同一处理器。请看以下示例:


#pragma MP taskloop private(x)
#pragma MP taskloop storeback(x)
   for (i=1; i <= n; i++) {
      if (...) {
          x=...
      }
}
   printf (“%d”, x);

在上例中,通过 printf() 调用打印出的 storeback 变量 x 的值可能与通过 i 循环的串行版本打印出的值不同,原因是,在显式并行化情况下,处理循环最后一次迭代(当 i==n 时)的处理器(即执行 x 的返回存储操作的处理器)可能不是当前包含 x 的最后更新值的同一处理器。编译器将尝试发出警告消息,提醒用户注意此类潜在问题。

在显式并行化的循环中,作为数组引用的变量不视为 storeback 变量。因此,如果需要此类返回存储操作(例如,如果作为数组引用的变量已声明为私有变量),则将作为数组引用的变量包括在 list_of_storeback_variables 中很重要。

savelast

#pragma MP taskloop savelast

使用此 pragma 指定要视为返回存储变量的所有私有变量。此 pragma 的语法如下:

#pragma MP taskloop savelast

通常,使用这种形式很方便,无需在将每个变量声明为返回存储变量时列出循环的每个私有变量。

reduction 变量

#pragma MP taskloop reduction (list_of_reduction_variables) 指定出现在 reduction 变量列表中的所有变量均将视为循环的 reduction 变量。reduction 变量的部分值可由处理循环迭代的每个处理器单独计算,其最终值可从其所有部分值中计算。有了 reduction 变量列表,便于编译器识别循环是否为约简循环,从而允许为其生成并行约简代码。请看以下示例:


#pragma MP taskloop reduction(x)
    for (i=0; i<n; i++) {
         x = x + a[i];
}

变量 x(sum) 约简变量,i 循环是 (sum) 约简循环。

调度控制

Sun ISO C 编译器支持多种 pragma,这些 pragma 可与 taskloop pragma 配合使用,以控制给定循环的循环调度策略。此 pragma 的语法是:

#pragma MP taskloop schedtype (scheduling_type)

此 pragma 可用来指定要用来调度并行化循环的特定 scheduling_typeScheduling_type 可为以下类型之一:


#pragma MP taskloop maxcpus(4)
#pragma MP taskloop schedtype(static)
    for (i=0; i<1000; i++) {
...
}

在以上示例中,四个处理器中的每个处理器将处理循环的 250 次迭代。


#pragma MP taskloop maxcpus(4)
#pragma MP taskloop schedtype(self(120))
for (i=0; i<1000; i++) {
...
}

在以上示例中,分配给每个参与处理的处理器的迭代次数按工作请求顺序依次为:

120、120、120、120、120、120、120、120、40。


#pragma MP taskloop maxcpus(4)
#pragma MP taskloop schedtype(gss(10))
for (i=0; i<1000; i++) {
...
}

在以上示例中,分配给每个参与处理的处理器的迭代次数按工作请求顺序依次为:

250、188、141、106、79、59、45、33、25、19、14、11、10、10、10。


#pragma MP taskloop maxcpus(4)
#pragma MP taskloop schedtype(factoring(10))
for (i=0; i<1000; i++) {
...
}

在以上示例中,分配给每个参与处理的处理器的迭代次数按工作请求顺序依次为:

125、125、125、125、62、62、62、62、32、32、32、32、16、16、16、16、10、10、10、10、10、10。