我已经使用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()返回的值没有排序,你看到它有一些逻辑错误,那么请让我知道相同的
答案 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;如果有一个很好的普遍答案(我相信没有),很难解决。需要考虑的几件事情:
上面有些自相矛盾的观点的结论是什么?在您实施它之后,一定要确定它是否真的运行得更快,因为这绝不是保证。而且不幸的是,如果没有测量,就没有办法确定什么是最好的。
另一方面,考虑排序并不是无关紧要的并行化。您已经在使用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();
获得最佳线程数