Я работаю с OpenMP и пытаюсь реализовать распараллеленную версию быстрой сортировки.
Я реализовал версию, в которой в качестве сводной части всегда используется первый элемент, его распараллеленную версию, версию, которая рандомизирует сводную точку, выбирая медиана трех случайных элементов и распараллеленная версия.
Меня беспокоит тот факт, что я получаю хорошую скорость с первой параллелизацией, тогда как второй (несмотря на параллелизацию таким же образом) медленнее, чем последовательный аналог.
В обоих случаях я распараллеливаю только первый вызов функции, я знаю, что мог бы провести более глубокое распараллеливание в трех рекурсиях, но дело в том, что я ожидал получить одинаковое ускорение от двух параллелизаций .
Вот фрагмент кода для функции разделения "наивный" (без рандомизации):
int partition(vector<int>& v, int p, int q){
int x = v[p];
int i = p;
for(int j = p+1; j <= q; j++){
if(v[j] <= x){
i++;
swap(v[i], v[j]);
}
}
swap(v[i], v[p]);
return i;
}
Это функция рандомизированного разделения:
int rand_median(const vector<int>& v, int p, int q){
int n1 = (rand() % (p - q)) + p;
int n2 = (rand() % (p - q)) + p;
int n3 = (rand() % (p - q)) + p;
if((v[n1] <= v[n2] && v[n1] >= v[n3]) || (v[n1] <= v[n3] && v[n1] >= v[n2])) return n1;
else if ((v[n2] <= v[n1] && v[n2] >= v[n3]) || (v[n2] <= v[n3] && v[n2] >= v[n1])) return n2;
else return n3;
}
int rand_partition(vector<int>& v, int p, int q){
int pivot = rand_median(v,p,q);
swap(v[p], v[pivot]);
int x = v[p];
int i = p;
for(int j = p+1; j <= q; j++){
if(v[j] <= x){
i++;
swap(v[i], v[j]);
}
}
swap(v[i], v[p]);
return i;
}
Наивная быстрая сортировка :
void quicksort(vector<int>& v, int s, int e){
if(s >= e) return;
int p = partition(v,s,e);
quicksort(v,s,p-1);
quicksort(v,p+1,e);
}
Распараллеленный наивный quicksor t:
void quick_par(vector<int>& v, int s, int e){
if(s >= e) return;
int p = partition(v,s,e);
omp_set_num_threads(2);
#pragma omp parallel sections
{
#pragma omp section
quicksort(v,s,p-1);
#pragma omp section
quicksort(v,p+1,e);
}
}
Рандомизированная быстрая сортировка:
void quick_rand(vector<int>& v, int s, int e){
if(s >= e) return;
int p = rand_partition(v,s,e);
quick_rand(v,s,p-1);
quick_rand(v,p+1,e);
}
Параллелизированная рандомизированная быстрая сортировка:
void quick_par_rand(vector<int>& v, int s, int e){
if(s >= e) return;
int p = rand_partition(v,s,e);
omp_set_num_threads(2);
#pragma omp parallel sections
{
#pragma omp section
quick_rand(v,s,p-1);
#pragma omp section
quick_rand(v,p+1,e);
}
}
А вот результаты, полученные с помощью теста Google:
bench_ser 887282457 ns 887038659 ns 10 //naive quicksort
bench_par 10738723 ns 10734826 ns 70 //parallelized naive
bench_rand 613904 ns 613686 ns 1039 //randomized quicksort
bench_par_rand 3249751 ns 3248460 ns 213 //parallelized randomized
bench_sort 106110 ns 106074 ns 5952 //std::sort
Как видите, распараллеленная рандомизированная версия медленнее, чем последовательная. Здесь - это вставка всего кода, который я использовал.