多线程快速排序堆栈溢出

时间:2012-01-07 08:16:23

标签: c recursion stack-overflow quicksort

我终于完成了一个自制的quicksort版本,用char字符串成员对结构数组进行排序。我没有使用stdlib qsort的原因是它在超快速机器上需要超过5分钟。

我有12个物理和24个逻辑核心(双xeon 5690),以及192 GB(是,GB,而不是MB)的ram,所以我想我可以通过编写quicksort的多线程版本来使用它。但是我得到了一个堆栈溢出异常,大概是因为每次递归在堆栈上创建的struct s_stream。我有超过2,400,000个字符串要排序,所以我只能想象递归必须有多深(如果深度是正确的术语)。

我无法真正缩小结构。我应该放弃这个并寻找另一种排序算法吗?如果是这样,哪一个?

struct s_stream {

    char name[100];
    int avg;
    int current;
    int currentY;
    int marrayIndex;

    int xy[2500];
    int zz[2500];

}

    void quickSort(struct s_stream items[], int left, int right)
    {
      int i, j;
      struct s_stream temp;

      i = left;
      j = right;
      temp = items[(left+right)/2];

      do {
            while((strcmp(items[i].name, temp.name) < 0) && (i < right)) { i++; }
            while((strcmp(items[j].name, temp.name) > 0) && (j > left)) {  j--; }
        if(i <= j)
        {
            temp = items[i];
            items[i] = items[j];
            items[j] = temp;

            i++;
            j--;
        }
  } while(i <= j);


    #pragma omp parallel sections
    {
        #pragma omp section
        if(left < j) { quickSort(items, left, j);}

        #pragma omp section
        if(i < right) { quickSort(items, i, right); }
     }
}

5 个答案:

答案 0 :(得分:2)

这种大规模的并行性并不是非常有益。调度的额外开销开始超过更多线程的好处。通过增加超过物理内核数量的线程数,可以看到很少的收益。

正如Basile所提到的,您可以考虑在数组的每个12位上并行使用stdlib的qsort,然后将这些块合并到一起。

可能会扼杀你的表现的是结构的大小。 20K足够大,你破坏了你的参考地点,而在现代处理器上,缓存很重要,这对性能来说是致命的。将xyzz更改为动态分配可能会带来巨大的性能提升,也可以对指针数组进行排序。

答案 1 :(得分:0)

排序算法最好是 O(n log n)(并且不能更好!)。

您可以对指向项目的指针进行排序,而不是对项目本身进行排序。这应该可以解决堆栈溢出问题并且可能更快(每个项目的数据量不是很大)。

有很多关于排序的书籍。阅读着名的Knuth算法书籍,并阅读其他一些好的算术书籍。还有用于排序的并行算法(我不太了解它们,但有相当多的文献)。排序可能仍然是一个研究课题,他们是会议和博士。

您可以为您的示例,加速一点,将数组切成较小的块,在单独的线程中并行排序每个块,并使用mergesort合并已排序的部分结果。

答案 2 :(得分:0)

好像你的用完了。解决问题的一种方法可能是增加堆栈,我认为你应该检查编译器手册,了解如何执行此操作。

另一种方法是在没有递归的情况下实现它,这是一个示例实现http://www.codeproject.com/KB/recipes/QuickSortWithoutRecursion.aspx

答案 3 :(得分:0)

如何减少堆栈消耗的一种可能性是将s_stream结构更改为:

struct s_stream {
    char name[100];
    int avg;
    int current;
    int currentY;
    int marrayIndex;

    // Instead of int xy[2500]
    int *xy;  // Allocated via malloc()
    int *zz;  // Allocated via malloc()
}

另一种选择是替换

struct s_stream temp;

struct s_stream *temp = malloc(sizeof(s_stream));

并在退出free(temp);函数时执行quickSort

快速排序实现中的堆栈消耗取决于数据透视表的选择。因此,如何使堆栈不那么深的另一个选择是使用更好的算法进行枢轴选择。

使用do { ... } while(i <= j);循环完成快速排序功能后,只要i等于j,就可以更新items[i].nameitems[j].name 。如果(j-left)的值与(right-i)的值相差太大,这可能很有用。

如果被排序的字符串很可能具有长公共前缀,则可以通过在排序字符串之前预处理字符串来提高字符串比较的性能。

答案 4 :(得分:0)

不要使用递归,请找一个体面的算法迭代版本。您总是可以在迭代算法中转换递归算法。不要试图尽早优化你的代码(&#34;那根源于邪恶&#34;正如Knuth所说:-)),首先让我们尝试一下香草快速排序。

更重要的是:对数据进行排序,而不是实际数据!!!!