如何在C ++

时间:2015-04-22 09:15:23

标签: c++ multithreading

我已经使用std :: sort编写了以下用于多线程排序的多线程程序。在我的程序中,grainSize是一个参数。由于grainSize或可以生成的线程数是系统相关的功能。因此,我没有得到应该将grainSize设置为的最佳值?我在Linux上工作?

 int compare(const char*,const char*)
{
   //some complex user defined logic    
}
void multThreadedSort(vector<unsigned>::iterator data, int len, int grainsize)
{
    if(len < grainsize) 
    {
        std::sort(data, data + len, compare);
    }
    else
    {
        auto future = std::async(multThreadedSort, data, len/2, grainsize);

        multThreadedSort(data + len/2, len/2, grainsize); // No need to spawn another thread just to block the calling thread which would do nothing.

        future.wait();

        std::inplace_merge(data, data + len/2, data + len, compare);
    }
}

int main(int argc, char** argv) {

    vector<unsigned> items;
    int grainSize=10;
    multThreadedSort(items.begin(),items.size(),grainSize);
    std::sort(items.begin(),items.end(),CompareSorter(compare));
    return 0;
}

我需要执行多线程排序。因此,对于排序大型矢量,我可以利用当今处理器中存在的多个核心。如果有人知道一个有效的算法,那么请分享。

我不知道为什么multiThreadedSort()返回的值没有排序,你看到它有一些逻辑错误,那么请让我知道相同的

3 个答案:

答案 0 :(得分:8)

这为您提供了最佳线程数(例如核心数):

unsigned int nThreads = std::thread::hardware_concurrency();

正如您所写,您的有效主题号码不等于grainSize:它将取决于列表大小,并且可能远远超过grainSize。

只需将GrainSize替换为:

unsigned int grainSize= std::max(items.size()/nThreads, 40);

40是任意的,但是要避免启动线程以便排序到少数项目,这将是次优的(开始线程的时间将大于排序少数项目)。它可以通过反复试验进行优化,并且可能大于40。

您至少有一个错误:

multThreadedSort(data + len/2, len/2, grainsize);

如果len为奇数(例如9),则不包括排序中的最后一项。替换为:

multThreadedSort(data + len/2, len-(len/2), grainsize);

答案 1 :(得分:1)

除非你使用完全破坏的编译器(破坏是错误的单词,更好的匹配将是...... shitty ),所以std::future的几次调用应该已经完成​​了为你工作,而不必担心。

请注意,std::future概念异步运行的东西,即可能生成另一个并发执行的线程。愿你,不要忘记你 这意味着它完全是合法的&#34;对于每个未来简单地生成一个线程的实现,从来没有产生任何线程并且只是在wait()内执行任务也是合法的。
在实践中,理智的实现避免按需生成线程,而是使用线程池,根据运行代码的系统,将worker数设置为合理的值。

请注意,尝试使用std::thread::hardware_concurrency()优化线程并不能真正帮助您,因为该函数的措辞过于宽松而无法使用。完全允许实现返回零,或者或多或少任意&#34;最佳猜测&#34;,并且没有机制可以检测返回的值是真值还是废话值。<登记/> 也没有办法区分超线程核心,或NUMA意识等任何东西。因此,即使您认为数字是正确的,它仍然没有多大意义。

更一般地说

问题&#34;什么是正确的线程数&#34;如果有一个很好的普遍答案(我相信没有),很难解决。需要考虑的几件事情:

  1. 10个工作组肯定是方式,太小。产生线程是一件非常昂贵的事情(是的,与流行的看法相反,Linux也是如此),切换或同步线程也很昂贵。尝试&#34;一万&#34;,而不是&#34;数十&#34;。
  2. 超线程内核仅在同一组中的另一个内核停止时执行,最常见的是在内存I / O上(或者,在旋转时,通过显式执行指令,例如Intel上的REP-NOP)。如果没有大量的内存停顿,则在超线程内核上运行的额外线程只会添加上下文切换,但运行速度不会更快。对于类似排序的东西(这些都是关于访问内存的!),你可能会很好地去做那个。
  3. 内存带宽通常饱和一个,有时是两个内核,很少(取决于实际的硬件)。在问题上投掷8或12个线程通常不会增加内存带宽,但会增加共享缓存级别(如L3存在,通常也是L2)和系统页面管理器的压力。对于特殊的排序情况(非常不连贯的访问,许多停顿),情况可能相反。可是,但不一定是。
  4. 由于上述原因,对于一般案例&#34;实际核心数量&#34;或&#34;真核数+ 1&#34;通常是一个更好的建议。
  5. 使用与您的方法相同的较差地点访问大量数据(单线程或多线程)会导致大量缓存/ TLB未命中,甚至可能导致页面错误。这可能不仅可以消除线程并行性的任何收益,而且可能确实执行的速度慢了4-5个数量级。只要想想你的页面错误是什么。在单页故障期间,您可以对一百万个元素进行排序。
  6. 与上述&#34;真实核心加1&#34;相反。一般规则,对于涉及可能长时间阻塞的网络或磁盘I / O的任务,甚至是内核数量的两倍&#34;也许是最好的比赛。所以...真的没有单一的&#34;正确的&#34;规则。
  7. 上面有些自相矛盾的观点的结论是什么?在您实施它之后,一定要确定它是否真的运行得更快,因为这绝不是保证。而且不幸的是,如果没有测量,就没有办法确定什么是最好的。

    另一方面,考虑排序并不是无关紧要的并行化。您已经在使用std::inplace_merge,因此您似乎意识到它不仅仅是&#34;拆分子范围并对它们进行排序&#34;。

    但是考虑一下,你的方法确实完全是什么?您将细分(递归递减)细分到某个深度,然后同时对子范围进行排序,并合并 - 这意味着重写。然后,您正在排序(递归递增)更大的范围并合并它们,直到整个范围被排序。经典的叉子连接。
    这意味着您触摸内存的某些部分以对其进行排序(以不缓存的模式),然后再次触摸它以合并它。然后再次触摸它以对较大的范围进行排序,然后再触摸它以合并更大的范围。对于任何&#34;运气&#34;,不同的线程将在不同的时间访问内存位置,因此您将进行错误共享。
    另外,如果您了解&#34;大数据&#34;与我的相同,这意味着你要覆盖20到30次之间的每个内存位置,可能更频繁。这是一个很大的流量。

    重复读取和写入大量内存,一遍又一遍,,主要瓶颈是内存带宽。看看我去哪了? Fork-join看起来像一个巧妙的东西,而在学术界它可能是......但是它并不确定它在真实机器上运行任何更快(它很可能是慢了很多次。)

答案 2 :(得分:0)

理想情况下,您不能假设系统中运行n * 2个以上的线程。 n是CPU核心数。

现代操作系统使用Hyperthreading的概念。所以,现在一次只能在一个CPU上运行2个线程。

正如另一个答案中所提到的,在C ++ 11中,您可以使用std::thread::hardware_concurrency();获得最佳线程数