OpenMP 创建了太多线程
OpenMP creates too many threads
我不确定为什么 OpenMP 使用这么多线程。它似乎与 Microsoft 实现无关,因为我也尝试过显示相同行为的 Intel 库。
我的代码中有一些并行部分是受计算限制的,不应创建和使用比我拥有的内核更多的线程。但我观察到的是,对于 n 个启动线程,OpenMP 创建了 n*Cores 线程。对我来说,这看起来像是一个大线程泄漏。
如果我在服务器上执行 "small" 32 位应用程序 运行 它可能会失败,因为 1000 个 OpenMP 线程需要 2 GB 地址 space 已经没有为应用程序留下内存。那不应该发生。我希望最先进的线程池能够重用其线程并删除不再使用的线程。
我曾尝试使用 omp_set_num_threads(8) 将线程池大小限制为 8 个内核,但这似乎只限制了每个启动线程实例的线程数。是我做错了还是 OpenMP 不应该以这种方式使用?
在我的 8 核机器上,我的 AsyncWorker 中的 5 个启动线程 class 将分配由 OpenMP 创建的 38 个线程。我希望只创建 8 个线程,并且应该在所有 5 个启动线程中重复使用这些线程。
#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;
}
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 个线程并行工作。
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 执行您自己的任务,使工作项出队
- 用 Intel Threading Building Blocks 替换 OpenMP - 它是开源的,支持 Windows、Linux、OS X 和 Android
- 用 Concurrency Runtime 中的 Microsoft PPL 替换 OpenMP,它基本上提供了 TBB
的不可移植子集
我不确定为什么 OpenMP 使用这么多线程。它似乎与 Microsoft 实现无关,因为我也尝试过显示相同行为的 Intel 库。 我的代码中有一些并行部分是受计算限制的,不应创建和使用比我拥有的内核更多的线程。但我观察到的是,对于 n 个启动线程,OpenMP 创建了 n*Cores 线程。对我来说,这看起来像是一个大线程泄漏。
如果我在服务器上执行 "small" 32 位应用程序 运行 它可能会失败,因为 1000 个 OpenMP 线程需要 2 GB 地址 space 已经没有为应用程序留下内存。那不应该发生。我希望最先进的线程池能够重用其线程并删除不再使用的线程。
我曾尝试使用 omp_set_num_threads(8) 将线程池大小限制为 8 个内核,但这似乎只限制了每个启动线程实例的线程数。是我做错了还是 OpenMP 不应该以这种方式使用?
在我的 8 核机器上,我的 AsyncWorker 中的 5 个启动线程 class 将分配由 OpenMP 创建的 38 个线程。我希望只创建 8 个线程,并且应该在所有 5 个启动线程中重复使用这些线程。
#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;
}
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 个线程并行工作。
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 执行您自己的任务,使工作项出队
- 用 Intel Threading Building Blocks 替换 OpenMP - 它是开源的,支持 Windows、Linux、OS X 和 Android
- 用 Concurrency Runtime 中的 Microsoft PPL 替换 OpenMP,它基本上提供了 TBB 的不可移植子集