我正在尝试将一些旧版 Fortran 代码与 OpenMP 并行化。 使用 Intel Inspector 检查竞争条件时,我在以下代码中遇到了问题(简化的测试示例):
PROGRAM TEST
!$ use omp_lib
implicit none
DOUBLE PRECISION :: x,y,z
COMMON /firstcomm/ x,y,z
!$OMP THREADPRIVATE(/firstcomm/)
INTEGER :: i
!$ call omp_set_num_threads(3)
!$OMP PARALLEL DO
!$OMP+ COPYIN(/firstcomm/)
!$OMP+ PRIVATE(i)
do i=1,3000
z = 3.D0
y = z+log10(z)
x=y+z
enddo
!$OMP END PARALLEL DO
END PROGRAM TEST
英特尔 Inspector 检测到以下几行之间的竞争条件:
-
!$OMP PARALLEL DO
(阅读) -
z = 3.D0
(写)
检查器“反汇编” View 分别提供了有关这两行的以下内容(除了两行中的内存地址似乎不同之外,我对这些不太了解):
-
0x3286 callq 0x2a30 <memcpy>
-
0x3338 movq %r14, 0x10(%r12)
与我的主应用程序一样,问题发生在公共(public) block 中的一个(/某些)变量上,但不会发生在以相同方式处理的其他变量上。
任何人都可以发现我的错误,还是这个竞争条件是误报?
我知道一般情况下不鼓励使用 COMMON block ,但我无法为当前项目更改这一点。
最佳答案
从技术上讲,您的示例代码不正确,因为您使用的是 COPYIN
使用未初始化的数据初始化线程私有(private)副本 COMMON BLOCK
。但这并不是数据竞争的原因 - 添加 DATA
语句或简单地分配给 x
, y
,和z
在并行区域之前不会改变结果。
这要么是英特尔 Fortran 编译器中的一个(非常古老的)错误,要么是英特尔正在奇怪地解释 OpenMP 标准的文本(当前版本的 section 2.15.4.1):
The copy is done, as if by assignment, after the team is formed and prior to the start of execution of the associated structured block.
英特尔通过插入 memcpy
来实现强调文本。在概述的程序开始时。换句话说:
!$OMP PARALLEL DO COPYIN(/firstcomm/)
do i = 1, 3000
...
end do
!$OMP END PARALLEL DO
变成(Fortran 和伪代码的混合):
par_region0:
my_firstcomm = get_threadprivate_copy(/firstcomm/)
if (my_firstcomm != firstcomm) then
memcpy(my_firstcomm, firstcomm, size of firstcomm)
end if
// Actual implementation of the DO worksharing construct
call determine_iterations(1, 3000, low_it, high_it)
do i = low_it, high_it
...
... my_firstcomm used here instead of firstcomm
...
end do
call openmp_barrier
end par_region0
MAIN:
// Prepare a parallel region with 3 threads
// and fire the outlined code in the worker threads
call start_parallel_region(3, par_region0)
// Fire the outlined code in the master thread
call par_region0
call end_parallel_region
概述的过程首先找到公共(public) block 的线程私有(private)副本的地址,然后将该地址与公共(public) block 本身的地址进行比较。如果两个地址匹配,则代码正在主线程中执行,不需要复制,否则 memcpy
被调用以将主数据按位复制到 threadprivate block 中。
现在,人们会期望在初始化部分的末尾和循环开始之前应该有一个障碍,尽管 Intel employees claim that there is one ,没有(使用 ifort 11.0、14.0 和 16.0 进行测试)。更重要的是,英特尔 Fortran 编译器不支持 COPYIN
中的变量列表。子句,如果子句中列出了其中包含的任何变量,则复制整个公共(public) block ,即 COPYIN(x)
与 COPYIN(/firstcomm/)
的处理方式相同.
这些究竟是英特尔 Fortran 编译器的错误还是功能,只有英特尔才能知道。也可能是我误读了汇编输出。 如果有人能找到缺失的障碍,请告诉我。一种可能的解决方法是拆分组合指令并在工作共享构造之前插入显式障碍:
!$OMP PARALLEL COPYIN(/firstcomm/) PRIVATE(I)
!$OMP BARRIER
!$OMP DO
do i = 1, 3000
z = 3.D0
y = z+log10(z)
x = y+z
end do
!$OMP END DO
!$OMP END PARALLEL
随着这一变化,数据竞争将转向log10
内内部调度表的初始化。调用,这可能是误报。
GCC 实现 COPYIN
不同。它创建主线程的线程私有(private)数据的共享副本,然后将其复制到工作线程以在复制过程中使用。
关于fortran - OpenMP 竞争条件(Fortran 77 w/COMMON block ),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35001653/