【问题标题】:Externally called OpenMP program runs with only one thread if called by another OpenMP program如果被另一个 OpenMP 程序调用,外部调用的 OpenMP 程序仅使用一个线程运行
【发布时间】:2016-05-03 12:22:28
【问题描述】:

我有两个程序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 &amp;")),它也只使用一个线程。调用图:

|- 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;
}

【问题讨论】:

  • 改用std::system("cat /proc/self/status"); 并查看Cpus_allowed_list 的值。也许启用了线程固定。检查环境变量集中是否存在OMP_PROC_BINDOMP_PLACESGOMP_CPU_AFFINITY
  • @HristoIliev 非常感谢这个提示。 Cpus_allowed_list = 1、OMP_PROC_BIND=TRUE 和 OMP_PLACES 和 GOMP_CPU_AFFINITY 未设置。 OMP_PROC_BIND=FALSE 似乎可以解决问题。但是现在我们调用了程序 (A),像这样 OMP_PROC_BIND=FALSE ./A。有没有办法在运行时设置它?
  • 运行 A 时不要设置 OMP_PROC_BIND。使用 setenv(3)OMP_PROC_BIND 设置为 TRUE 在生成 B 之前从 A 内部或使用 execle(2)/execvpe(2) 传递修改子进程的环境。
  • @HristoIliev 非常感谢。这行得通。不幸的是,我无法将您的评论标记为答案。无论如何,您能想到如何避免为工具 A 编写包装脚本来取消设置 OMP_PROC_BIND?
  • 你去 - 我在答案中总结了几种方法来做到这一点,而无需与环境打交道。

标签: c++ c linux process openmp


【解决方案1】:

大多数 OpenMP 运行时库的初始化发生在它们的构造函数中,它们在进程生命周期的早期执行。因此,无法阻止OMP_PROC_BIND 对程序 A 的绑定影响,但可以在生成程序 B 之前抵消它。有多种方法,但我立即想到了两种:

1) 在对std::system() 的调用中使用taskset 覆盖子进程的CPU 亲和掩码:

std::system("taskset -c 0-63 ./B 100000000000");

-c 0-63 参数生成一个设置为 64 位的 CPU 关联掩码,这对于大多数当前一代的多核系统来说应该是好的(除非程序在 Intel Xeon Phi 或一些奇特的硬件上运行,比如我们的 Bull Coherent Switch 耦合胖节点)。显然,如果系统上没有安装taskset(作为util-linux 的一部分,它应该默认安装在许多系统上),它将无法工作。

2) 在调用std::system() 之前,使用sched_setaffinity(2)pthread_setaffinity_np(3) 重置A 的CPU 亲和掩码。寻找灵感here

3) 如果你能负担得起外部依赖,hwloc library 有一个非常好的 API,可用于获取和操作 CPU 亲和性。它也是跨平台的,也可以在 Windows 上运行。

选项 3 是最干净的选项,因为您不必预先在参数中将足够宽的掩码硬编码到 taskset 或传递给调度程序函数的 CPU 集中。

【讨论】:

    猜你喜欢
    • 2023-03-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-10
    • 1970-01-01
    相关资源
    最近更新 更多