使用编译器注释可检查自动确定作用域的结果,并确定是否因自动确定作用域失败而导致将并行区域序列化。
使用 -g 调试选项编译时,编译器将生成行内注释。可以使用 er_src 命令查看这个生成的注释,如下所示。(er_src 命令作为 Sun Studio 软件的一部分提供;有关更多信息,请参见 er_src(1) 手册页或《Sun Studio 性能分析器》手册。)
使用 -xvpara 选项进行编译是一个良好的开端。如果自动确定作用域失败,将打印一条警告消息(如下所示)。
%cat t.f INTEGER X(100), Y(100), I, T C$OMP PARALLEL DO DEFAULT(__AUTO) DO I=1, 100 T = Y(I) CALL FOO(X) X(I) = T*T END DO C$OMP END PARALLEL DO END %f95 -xopenmp -xO3 -vpara -c t.f "t.f", line 2: Warning: parallel region will be executed by a single thread because the autoscoping of following variables failed - x |
使用 f95 的 -vpara 或 cc 的 -xvpara 进行编译。(CC 中尚未实现此选项。)
%cat t.f INTEGER X(100), Y(100), I, T C$OMP PARALLEL DO DEFAULT(__AUTO) DO I=1, 100 T = Y(I) X(I) = T*T END DO C$OMP END PARALLEL DO END %f95 -xopenmp -xO3 -g -c t.f %er_src t.o Source file: ./t.f Object file: ./ot.o Load Object: ./t.o 1. INTEGER X(100), Y(100), I, T Source OpenMP region below has tag R1 Variables autoscoped as SHARED in R1: x, y Variables autoscoped as PRIVATE in R1: t, i Private variables in R1: i, t Shared variables in R1: y, x 2. C$OMP PARALLEL DO DEFAULT(__AUTO) <Function: _$d1A2.MAIN_> Source loop below has tag L1 L1 parallelized by explicit user directive L1 parallel loop-body code placed in function _$d1A2.MAIN_ along with 0 inner loops Copy in M-function of loop below has tag L2 L2 scheduled with steady-state cycle count = 3 L2 unrolled 4 times L2 has 0 loads, 0 stores, 2 prefetches, 0 FPadds, 0 FPmuls, and 0 FPdivs per iteration L2 has 1 int-loads, 1 int-stores, 4 alu-ops, 1 muls, 0 int-divs and 1 shifts per iteration 3. DO I=1, 100 4. T = Y(I) 5. X(I) = T*T 6. END DO 7. C$OMP END PARALLEL DO 8. END |
接下来,使用一个更复杂的示例来解释自动作用域规则的应用方式。
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 并行构造,则并行区域中的代码所执行的操作如下:
将数组 X 中的值复制到数组 Y
查找 X 中的最大正数值,并将其存储在 M 中
将 X 的一些元素的值累加到变量 W 中。
让我们看一下编译器如何使用上述规则来查找并行区域中变量的相应作用域。
在并行区域中使用下列变量:I、N、MM、T、W、M、X 和 Y。编译器将确定以下内容。
标量 I 是工作共享 DO 循环的循环索引。OpenMP 规范要求将 I 的作用域确定为 PRIVATE。
标量 N 在并行区域中只被进行读取,因此不会导致数据争用,这样,按照规则 S1,将其作用域确定为 SHARED。
任何执行并行区域的线程都会执行语句 14,以便将标量 MM 的值设置为 0.0。这一写入操作会导致数据争用,因此规则 S1 不适用。由于是在同一线程中读取 MM 之前出现该写入操作,因此,按照规则 S2,将 MM 的作用域确定为 PRIVATE。
同样,将标量 T 的作用域确定为 PRIVATE。
先读取标量 W,然后将其写入语句 21,因此,规则 S1 和 S2 不适用。加法运算同时符合结合律和交换律,因此,按照规则 S3,将 W 的作用域确定为 REDUCTION(+)。
标量 M 写入语句 11,该语句位于 SINGLE 构造内。SINGLE 构造末尾的隐式屏障可确保写入语句 11 不会与读取语句 28 或写入语句 29 同时发生,并且后两者不会同时发生,因为它们都位于同一 CRITICAL 构造内。没有任何两个线程可以同时访问 M。因此,在并行区域中写入和读取 M 都不会导致数据争用,按照规则 S1,将 M 的作用域确定为 SHARED。
数组 X 在区域中只被进行读取,不进行写入,因此,按照规则 A1,将其作用域确定为 SHARED。
写入数组 Y 的操作分布在各个线程中,没有任何两个线程会写入 Y 的相同元素。由于不存在数据争用,因此,按照规则 A1,将 Y 的作用域确定为 SHARED。