这是在F90,但问题适用于任何支持OpenMP的语言。为需要多个存储阵列进行时间集成的模拟代码构建数据的典型方法是(目前为2维):
REAL, DIMENSION(imax,jmax,n_sub_timesteps) :: vars
然后会用以下内容更新:
DO J = 1, jmax
DO I = 1, imax
vars(I,J,2) = func(vars(:,:,1))
END DO
END DO
根据我的经验,OpenMP实际上不会并行化这些循环,因为它认为vars
不是线程安全的。但对于程序员来说,显然是这样。
让我们假设在进一步的实际情况下,使vars
线程局部化将太昂贵而无法将数据复制到其中。
那么,有没有办法轻轻提示(又称强制)OpenMP不锁定vars
,因为它可能不会发现没有线程依赖问题,但实际上没有?我知道有办法告诉它某些东西不是线程安全的并且需要锁定,但是有没有办法在不为每个线程制作副本的情况下指定反转?
答案 0 :(得分:3)
看起来你错误地将OpenMP误认为是自动并行化。我不知道任何执行数据锁定的OpenMP实现,除非显式通过引入CRITICAL
部分或ATOMIC
语句(或在并行结束时)告知带有REDUCTION
子句的区域。 OpenMP编译器不检查代码是否存在可能的数据依赖性并阻止您并行运行 - 这完全由您自己完成。如果您想进行不受保护的并发访问,则可以执行此操作,并且没有启用OpenMP的编译器会阻止您这样做。以下代码将始终生成并行区域,并将在团队中的线程之间分配外部循环:
!$OMP PARALLEL DO PRIVATE(I)
DO J = 1, jmax
DO I = 1, imax
vars(I,J,2) = func(vars(:,:,1))
END DO
END DO
!$OMP END PARALLEL DO
另一方面,大多数编译器中的内置自动并行程序非常保守和谨慎,如果没有程序员的明确提示,通常不会像你这样的情况并行化。这些提示通常采用特定于编译器的指令的形式(格式化为Fortran中的注释或C / C ++中的编译指示)。例如,英特尔Fortran支持!DEC$ PARALLEL
指令,该指令暗示它忽略遵循该指令的循环中的假定数据依赖性:
!DEC$ PARALLEL
DO J = 1, jmax
DO I = 1, imax
vars(I,J,2) = func(vars(:,:,1))
END DO
END DO
许多编译器重用其OpenMP实现和运行时库以实现自动并行化功能,因此生成的可执行文件的工作通常由OpenMP环境变量控制,如OMP_NUM_THREADS
。
如果您的并行OpenMP程序运行速度低于预期,还有许多其他原因,主要与错误共享,缓存废弃,TLB废弃,内存带宽限制,NUMA系统上的非本地内存访问,使用非临时负载有关/存储到共享变量等,因此看起来OpenMP可能会执行自动数据锁定,但它不会。