【问题标题】:Why is OpenMP outperforming threads?为什么 OpenMP 的性能优于线程?
【发布时间】:2014-06-08 23:11:35
【问题描述】:

我一直在 OpenMP 中调用它

#pragma omp parallel for num_threads(totalThreads)
for(unsigned i=0; i<totalThreads; i++)
{
workOnTheseEdges(startIndex[i], endIndex[i]);
}

这在 C++11 std::threads 中(我相信那些只是 pthreads)

vector<thread> threads;
for(unsigned i=0; i<totalThreads; i++)
{
threads.push_back(thread(workOnTheseEdges,startIndex[i], endIndex[i])); 
}
for (auto& thread : threads)
{
 thread.join();
}

但是,OpenMP 实现是速度的 2 倍——更快!我本来希望 C++11 线程更快,因为它们更底层。注意:上面的代码不仅被调用了一次,而且可能在一个循环中被调用了 10,000 次,所以也许这与它有关?

编辑:为了澄清,在实践中,我要么使用 OpenMP 要么使用 C++11 版本——而不是两者。当我使用 OpenMP 代码时,需要 45 秒,而当我使用 C++11 时,需要 100 秒。

【问题讨论】:

  • 大概OpenMP不会生成数千个线程...
  • 我的水晶球无法显示totalThreads 的值是多少,您的CPU 有多少个内核/硬件线程,startIndex 的大小是多少以及执行@ 需要多少时间987654326@一次。
  • 他们没有做同样的事情。 OpenMP 版本在 16 个线程上分配 10,000 个任务。 C++11 版本在 10,000 个线程上运行 10,000 个任务。线程很昂贵,拥有比内核更多的线程甚至更昂贵。您不能只在每个小任务上抛出新线程(除非您碰巧有 10,000 个左右的内核来运行它们)。 OpenMP 版本会为您解决这个问题。
  • @user2588666:你说“上面的代码是循环调用的”。每次调用时,std::thread 版本都会创建 totalThreads new 线程,但 OpenMP 每次循环执行时都会重用相同的 16 个线程。
  • @user2588666:Visual Studio 实现 std::async 以重用相同的线程。除此之外,您必须自己管理 16 个线程(这对您的情况来说非常容易。将 threads 向量设为静态:coliru.stacked-crooked.com/a/3fdad471c0c26d41

标签: c++ multithreading c++11


【解决方案1】:

totalThreads 在您的 OpenMP 版本中来自哪里?我敢打赌不是startIndex.size()

OpenMP 版本将请求排入totalThreads 工作线程。看起来 C++11 版本创建了 startIndex.size() 线程,如果这是一个很大的数字,这会涉及到荒谬的开销。

【讨论】:

  • 很抱歉造成混乱,但 startIndex.size() 等于 totalThreads。
【解决方案2】:

考虑以下代码。 OpenMP 版本运行时间为 0 秒,而 C++11 版本运行时间为 50 秒。这不是因为函数是 doNothing,也不是因为向量在循环中。可以想象,c++11 线程是在每次迭代中创建然后销毁的。另一方面,OpenMP 实际上实现了线程池。它不在标准中,但在 Intel 和 AMD 的实现中。

for(int j=1; j<100000; ++j)
{
    if(algorithmToRun == 1)
    {
        vector<thread> threads;
        for(int i=0; i<16; i++)
        {
            threads.push_back(thread(doNothing));
        }
        for(auto& thread : threads) thread.join();
    }
    else if(algorithmToRun == 2)
    {
        #pragma omp parallel for num_threads(16)
        for(unsigned i=0; i<16; i++)
        {
            doNothing();
        }
    }
}

【讨论】:

  • 不是在j 的每次迭代中创建/销毁线程,而是可能有一种方法可以在主循环之外定义它们并为每个j 重新启动它们。
猜你喜欢
  • 2012-10-07
  • 2018-09-22
  • 1970-01-01
  • 2013-01-14
  • 2013-05-14
  • 1970-01-01
  • 1970-01-01
  • 2021-03-27
  • 2014-04-30
相关资源
最近更新 更多