并发快速排序的实现是否正确?

时间:2017-04-05 01:12:01

标签: c++ parallel-processing openmp

我使用OpenMP在C ++中实现了并发快速排序。

#include <omp.h>
#include <iostream>
#include <algorithm>
using namespace std;

void sort(int *a, int low, int high);
int partition(int *a, int low, int high);

class QuickSort {
    private:
        int *arr;
        int len;

    public:
        void init();
        void Sort();
        void Display();
};

int main() {
    cout << "Program implementing Quicksort." << endl;

    QuickSort a;

    a.init();
    a.Sort();
    a.Display();
}

void sort(int *a, int low, int high) {
    if(high < low || low == high)
        return;
    if(high == low+1) {
        if(a[low] > a[high])
            swap(a[low], a[high]);
        return;
    }
    int pivotidx = partition(a, low, high);
    /*for(int i = 0; i < 5; ++i)
        cout << a[i] << " ";
    cout << endl;*/
    cout << "Pivot element has been placed at correct position: " << pivotidx << " by thread " << omp_get_thread_num() << endl;
    #pragma omp parallel sections
    {
        #pragma omp section
        {
            sort(a, low, pivotidx);
        }
        #pragma omp section
        {
            sort(a, pivotidx+1, high);
        }
    }
}

int partition(int *a, int low, int high) {
    int pivot = low;
    int pivotval = a[low];
    int leftpointer = low;
    int rightpointer = high;
    while(leftpointer < rightpointer) {
        while(a[leftpointer] <= a[pivot] && leftpointer <= high)
            ++leftpointer;
        if(leftpointer > high)
            --leftpointer;
        while(a[rightpointer] >= a[pivot] && rightpointer >= low)
            --rightpointer;
        if(rightpointer < low)
            ++rightpointer;
        if(leftpointer < rightpointer)
            swap(a[leftpointer], a[rightpointer]);
    }
    a[low] = a[rightpointer];
    a[rightpointer] = pivotval;
    return rightpointer;
}

void QuickSort::init() {
    cout << "Enter the number of elements in the array: ";
    cin >> len;

    cout << "Enter the elements of the array: ";
    arr = new int[len];
    for(int i = 0; i < len; ++i)
        cin >> arr[i];
}

void QuickSort::Sort() {
    sort(arr, 0, len-1);
}

void QuickSort::Display() {
    cout << "Sorted array is: " << endl;
    for(int i = 0; i < len; ++i)
        cout << arr[i] << " ";
    cout << endl;
}

它正确排序,但我不确定它是否真的在多核上运行。我怎么检查这个?此外,我的并行代码与顶部答案here中的代码非常相似。在最后提到它不能提取比两个线程更多的并行性:如果它用更多线程执行,其他线程没有任何工作要做,只会闲置。为什么会这样?

1 个答案:

答案 0 :(得分:1)

partition中有一个微妙的错误:

    while(a[leftpointer] <= a[pivot] && leftpointer <= high)
    ...
    while(a[rightpointer] >= a[pivot] && rightpointer >= low)

在这两种情况下,您必须更改这些检查的顺序,否则您有时会访问可能超出范围的a[leftpointer] leftpointer > high。类似地,对于第二个while条件。

不要欺骗读者leftpointer不是指针,而是索引!还有其他严重的样式问题,但由于这不是CodeReview,我专注于并行化。

在这里使用并行部分远非理想。例如,您必须启用嵌套并行性,以便同时有两个以上的线程可能处于活动状态。相反,您应该使用OpenMP任务。现在为每个sort调用生成一个任务是不好的,因为它会创建许多微小任务并且开销/工作比率很低。相反,只为足够大的数据块创建任务,并确保在递归中不会发生运行时开销。为此,复杂的第二递归函数是最佳选择:

void sort_serial(int* a, int low, int high)
{
    if (high < low || low == high)
        return;
    if (high == low + 1)
    {
        if (a[low] > a[high])
            swap(a[low], a[high]);
        return;
    }
    int pivotidx = partition(a, low, high);
    sort_serial(a, low, pivotidx);
    sort_serial(a, pivotidx + 1, high);
}

void sort(int* a, int low, int high)
{
    if (high < low || low == high)
        return;
    if (high == low + 1)
    {
        if (a[low] > a[high])
            swap(a[low], a[high]);
        return;
    }
    int pivotidx = partition(a, low, high);

    // This is an arbitrary threshold.
    if (high - low > 1000000)
    {
#pragma omp task
        {
            sort(a, low, pivotidx);
        }
#pragma omp task
        {
            sort(a, pivotidx + 1, high);
        }
    }
    else
    {
        sort_serial(a, low, pivotidx);
        sort_serial(a, pivotidx + 1, high);
    }
}

要完成任务,您必须在某处创建一个并行区域 - 并且通常将其缩小以便单个线程启动,例如,像这样:

void QuickSort::Sort()
{    
#pragma omp parallel
    {
#pragma omp single
        sort(arr, 0, len - 1);
    }
}

对于足够大的输入和良好的阈值选择,这将暴露出可以并行完成的足够工作,但不会产生巨大的开销。

要检查这是如何并行运行的,通常会使用特定于操作的监控工具,例如: Linux上的time。您还可以使用复杂的性能分析工具,它可以详细告诉您线程如何并行执行任务。