OpenMP创建了太多线程

时间:2016-05-23 12:49:54

标签: c++ openmp

我不确定为什么OpenMP会使用这么多线程。它似乎与Microsoft实现无关,因为我也尝试过显示相同行为的英特尔库。 我的代码中有一些并行部分是计算绑定的,不应该创建和使用比我有内核更多的线程。但我观察到的是,对于n个启动线程,OpenMP创建了n * Cores线程。这看起来像一个大线程泄漏给我。

如果我执行"小"在服务器上运行的32位应用程序可能会失败,因为1000个OpenMP线程需要2 GB的地址空间已经没有为应用程序留下内存。这不应该发生。我期望从最先进的线程池重用其线程并带走不再使用的线程。

我曾尝试使用omp_set_num_threads(8)将线程池大小限制为8个内核,但这似乎只限制了每个启动线程实例的线程数。我做错了还是OpenMP不是故意这样用的?

在我的8核机器上,我的AsyncWorker类中的5个启动线程将分配由OpenMP创建的38个线程。我希望只创建8个线程,这些线程应该在所有5个启动线程中重用。

enter image description here

#include <atomic>
#include <thread>
#include <omp.h>
#include <chrono>
#include <vector>
#include <memory>

class AsyncWorker {
private:
    std::vector<std::thread> threads;

public:
    AsyncWorker()
    {
    }

    void start() // add one thread that starts an OpenMP parallel section
    {
        threads.push_back(std::thread(&AsyncWorker::threadFunc, this));
    }

    ~AsyncWorker()
    {
        for (auto &t : threads)
        {
            t.join();
        }
    }

private:
    void threadFunc()
    {
        std::atomic<int> counter;

        auto start = std::chrono::high_resolution_clock::now();
        std::chrono::milliseconds durationInMs;

        while (durationInMs.count() <5000l) 
        {
        // each instance seems to get its own thread pool. 
        // Why? And how can I limit the threadpool to the number of cores and when will the threads be closed?
#pragma omp parallel  
            {
                counter++;
                auto stop = std::chrono::high_resolution_clock::now();
                durationInMs = std::chrono::duration_cast<std::chrono::milliseconds>(stop - start);
            }
        }
    }

};

int main() {
    //omp_set_dynamic(0);
    //omp_set_nested(0);
    //omp_set_num_threads(8);

    {
        AsyncWorker foo;

        foo.start();  // 1
        foo.start();  // 2 
        foo.start();  // 3
        foo.start();  // 4
        foo.start();  // 5

        system("pause");
    }

    return 0;
}

2 个答案:

答案 0 :(得分:3)

OpenMP使用的线程数是按并​​行部分设置的,并且您同时生成5个并行部分。因此你得到40个主题。

似乎您正在寻找基于任务的并行性。在OpenMP中,您可以通过启动并行区域然后根据​​需要创建任务来实现此目的。从头顶开始,这个模式的代码写得像这样:

// Start parallel region
#pragma omp parallel
{
  // Only let a single thread create the tasks
  #pragma omp single
  {
     for(int i = 0; i < 40; i++)
     {
       // Actually create the task that needs to be performed
       #pragma omp task
       {
         heavy_work();
       }
     }
  }
}

这样你只能有8个并行的线程。

答案 1 :(得分:2)

OpenMP并不意味着以这种方式使用。混合使用OpenMP和其他线程方法是灾难的一种方法,除非非常仔细地进行。即便如此,结果也是不可预测的。 OpenMP标准故意不再定义这种互操作性,供应商可以按照自己认为合适的方式自由提供(如果他们认为合适)。

omp_set_num_threads(8)没有按照您的想法行事。它设置当没有num_threads()子句时当前线程遇到的并行区域的线程数。此外,omp_set_nested(0)已经(或可能)没有任何影响,因为您没有从OpenMP线程启动并行区域,而是从C ++ 11线程启动。可以通过OMP_THREAD_LIMIT环境变量设置OpenMP线程总数的全局限制,但这仅在OpenMP 3.0及更高版本中可用,并且MSVC(永远?)陷入OpenMP 2.0时代。

可能的行动方针是:

  • 使用共享队列结构和OpenMP线程实现您对OpenMP 2.0的自己的任务,这些线程在循环中出现队列
  • 将OpenMP替换为英特尔Threading Building Blocks - 它是开源的,支持Windows,Linux,OS X和Android上的各种编译器
  • 使用来自Concurrency Runtime的Microsoft的PPL替换OpenMP,它基本上提供了TBB的非可移植子集