我有三项任务彼此完全独立,因此是并行执行的好人选:
任务1:执行名为subA()的(单线程)子例程。
任务2:执行名为subB()的(单线程)子例程。
任务3:在DO循环中填充数组。DO-循环的每一次迭代都是独立于所有其他迭代的。
假设我有8个线程。我希望线程0处理任务1,线程1处理任务2,线程2-7处理任务3。
COMPLEX*8, EXTERNAL :: func
!$OMP PARALLEL
!$OMP SECTIONS
!$OMP SECTION
!
! Task 1, performed by one thread
!
CALL subA()
!$OMP SECTION
!
! Task 2, performed by one thread
!
CALL subB()
!$OMP END SECTIONS NOWAIT
!$OMP DO
!
! Task 3, performed by all threads
!
DO j=1,nn
vals(j) = func(j)
END DO
!$OMP END DO NOWAIT
!$OMP END PARALLEL但是上面的代码不是我想要的。在任务1和任务2上工作的线程也计划在任务3中的DO循环上工作,这似乎减缓了一切,大概是因为这两个线程到达DO循环“晚”了,因此所有其他线程都必须在并行区域末尾的隐式屏障上等待它们。
在这种情况下,处理线程调度的正确方法是什么?
冒着提供太多信息的风险,我已经知道subA()和subB()是计算密集型的,而对func(j)的每个评估都比较快。当多个线程被分配给后一个任务时,完成subA()和subB()所需的时间大致相当于整个DO循环完成所需的时间。
一些注意事项:
ETA:来自英特尔的人指出,我最初的问题是模棱两可的:不清楚我的DO循环是一个memcpy(),从func(1..nn)到vals(1..nn),还是调用一个函数func() nn次。后者是我的意图,我在示例代码中澄清了这一点。
发布于 2016-06-22 13:05:19
使用OpenMP任务,如下所示(未经测试)
!$omp parallel
! Have a single thread create all the tasks.
!$omp single
!$omp task
call subA()
!$omp end task
!$omp task
call subB()
!$omp end task
DO j=1,nn
!$omp task
vals(j) = func(j)
!$omp end task
END DO
!$omp end single
!$omp end parallel发布于 2016-06-22 16:09:14
到目前为止,我找到的最佳解决方案是手动执行我希望OpenMP自动为我执行的调度。特别是,让nthr是我希望使用的线程数。然后,按照以下方式使用SELECT CASE结构:
SELECT CASE (nthr)
CASE (3)
! Use 3 threads. See example below
CASE (4)
! Use 4 threads. See example below
CASE (5)
! And so on...
CASE DEFAULT
! Catch-all
END SELECT在有4个线程的情况下,我执行以下操作:
!$OMP PARALLEL PRIVATE(j) NUM_THREADS(4)
!$OMP SECTIONS
!$OMP SECTION
!
! Task 1, performed by one thread
!
CALL subA()
!$OMP SECTION
!
! Task 2, performed by one thread
!
CALL subB()
!$OMP SECTION
!
! One thread does half the loop...
!
DO j=1,nn/2
vals(j)=func(j)
END DO
!$OMP SECTION
!
! ...and one thread does the other half
!
DO j=nn/2+1,nn
vals(j)=func(j)
END DO
!$OMP END SECTIONS NOWAIT
!$OMP END PARALLELnthr的其他值的情况是对上述代码的明显修改。
这个解决方案很好地工作在正确和快速的意义上,但是从软件工程的角度来看,代码重复使得它不太理想。
https://stackoverflow.com/questions/37949734
复制相似问题