2

我在 OpenMP 并行区域中有两个 do-loop,如下所示:

!$OMP PARALLEL
...
!$OMP DO
...
!$OMP END DO
...
!$OMP DO
...
!$OMP END DO
...
!$OMP END PARALLEL

假设 OMP_NUM_THREADS=6。我想用 4 个线程运行第一个 do-loop,用 3 个线程运行第二个 do-loop。你能展示怎么做吗?我希望它们在一个平行区域内。也可以指定哪个线程号应该执行任何一个 do-loop,例如在第一个 do-loop 的情况下,我可以要求它使用线程号 1、2、4 和 5。谢谢。

4

2 回答 2

5

好吧,您可以将该num_threads子句添加到 OpenMPparallel指令,但这适用于该区域内的任何指令。在您的情况下,您可以将程序分成两个区域,例如

!$OMP PARALLEL DO num_threads(4)
...
!$OMP END PARALLEL DO
...
!$OMP PARALLEL DO num_threads(3)
...
!$OMP END PARALLEL DO

当然,这正是你说你不想做的,只有一个平行区域。但是没有限制并行区域内使用的线程数量的机制。就我个人而言,我不明白为什么有人愿意这样做。

至于将部分计算分配给特定线程,同样,不,OpenMP 没有提供这样做的机制,您为什么要这样做?

我想我非常传统,但是当我看到程序员试图对单个线程进行精确控制的并行程序的迹象时,我通常会看到一个具有以下一个或多个特征的程序:

  • OpenMP 指令用于确保代码串行运行,结果运行时间超过原始串行代码的运行时间;
  • 程序不正确,因为程序员未能正确处理数据竞争的微妙之处;
  • 它经过精心安排,仅在特定数量的线程上运行。

在并行程序中这些都不是可取的,如果您想要控制线程数量的级别以及将工作分配给各个线程,您将不得不使用比 OpenMP 提供的更低级别的方法。此类方法比比皆是,因此放弃 OpenMP 不应限制您。

于 2013-07-27T19:53:29.820 回答
2

现有的 OpenMP 结构无法实现您想要的,只能手动实现。想象一下原来的并行循环是:

!$OMP DO
DO i = 1, 100
...
END DO
!$OMP END DO

自定义选择参与线程的修改版本将是:

USE OMP_LIB

INTEGER, DIMENSION(:), ALLOCATABLE :: threads
INTEGER :: tid, i, imin, imax, tidx

! IDs of threads that should execute the loop
! Make sure no repeated items inside
threads = (/ 0, 1, 3, 4 /)

IF (MAXVAL(threads, 1) >= omp_get_max_threads()) THEN
   STOP 'Error: insufficient number of OpenMP threads'
END IF

!$OMP PARALLEL PRIVATE(tid,i,imin,imax,tidx)
! Get current thread's ID
tid = omp_get_thread_num()
...
! Check if current thread should execute part of the loop
IF (ANY(threads == tid)) THEN
   ! Find out what thread's index is
   tidx = MAXLOC(threads, 1, threads == tid)
   ! Compute iteration range based on the thread index
   imin = 1 + ((100-1 + 1)*(tidx-1))/SIZE(threads)
   imax = 1 + ((100-1 + 1)*tidx)/SIZE(threads) - 1
   PRINT *, 'Thread', tid, imin, imax
   DO i = imin, imax
      ...
   END DO
ELSE
   PRINT *, 'Thread', tid, 'not taking part'
END IF
! This simulates the barrier at the end of the worksharing construct
! Remove in order to implement the "nowait" clause
!$OMP BARRIER
...
!$OMP END PARALLEL

以下是三个示例执行:

$ OMP_NUM_THREADS=2 ./custom_loop.x | sort
STOP Error: insufficient number of OpenMP threads
$ OMP_NUM_THREADS=5 ./custom_loop.x | sort
 Thread           0           1          33
 Thread           1          34          66
 Thread           2 not taking part
 Thread           3 not taking part
 Thread           4          67         100
$ OMP_NUM_THREADS=7 ./custom_loop.x | sort
 Thread           0           1          33
 Thread           1          34          66
 Thread           2 not taking part
 Thread           3 not taking part
 Thread           4          67         100
 Thread           5 not taking part
 Thread           6 not taking part

请注意,这是一个糟糕的 hack,违反了 OpenMP 模型的基本前提。我强烈建议不要这样做并依赖某些线程来执行代码的某些部分,因为它会创建高度不可移植的程序并阻碍运行时优化。


如果您决定放弃显式分配应该执行循环的线程的想法并且只想动态更改线程数,那么SCHEDULE子句中的块大小参数是您的朋友:

!$OMP PARALLEL
...
! 2 threads = 10 iterations / 5 iterations/chunk
!$OMP DO SCHEDULE(static,5)
DO i = 1, 10
   PRINT *, i, omp_get_thread_num()
END DO
!$OMP END DO
...
! 10 threads = 10 iterations / 1 iteration/chunk
!$OMP DO SCHEDULE(static,1)
DO i = 1, 10
   PRINT *, i, omp_get_thread_num()
END DO
!$OMP END DO
...
!$OMP END PARALLEL

以及 10 个线程的输出:

$ OMP_NUM_THREADS=10 ./loop_chunks.x | sort_manually :)
 First loop
 Iteration     Thread ID
       1           0
       2           0
       3           0
       4           0
       5           0
       6           1
       7           1
       8           1
       9           1
      10           1
 Second loop
 Iteration     Thread ID
       1           0
       2           1
       3           2
       4           3
       5           4
       6           5
       7           6
       8           7
       9           8
      10           9
于 2013-07-27T20:11:18.827 回答