我使用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中的代码非常相似。在最后提到它不能提取比两个线程更多的并行性:如果它用更多线程执行,其他线程没有任何工作要做,只会闲置。为什么会这样?
答案 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
。您还可以使用复杂的性能分析工具,它可以详细告诉您线程如何并行执行任务。