【发布时间】:2017-12-19 20:55:35
【问题描述】:
在 OpenMP 中,我可以如下创建一堆任务并使用一些固定数量的线程异步运行它们:
#pragma omp parallel
{
#pragma omp single
{
for (int i = 0; i < 1000; i++) {
#pragma omp task
f(i);
} } }
在 C++11 中,我可以做一些事情不太一样 std::async:
std::vector<std::future> futures;
for (int i = 0; i < 1000; i++) {
auto fut = std::async(f, i);
futures.push_back(std::move(fut));
}
...
for (auto & fut : futures) {
auto res = fut.get();
// do something with res
}
我担心的是效率。如果我是正确的,在 OpenMP 中,任务存储在某个任务池中,然后分发到线程(由 OpenMP 运行时自动)。
在 C++ 中,在调用 std::async 时,运行时决定是在新线程中异步运行 f(i),还是将其运行推迟到调用 std::future::get 的时间点。
因此,无论是运行时
- 创建 1000 个线程并同时运行它们,
- 或创建更少的线程,但随后将在主线程中(在最终循环中)按顺序执行一些
f(i)调用。
这两个选项的效率似乎普遍低于 OpenMP 所做的(创建许多任务并在固定数量的线程中同时运行它们)。
有没有办法获得与 OpenMP 任务通过 C++ 线程提供的行为相同的行为?
更新
我使用以下代码进行了一些测量:https://wandbox.org/permlink/gLCFPr1IjTofxwQh 在 12C Xeon E5 CPU 上使用 GCC 7.2 和 -O2 编译:
- 具有 12 个线程的 OpenMP 运行时:12.2 [s]
- C++ 线程运行时:12.4 [s]
(多次运行的平均值)。它们看起来几乎是一样的。
但是,我也对 500,000 个任务 (n) 和其中的 1,000 次迭代 (m) 进行了相同的尝试,然后时间差异很大:
- 具有 12 个线程的 OpenMP 运行时:15.1 [s]
- C++ 线程处理运行时间:175.6 [s]
更新 2
我测量了创建了多少次新线程(按照这个答案插入 pthread_create 调用:https://stackoverflow.com/a/3709027/580083):
第一个实验(20,000 个任务,20,000 次迭代):
- 具有 12 个线程的 OpenMP 运行时:11
- C++ 线程处理运行时:20,000
第二次实验(500,000 个任务,1,000 次迭代):
- 具有 12 个线程的 OpenMP 运行时:11
- C++ 线程处理运行时:32,744
【问题讨论】:
-
实现可以选择使用线程池实现
std::async()。似乎 libstdc++ 使用了一个线程池(基于性能测量而不是查看代码)。 -
@DietmarKühl 你能分享你的绩效测量吗?看起来很有趣。
-
@Zulan:并不是特别令人兴奋:当查看我的Parallel Algorithms 演示文稿的数据时,尤其是使用
std::thread和std::async()手工制作的for_each,你可以看到它的性能类似于使用线程池的性能。在我上次测试行为时,其他实现的行为完全不同。 -
如果您对测量 C++ 任务系统的性能感兴趣,那么您可能还想查看线程构建块 (TBB) threadingbuildingblocks.org(尽管 Intel 品牌它是 Apache 许可并在许多不同的架构(ARM、Power、SPARC、...)
-
@DietmarKühl 根据我的第二次更新,libstdc++ 似乎没有使用线程池。我认为这甚至不可能,请参阅祖兰的回答。实现可以延迟调用策略选择,但它仍然需要创建新线程或同步运行任务。
标签: c++ c++11 asynchronous task openmp