Oracle Solaris Studio 12.2:OpenMP API 用户指南

6.7 自动作用域示例

下面的示例说明了自动作用域规则的工作原理。


示例 6–3 更复杂的示例


 1.      REAL FUNCTION FOO (N, X, Y)
 2.      INTEGER       N, I
 3.      REAL          X(*), Y(*)
 4.      REAL          W, MM, M
 5.
 6.      W = 0.0
 7.
 8. C$OMP PARALLEL DEFAULT(__AUTO)
 9.
10. C$OMP SINGLE
11.       M = 0.0
12. C$OMP END SINGLE
13.
14.       MM = 0.0
15.
16. C$OMP DO
17.       DO I = 1, N
18.          T = X(I)
19.          Y(I) = T
20.          IF (MM .GT. T) THEN
21.             W = W + T
22.             MM = T
23.          END IF
24.       END DO
25. C$OMP END DO
26.
27. C$OMP CRITICAL
28.       IF ( MM .GT. M ) THEN
29.          M = MM
30.       END IF
31. C$OMP END CRITICAL
32.
33. C$OMP END PARALLEL
34.
35.      FOO = W - M
36.
37.      RETURN
38.      END

函数 FOO() 包含一个并行区域,该并行区域包含一个 SINGLE 构造、一个工作共享 DO 构造和一个 CRITICAL 构造。如果我们忽略所有 OpenMP 并行构造,则并行区域中的代码所执行的操作如下:

  1. 将数组 X 中的值复制到数组 Y

  2. 查找 X 中的最大正数值,并将其存储在 M

  3. X 的一些元素的值累加到变量 W 中。

让我们看一下编译器如何使用上述规则来查找并行区域中变量的相应作用域。

在并行区域中使用下列变量:INMMTWMXY。编译器将确定以下内容。


示例 6–4 使用 QuickSort 的示例


static void par_quick_sort (int p, int r, float *data)
{
   if (p < r) 
   {
      int q = partition (p, r, data);  
      
      #pragma omp task default(__auto) if ((r-p)>=low_limit)
      par_quick_sort (p, q-1, data);

      #pragma omp task default(__auto) if ((r-p)>=low_limit)
      par_quick_sort (q+1, r, data);
   }
}

int main ()
{
  ...
  #pragma omp parallel
  {
     #pragma omp single nowait
     par_quick_sort (0, N-1, &Data[0]);
  }
  ...
}

er_src result:

      Source OpenMP region below has tag R1
      Variables autoscoped as FIRSTPRIVATE in R1: p, q, data
      Firstprivate variables in R1: data, p, q
        47. #pragma omp task default(__auto) if ((r-p)>=low_limit)
        48. par_quick_sort (p, q-1, data);
      
      Source OpenMP region below has tag R2
      Variables autoscoped as FIRSTPRIVATE in R2: q, r, data
      Firstprivate variables in R2: data, q, r
        49. #pragma omp task default(__auto) if ((r-p)>=low_limit)
        50. par_quick_sort (q+1, r, data);

标量变量 pq 以及指针变量数据在任务构造和并行区域中都是只读的。因此,根据 TS1 自动将其作用域确定为 FIRSTPRIVATE



示例 6–5 另一个示例


int fib (int n)
{
   int x, y;
   if (n < 2) return n;

   #pragma omp task default(__auto)
   x = fib(n - 1);

   #pragma omp task default(__auto)
   y = fib(n - 2);

   #pragma omp taskwait
   return x + y;
}

er_src result:

   Source OpenMP region below has tag R1
   Variables autoscoped as SHARED in R1: x
   Variables autoscoped as FIRSTPRIVATE in R1: n
   Shared variables in R1: x
   Firstprivate variables in R1: n
    24.         #pragma omp task default(__auto) /* shared(x) firstprivate(n) */
    25.         x = fib(n - 1);
   
   Source OpenMP region below has tag R2
   Variables autoscoped as SHARED in R2: y
   Variables autoscoped as FIRSTPRIVATE in R2: n
   Shared variables in R2: y
   Firstprivate variables in R2: n
    26.         #pragma omp task default(__auto) /* shared(y) firstprivate(n) */
    27.         y = fib(n - 2);
    28. 
    29.         #pragma omp taskwait
    30.         return x + y;
    31. }

标量 n 在任务构造和并行构造中都是只读的。因此,根据 TS1 自动将 n 的作用域确定为 FIRSTPRIVATE

标量变量 xy 是函数 fib() 的局部变量。在两个任务中访问 xy 不会导致数据争用。由于存在一个 taskwait,因此这两个任务将在执行 fib() 的线程(遇到了这两个任务)退出 fib() 之前完成执行;这意味着 xy 将在这两个任务执行时出现。因此,根据 TS2 自动将 xy 的作用域确定为 SHARED



示例 6–6 另一个示例


int main(void)
{
  int yy = 0;

  #pragma omp parallel default(__auto) shared(yy)
  {
    int xx = 0;

    #pragma omp single
    {
       #pragma omp task default(__auto) // task1
       {
          xx = 20;
       }
    }

    #pragma omp task default(__auto) // task2
    {
       yy = xx;
    }
  }

  return 0;
}

er_src result:

   Source OpenMP region below has tag R1
   Variables autoscoped as PRIVATE in R1: xx
   Private variables in R1: xx
   Shared variables in R1: yy
     7.   #pragma omp parallel default(__auto) shared(yy)
     8.   {
     9.     int xx = 0;
    10.

   Source OpenMP region below has tag R2
    11.     #pragma omp single
    12.     {

   Source OpenMP region below has tag R3
   Variables autoscoped as SHARED in R3: xx
   Shared variables in R3: xx
    13.        #pragma omp task default(__auto) // task1
    14.        {
    15.           xx = 20;
    16.        }
    17.     }
    18.

   Source OpenMP region below has tag R4
   Variables autoscoped as PRIVATE in R4: yy
   Variables autoscoped as FIRSTPRIVATE in R4: xx
   Private variables in R4: yy
   Firstprivate variables in R4: xx
    19.     #pragma omp task default(__auto) // task2
    20.     {
    21.        yy = xx;
    22.     }
    23.   }

在此示例中,xx 是并行区域中的专用变量。组中的其中一个线程会修改 xx 的初始值(通过执行 task1)。然后,所有线程都会遇到使用 xx 执行某些计算的 task2

task1 中,使用 xx 不会导致数据争用。由于单个构造结尾有一个隐式屏障,而且 task1 应在退出此屏障之前完成,因此,xx 将在 task1 执行时出现。所以,根据 TS2,在 task1 中自动将 xx 的作用域确定为 SHARED

task2 中,xx 的使用是只读的。但是,xx 的使用在包含它的并行构造中不是只读的。由于 xx 在并行构造中预先确定为 PRIVATE,因此无法确定 xx 将在执行 task2 时出现。所以,根据 TS3,在 task2 中自动将 xx 的作用域确定为 FIRSTPRIVATE

task2 中,使用 yy 会导致数据争用,在每个执行 task2 的线程中,在读取变量 yy 之前始终先由同一线程写入该变量。所以,根据 TS4,在 task2 中自动将 yy 的作用域确定为 PRIVATE



示例 6–7 另一个示例


int foo(void)
{
  int xx = 1, yy = 0;

  #pragma omp parallel shared(xx,yy)
  {
    #pragma omp task default(__auto)
    {
       xx += 1;

       #pragma omp atomic
       yy += xx;
    }

    #pragma omp taskwait
  }
  return 0;
}

er_src result:

   Source OpenMP region below has tag R1
   Shared variables in R1: yy, xx
     5.   #pragma omp parallel shared(xx,yy)
     6.   {

   Source OpenMP region below has tag R2
   Variables autoscoped as SHARED in R2: yy
   Variables autoscoped as FIRSTPRIVATE in R2: xx
   Shared variables in R2: yy
   Firstprivate variables in R2: xx
     7.     #pragma omp task default(__auto)
     8.     {
     9.        xx += 1;
    10.
    11.        #pragma omp atomic
    12.        yy += xx;
    13.     }
    14.
    15.     #pragma omp taskwait
    16.   }

任务构造中,xx 的使用不是只读的,且会导致数据争用。但在任务区域中读取 x 会获取在任务之外定义的 x 值。(在此示例中,由于 xx 对于并行区域确定为 SHARED,因此 x 的定义实际上在并行区域之外。)所以,根据 TS5,自动将 xx 的作用域确定为 FIRSTPRIVATE

任务构造中,yy 的使用不是只读的,但不会导致数据争用。由于存在 taskwait,因此可在执行任务时访问 yy。所以,根据 TS2,自动将 yy 的作用域确定为 SHARED