我有3个完全独立的任务,因此是并行执行的理想选择:
任务1:执行名为subA()的(单线程)子程序。
任务2:执行名为subB()的(单线程)子程序。
任务3:在DO循环中填充数组。 DO循环的每次迭代都独立于所有其他迭代。
假设我有8个线程。我想线程0在任务1上工作,线程1在任务2上工作,线程2-7在任务3上工作。在Fortran中,我想象这样的事情:
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循环,因此所有其他线程必须在PARALLEL区域末尾的隐式屏障处等待它们。
在这种情况下处理线程调度的正确方法是什么?
冒着提供太多信息的风险,我已经知道subA()和subB()是计算密集型的,而func(j)的每个评估都相对较快。对于每个subA()和subB()来说,完成它需要大致相同的时间,因为当多个线程被分配给后一个任务时,整个DO循环就完成了。
一些注意事项:
ETA:来自英特尔的人指出我原来的问题含糊不清:我不清楚我的DO循环是否只是一个从func(1..nn)到vals(1..nn)的memcpy(),或者我是不是正在调用一个名为func()的函数nn次。后者是我的意图,我在示例代码中澄清了这一点。
答案 0 :(得分:0)
使用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
答案 1 :(得分:0)
我到目前为止找到的最佳解决方案是手动执行我希望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 PARALLEL
nthr的其他值的情况是对上述代码的明显修改。
此解决方案在正确且快速的意义上运行良好,但从软件工程的角度来看,代码重复使其不是最理想的。