首页 > 解决方案 > 为什么 omp_set_num_threads( omp_get_num_threads() ) 会改变任何东西?

问题描述

我遇到了一些奇怪的事情。我正在一台只有一个不起眼的 4 核 I3 的小型本地机器上测试 MPI + OMP 并行代码。事实证明,我的一个循环非常慢,在这个环境中每个进程有超过 1 个 OMP 线程(线程多于内核)。

#pragma omp parallel for
for ( int i = 0; i < HEIGHT; ++i ) 
{
    for ( int j = 0; j < WIDTH; ++j ) 
    {
        double a = 
           ( data[ sIdx * S_SZ + j + i * WIDTH ] - dMin ) / ( dMax - dMin );

        buff[ i ][ j ] = ( unsigned char ) ( 255.0 * a );
    }
}

OMP_NUM_THREADS如果我使用默认值(不设置或使用)运行此代码omp_set_num_threads,则大约需要 1 秒。但是,如果我使用任何一种方法显式设置线程数(export OMP_NUM_THREADS=1或者omp_set_num_threads(1))大约需要 0.005 秒(快 200 倍))。

但似乎omp_get_num_threads()无论如何都返回 1。事实上,如果我只是这样做omp_set_num_threads( omp_get_num_threads() );,则大约需要 0.005 秒,而注释掉该行则需要 1 秒。

知道这里发生了什么吗?为什么omp_set_num_threads( omp_get_num_threads() )在程序开始时调用一次会导致 200 倍的性能差异?

一些上下文,

cpu:             Intel(R) Core(TM) i3-9100F CPU @ 3.60GHz
g++ --version:   g++ (GCC) 10.2.0
compiler flags:  mpic++ -std=c++11 -O3 -fpic -fopenmp ...
running program: mpirun -np 4 ./a.out

标签: c++multithreadingparallel-processingmpiopenmp

解决方案


我遇到了一些奇怪的事情。我正在一台只有一个不起眼的 4 核 I3 的小型本地机器上测试 MPI + OMP 并行代码。事实证明,我的一个循环非常慢,在这个环境中每个进程有超过 1 个 OMP 线程(线程多于内核)。

首先,如果没有将OpenMP线程(在 MPI 进程内)显式绑定到内核,则无法确定这些线程最终会在哪个内核中结束。自然地,在同一个逻辑核心中运行多个线程通常会增加被并行化的应用程序的整体执行。您可以通过以下任一方式解决此问题: 1) 禁用与 MPI 标志的绑定--bind-to none,以便将线程分配给不同的内核;2) 或相应地执行线程绑定。检查此SO 线程,了解如何将线程映射到混合并行化中的核心,例如MPI + OpenMP.

尽管如此,即使(假设)每个进程映射到一个核心,并且每个核心有 4 个线程假设每个核心都有两个逻辑核心(超线程),应用程序的总执行时间将很可能比使用4Process x1线程运行它要慢。在当前的上下文中,人们可能希望(最多)使用4Process x2线程来提高性能。

但似乎 omp_get_num_threads() 无论如何都返回 1。事实上,如果我只是这样做 omp_set_num_threads( omp_get_num_threads() );

源代码可以阅读:

2.15 omp_get_num_threads – 活跃团队的规模

说明:*返回当前团队中的线程数。在程序的连续部分中,omp_get_num_threads 返回 1。

非正式地,如果omp_get_num_threads()在并行区域之外调用,则将得到1线程数,即初始线程

为什么在程序开始时调用一次 omp_set_num_threads(omp_get_num_threads()) 会导致 200 倍的性能差异?

The root cause of the problem is not the call omp_set_num_threads( omp_get_num_threads() ) per si, but rather the fact that threads are fighting for resources. By explicitly setting the number of threads per process to 1, you ensured that the application ran with 1 thread per core, which consequently lead to not having multiple threads within the same core fighting for resources.


推荐阅读