假设我有一个以下函数,它在内部使用#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()
仅影响由调用线程启动的嵌套区域,因此可以具有不同的嵌套团队大小。