在同一个 CPU 核心上执行的 OpenMP 线程

8

我目前正在使用OpenMP在4核Phenom2上并行化程序。然而,我注意到我的并行化对性能没有任何影响。自然地,我认为我错过了什么(如false sharing、锁定序列化等),但是我没有找到任何类似的问题。此外,从CPU利用率来看,似乎程序只在一个核上执行。根据我所发现的,sched_getcpu()应该给我当前调度线程的核心ID。因此,我编写了以下测试程序:

#include <iostream>
#include <sstream>
#include <omp.h>
#include <utmpx.h>
#include <random>
int main(){
    #pragma omp parallel
    {
        std::default_random_engine rand;
        int num = 0;
    #pragma omp for
        for(size_t i = 0; i < 1000000000; ++i) num += rand();
    auto cpu = sched_getcpu();
    std::ostringstream os;
        os<<"\nThread "<<omp_get_thread_num()<<" on cpu "<<sched_getcpu()<<std::endl;
        std::cout<<os.str()<<std::flush;
    std::cout<<num;
    }
}

在我的电脑上,这将输出以下内容(随机数当然会有所不同):
Thread 2 on cpu 0 num 127392776
Thread 0 on cpu 0 num 1980891664
Thread 3 on cpu 0 num 431821313
Thread 1 on cpu 0 num -1976497224

从这里我推断所有线程都在同一个核心上执行(即id为0的那个)。为了更确定,我还尝试了这个答案中提到的方法。结果也是一样的。此外,使用#pragma omp parallel num_threads(1)并没有使执行变慢(事实上略微加快了),从而证明了所有线程使用同一个CPU的理论可信度,但是CPU始终显示为0让我有点怀疑。此外,我检查了GOMP_CPU_AFFINITY,最初没有设置它,所以我尝试将其设置为0 1 2 3,这应该将每个线程绑定到不同的核心。然而,这并没有什么区别。
由于我在Windows系统上开发,我在虚拟机中使用Linux进行开发。因此我认为可能虚拟系统无法访问所有核心。然而,检查virtualbox的设置显示虚拟机应该获得所有4个核心,并且同时执行我的测试程序4次似乎使用了所有4个核心,从CPU利用率(以及系统非常不响应的事实)来判断。
因此,我的问题基本上是这里究竟发生了什么。更重要的是:我的推断所有线程使用同一个核心是正确的吗?如果是,可能是什么原因导致了这种行为?

1
这是一个常见的错误,你设置了环境变量 OMP_NUM_THREADS = 4 吗? - pyCthon
1
@pyCthon:似乎没有设置OMP_NUM_THREADS,但由于openmp会创建4个线程,我认为不需要设置。 - Grizzly
奇怪,我认为可能是你的虚拟机有问题。我尝试了相同的代码,甚至安装了utmpx.h,它在8核和16核的机器上似乎都正常工作。 - pyCthon
我曾经在某个地方读到,虚拟机(客户操作系统)作为主机操作系统内的单个进程运行。这可能是您正在看到的行为的原因吗? - maths-help-seeker
在一台具有 Scientific Linux 6 的 2 CPU x86-64 服务器上,我也遇到了同样的问题。没有看到任何 IDE 或虚拟机。 - Vladimir F Героям слава
4个回答

6

经过一些实验,我发现问题在于我是从eclipse IDE内部启动我的程序的,这似乎设置了亲和性来只使用一个核心。我认为当我从IDE外面启动时会遇到同样的问题,但重复测试表明,当从终端启动而不是从IDE内部启动时,程序运行正常。


这些可以通过像这样的变量进行设置:https://web.archive.org/web/20220114064748/https://pages.tacc.utexas.edu/~eijkhout/pcse/html/omp-affinity.html - Y00

2

我在Linux上使用g++ 4.6编译了您的程序。

g++ --std=c++0x -fopenmp test.cc -o test

输出结果是,毫不意外地:
Thread 2 on cpu 2

Thread 3 on cpu 1
910270973
Thread 1 on cpu 3
910270973
Thread 0 on cpu 0
910270973910270973

如果您没有以任何方式设置线程数(例如使用OMP_NUM_THREADS),那么启动4个线程应该意味着程序能够看到4个可用的CPU。我无法猜测为什么它没有使用它们,但我怀疑是您的硬件/软件设置、某些环境变量或编译器选项出现了问题。


0

你应该使用#pragma omp parallel for
而且,你说得对,不需要使用OMP_NUM_THREADS。omp_set_num_threads(4);也可以。


如果我想让线程在循环外执行其他任务(例如将其ID写入输出),为什么要使用#pragma omp parallel for?正如我所提到的,默认情况下它确实创建了4个线程,但似乎在同一个核心上执行。 - Grizzly
这也是真的。顺便说一下,如果你不使用omp parallel for,那么循环中就不会发生并行化。但当然你在一个并行部分内,所以...我能想到的唯一其他可能的解释是你的虚拟机没有硬件支持。你试过其他CPU吗?http://superuser.com/questions/33723/getting-2-processors-to-work-with-virtualbox-on-dual-core-celeron - Nav
我没有。但是正如提到的,可以使用vbox的所有核心,因此缺乏支持似乎不太可能。 - Grizzly

0

如果您正在使用Windows系统,请尝试以下方法:

c:\windows\system32\cmd.exe /C start /affinity F path\to\your\program.exe

/affinity 1 使用CPU0

/affinity 2 使用CPU1

/affinity 3 使用CPU0和CPU1

/affinity 4 使用CPU2

/affinity F 使用所有4个核心

将数字转换为十六进制,并查看从右侧开始要使用的核心位。

您可以在任务管理器中运行时验证亲和性。


虚拟盒确实具有正确的亲和性以使用所有内核(我已经检查过了,而且在我的测试程序多次启动时它如何使用所有内核)。由于我在虚拟盒中使用的是Linux,这并没有真正帮助到我。 - Grizzly

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接