如何通过循环将 OpenMP 线程拆分为子组

How to split OpenMP threads into subteams over a loop

本文关键字:拆分 线程 OpenMP 何通过 循环      更新时间:2023-10-16

假设我有一个以下函数,它在内部使用#pragma omp parallel

void do_heavy_work(double * input_array);

我现在想这样do_heavy_work许多input_arrays

void do_many_heavy_work(double ** input_arrays, int num_arrays)
{
    for (int i = 0; i < num_arrays; ++i)
    {
        do_heavy_work(input_arrays[i]);
    }
}

假设我有N硬件线程。上面的实现将导致do_heavy_work的调用以串行方式发生num_arrays每个N线程在内部使用所有线程来执行它想要的任何并行操作。

现在假设num_arrays > 1时,通过这个外部循环并行实际上比在do_heavy_work内部并行更有效。我现在有以下选择。

  • #pragma omp parallel for放在外环上并设置OMP_NESTED=1。但是,通过设置OMP_NUM_THREADS=N我最终将生成大量线程(N*num_arrays(。
  • 如上所述,但关闭嵌套并行性。这在num_arrays < N时浪费了可用的内核。

理想情况下,我希望 OpenMP 将其OMP_NUM_THREADS线程团队拆分为num_arrays子团队,然后每个do_heavy_work都可以在其分配的子团队上线程(如果给定一些子团队(。

实现这一目标的最简单方法是什么?

(出于此讨论的目的,我们假设事先不一定知道num_arrays,并且我无法更改do_heavy_work本身的代码。该代码应该在多台机器上工作,因此N应该是可自由指定的。

OMP_NUM_THREADS可以

设置为列表,从而指定每个嵌套级别的线程数。 例如 OMP_NUM_THREADS=10,4将告诉 OpenMP 运行时执行具有 10 个线程的外部并行区域,每个嵌套区域将使用 4 个线程执行,总共最多有 40 个同时运行的线程。

或者,您可以使用类似于以下内容的代码使程序自适应:

void do_many_heavy_work(double ** input_arrays, int num_arrays)
{
    #pragma omp parallel num_threads(num_arrays)
    {
        int nested_team_size = omp_get_max_threads() / num_arrays;
        omp_set_num_threads(nested_team_size);
        #pragma omp for
        for (int i = 0; i < num_arrays; ++i)
        {
            do_heavy_work(input_arrays[i]);
        }
    }
}

如果 OMP_NUM_THREADS 的值不能被 num_arrays 整除,则此代码不会使用所有可用的线程。如果每个嵌套区域具有不同数量的线程是可以的(这可能会导致某些数组的处理速度比其他数组更快(,请了解如何分配线程并相应地在每个线程中设置nested_team_size。从并行区域内调用omp_set_num_threads()仅影响由调用线程启动的嵌套区域,因此可以具有不同的嵌套团队大小。