使用Pthreads并行化Quicksort无法获得任何加速

时间:2010-06-07 13:44:16

标签: c pthreads quicksort

我正在使用Pthreads为列表分成左右两半(小于和大于枢轴)后为每个分区创建新的胎面。我递归执行此操作,直到达到允许的最大线程数。

当我使用printfs来跟踪程序中发生的事情时,我清楚地看到每个线程并行执行其委托工作。但是,使用单个进程始终是最快的。一旦我尝试使用更多线程,完成几乎双倍所需的时间,并随着线程数量不断增加。

我可以在运行它的服务器上使用最多16个处理器。

算法如下: 通过将元素与枢轴进行比较,将数组拆分为右侧和左侧。 为右侧和左侧启动一个新线程,并等待线程重新加入。 如果有更多可用线程,则可以递归创建更多线程。 每个帖子都会等待孩子加入。

一切对我来说都很有意义,排序效果非常好,但是更多的线程让它变得非常慢。

我尝试为每个分区设置最小数量的元素,以便启动一个线程(例如50000)。

我尝试了一种方法,当线程完成时,它允许启动另一个线程,这导致数百个线程开始和完成。我认为开销太大了。所以我摆脱了它,如果一个线程完成执行,没有创建新的线程。我获得了更多的加速,但仍然比单个过程慢很多。

我使用的代码如下。

http://pastebin.com/UaGsjcq2

有没有人知道我可能做错了什么?

4 个答案:

答案 0 :(得分:5)

启动一个线程有相当大的开销。你可能最好创建一个具有固定数量线程的线程池,以及一个线程安全队列来为线程排队作业。线程等待队列中的项目,处理该项目,然后等待另一个项目。如果你想真正做到这一点,这应该是一个优先级队列,其顺序基于分区的大小(所以你总是先排序最小的分区,以防止队列大小过多)。

这至少可以减少启动线程的开销 - 但这仍然不能保证你获得比单线程版本更好的性能。特别是,快速排序涉及CPU本身的足够少的工作,它可能几乎完全受带宽到内存的约束。一次处理多个分区可能会影响缓存局部性,以至于在任何情况下都会失去速度。

答案 1 :(得分:1)

首先猜测创建,销毁,特别是同步你的线程将会消耗掉你可能获得的收益,这取决于你正在排序的元素数量。我实际上猜测需要花费很长时间来弥补开销,而且可能永远不会弥补。

由于你的排序方式,你有1个线程等待另一个等待另一个...你开始并没有真正得到那么多的并行性。你最好使用一个更线性的排序,也许就像Radix一样,用更多的数据来分割线程。这仍然有一个线程等待其他人很多,但至少线程在同一时间做更多的工作。但是,即使这样,我也不认为线程会帮助太多。

答案 2 :(得分:1)

我只是快速查看您的代码。我得到了一个评论。 你为什么要使用锁。 如果我明白你在做什么就像:

quickSort(array)
{
    left, right = partition(array);
    newThread(quickSort(left));
    newThread(quickSort(right));
}

你不应该需要锁定。 通常,每次调用快速排序都不会访问数组的其他部分。 所以不涉及共享。

答案 3 :(得分:0)

除非每个线程在单独的处理器或核心上运行,否则它们不会真正并发运行,并且上下文切换时间将非常重要。线程数应限制为可用执行单元的数量,即使这样,您也必须相信操作系统会将它们分配到单独的处理器/核心,如果它们也用于其他进程,则可能无法执行。

此外,您应该使用静态线程池,而不是动态创建和销毁线程。创建/销毁线程包括从堆中分配/释放堆栈,这是非确定性的并且可能非常耗时。

最后是服务器上的16个处理器还是虚拟机?它们是专门分配给您的流程吗?