OpenMP与C++11线程的比较

32
在下面的例子中,C++11线程需要大约50秒才能执行,但OMP线程只需要5秒。 有什么想法为什么会这样?(我可以向您保证,如果您正在执行真正的工作而不是doNothing,或者以不同的顺序执行它等等,它仍然成立。)我也在一台16核机器上。
#include <iostream>
#include <omp.h>
#include <chrono>
#include <vector>
#include <thread>

using namespace std;

void doNothing() {}

int run(int algorithmToRun)
{
    auto startTime = std::chrono::system_clock::now();

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

    auto endTime = std::chrono::system_clock::now();
    std::chrono::duration<double> elapsed_seconds = endTime - startTime;

    return elapsed_seconds.count();
}

int main()
{
    int cppt = run(1);
    int ompt = run(2);

    cout<<cppt<<endl;
    cout<<ompt<<endl;

    return 0;
}

1
我的猜测是OpenMP足够聪明,可以优化整个循环,因为它是一个NOP。使用“线程”时,您将遭受旋转和拆除所有这些NOP线程的开销。尝试向测试函数添加一些实际代码,看看会发生什么。 - aruisdante
好的,有一件事是你正在使用一个动态调整大小的容器来保存线程;这对性能没有帮助。 - RamblingMad
尝试使用固定大小的数组,并在创建时初始化所有元素。 - RamblingMad
@aruisdante:我已经添加了真实的代码,并且我可以向您保证差异仍然存在(我有很多代码并将其缩小以发布在这里)- 这不是由于NOP引起的。 - user2588666
@CoffeeandCode:我已经这样做了(并且刚刚又尝试了一下),差别微乎其微,因为对thread()的调用会自动调用new。不过你说得很好--但我也可以向你保证,这不会影响性能。 - user2588666
可能归结为:https://dev59.com/X2865IYBdhLWcg3wLbar 在Linux上。 - Ciro Santilli OurBigBook.com
2个回答

38

OpenMP为其Pragmas提供了线程池(也可以在这里这里找到相关信息)。启动和关闭线程是非常昂贵的。OpenMP避免了这种开销,因此它所做的只是实际的工作和最小的共享内存传输执行状态。在你的Threads代码中,你每次迭代都会启动和关闭一组新的16个线程。


谢谢。这肯定是答案,但你会不会觉得外层的for循环需要一个#pragma呢?另外,你怎么知道这是事实--我甚至在文档中都没有找到相关信息,即使在链接的站点上也是如此。我相信你是对的,只是想要验证一下信息。我从未确切地阅读过他们这样做的事实。 - user2588666
看看第二个链接,里面有一些讨论。我可以尝试找到更可靠的文档,我知道我在某个地方明确地读过它。 - aruisdante
这里有另一个与此相关的讨论。基本上,OpenMP标准并没有对此进行定义,但是大多数平台上的大多数实现似乎都会在更高效的情况下这样做。 - aruisdante
再次感谢:)我想这一定是线程池,但令人惊讶的是我找不到任何地方有明确说明。在进一步查找后,我找到了这个。我将在非英特尔机器上尝试并查看它是否仍然适用。你比我快——看起来它基本上在所有实现中都完成了。 - user2588666
PS. 我可以确认这个差异也存在于 AMD 机器上。 - user2588666

2

我尝试了一个在选择正确的线程框架上循环100次的代码,OpenMP用了0.0727毫秒,Intel TBB用了0.6759毫秒,C++线程库用了0.5962毫秒。

我还使用了AruisDante建议的方法;

void nested_loop(int max_i, int band)  
{
    for (int i = 0; i < max_i; i++)
    {
        doNothing(band);
    }
}
...
else if (algorithmToRun == 5)
{
    thread bristle(nested_loop, max_i, band);
    bristle.join();
}

这段代码似乎比您原来的C++ 11线程部分运行时间更短。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接