我有两个程序A和B,用g ++ - 5.1和openmp编译。 代码在Scientific Linux 7.1中运行。
g++ <program>.cpp -fopenmp -o <program>
程序(A)使用std :: system调用启动程序(B)。 程序的调用图如下所示:
|- A
|- B
程序B应该使用系统上所有可用的CPU内核,但它只使用一个线程。
如果编译的程序A没有-fopenmp
标志,那么程序B使用所有可用的核心。
即使程序B不再是A的子进程(std::system("setsid ./B &")
),它也只使用一个线程。调用图:
|- B
...
|- A
为什么会出现这种情况?如何在程序A调用时让程序B使用所有内核?
同样奇怪的是,如果调用者没有使用-fopenmp编译,我会得到预期的行为。我尝试过的其他事情:使用execve,posix_spawn生成子进程,在其间有一个bash实例。使用编译器g ++ - 4.8.3也会出现问题。我没有想法。
计划A:A.cpp
#include <cstdlib>
int main(int argc, const char** argv) {
std::system("setsid ./B 100000000000 &");
}
计划B:B.cpp
#include "omp.h"
#include <cstdlib>
#include <iostream>
// ignore the workload, I just need something to spin the CPU
double workload(size_t num_steps)
{
size_t i;
double x=0;
double sum = 0.0;
double step = 1.0 / (double) num_steps;
#pragma omp parallel private(i,x)
{
#pragma omp for reduction(+:sum) schedule(dynamic, 100)
for (i=0; i<num_steps; i=i+1){
x=(i+0.5)*step;
sum = sum + 4.0/(1.0+x*x);
}
}
return step*sum;
}
int main(int argc, const char** argv) {
size_t a = strtoull(argv[1], NULL, 10);
std::cout << a << " " << workload(a) << "\n";
return 0;
}
答案 0 :(得分:2)
大多数OpenMP运行时库的初始化都发生在它们的构造函数中,这些构造函数在流程生命周期的早期就会执行。因此,不可能阻止OMP_PROC_BIND
对程序A的绑定效果,但是可以在产生程序B之前抵消它。有多种方法,但有两种方法立即出现在我的脑海中:
1)在taskset
的调用中使用std::system()
来覆盖子进程的CPU关联掩码:
std::system("taskset -c 0-63 ./B 100000000000");
-c 0-63
参数产生一个64位设置的CPU亲和力掩码,这应该适用于大多数当前的多核系统(除非该程序在Intel Xeon Phi或某些奇特的硬件上运行,例如我们的Bull Coherent Switch耦合胖节点)。显然,如果系统上没有安装taskset
(作为util-linux
的一部分,它将无法正常工作,它应默认安装在许多系统上)。
2)在调用sched_setaffinity(2)
之前,使用pthread_setaffinity_np(3)
或std::system()
重置A的CPU关联掩码。看here寻找灵感。
3)如果你能负担得起外部依赖,hwloc library有一个非常好的API,可以用来获取和操纵CPU亲和力。它也是跨平台的,也适用于Windows。
选项3是最干净的选项,因为您不必在参数taskset
或传递给调度程序功能的CPU集中预先硬编码足够宽的掩码。