c++ - 为什么 omp_set_num_threads( omp_get_num_threads() ) 会改变任何东西?

标签 c++ multithreading parallel-processing mpi openmp

我遇到了一些奇怪的事情。我正在一台小型本地机器上测试 MPI + OMP 并行代码,该机器只有一个简陋的 4 核 I3。事实证明,我的一个循环非常慢,在此环境中每个进程有超过 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=1omp_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

最佳答案

I've run across something odd. I am testing an MPI + OMP parallel code on a small local machine with only a single, humble 4 core I3. One of my loops, it turns out, is very slow with more than 1 OMP thread per process in this environment (more threads than cores).

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

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

But it seems that omp_get_num_threads() returns 1 regardless. And in fact, if I just do this omp_set_num_threads( omp_get_num_threads() );

来自source人们可以阅读:

2.15 omp_get_num_threads – 活跃团队的规模

Description: *Returns the number of threads in the current team. In a sequential section of the program omp_get_num_threads returns 1.

通俗地说,如果在并行区域之外调用 omp_get_num_threads(),则会得到 1 作为线程数,即初始线程 em>.

Why should calling omp_set_num_threads( omp_get_num_threads() ) once at the beginning of a program ever result in a 200X difference in performance?

问题的根本原因不是调用omp_set_num_threads( omp_get_num_threads() ) per si,而是线程正在战斗 获取资源。通过将每个进程的线程数显式设置为1,您可以确保应用程序运行时每个线程1核心,从而导致同一核心内不会有多个线程争夺资源。

关于c++ - 为什么 omp_set_num_threads( omp_get_num_threads() ) 会改变任何东西?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65316195/

相关文章:

logging - 并行 MATLAB 和日志记录

c# - 使用 com 可调用包装器将结构数组从 C# 传递到 C++

c++ - 带有可选字段的 ASN.1 编码

c++ - C++ 中的命名空间

multithreading - C/C++ 和其他语言中的条件变量使用模式

协程中的 Python 循环

python - 将 c 函数的线程与 python 的多处理混合

Bash:子进程访问变量

python - 在python脚本中并行运行多个作业

c++ - 实现基于迭代器的 shell 排序