【问题标题】:Assigning different thread numbers in OpenMP do-loops在 OpenMP 循环中分配不同的线程号
【发布时间】:2013-07-28 04:25:47
【问题描述】:

我在 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。谢谢。

【问题讨论】:

    标签: fortran openmp


    【解决方案1】:

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

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

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

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

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

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

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

    【讨论】:

      【解决方案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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-06-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-02-08
        相关资源
        最近更新 更多