例如:
#pragma omp parallel num_threads(2)
{
#pragma omp single
QuickSort(arr, 0, arr.length, cuttoff);
}
据我所知#pragma omp single
使代码串行化。只有一个进程将执行QuickSort()。上面的代码有问题吗?
答案 0 :(得分:2)
此方案通常由任务并行使用,首先,您需要创建固定数量的线程,其次,您定义一些任务,然后将其分发到OpenMP运行时的这些线程。任务的定义(通过#pragma omp task
或#pragma omp section
)必须在并行部分内完成。但是,有些情况,例如在快速排序中,从多个线程定义任务没有意义;要仅从单个线程定义它们,请使用#pragma omp single
。
快速排序的并行化并非易事。除了任务,您还需要在递归的顶层并行执行 partitioning 。这是通过嵌套并行性实现的OpenMP。
考虑一下,例如,quicksort和8个核心:
#pragma omp parallel
)。quicksort
(#pragma omp single
)。quicksort
内,您调用partition
,它通过嵌套并行性将单个线程拆分为8个线程,并且并行执行分区。#pragma omp task
)并通过调用quicksort
以递归方式调用它们。quicksort
函数。此案例仅描述最高级别的递归。在较低级别,您需要关心负载平衡,因为两个分区可能具有不同的大小。 (在第二级,有两个线程同时执行partition
。但是,由于两个分区的大小通常不同,例如,您将第一个线程拆分为3个线程,将第二个线程拆分为5个线程以利用所有8核。基本上你希望尽可能多地利用所有这些核心,这对于多线程快速排序开发者来说是一个相当大的挑战。)
高效实施还将采用尾调用优化,其中只创建1个任务而不是2个;它节省了大量的堆栈空间并避免了许多call
指令。低于某个阈值,然后切换到顺序快速排序(或者更改为快速排序,插入排序和堆栈的某种组合)。
为了说明,这是使用OpenMP并行快速排序的简单实现,用于对整数数组进行排序:
void par_qs_rec(int* a, long lo, long hi) {
if (lo < hi) {
long p = partition(a, lo, hi);
#pragma omp task
par_qs_rec(a, lo, p - 1);
#pragma omp task
par_qs_rec(a, p + 1, hi);
}
}
void par_qs(int* a, long lo, long hi) {
#pragma omp parallel
{
#pragma omp single
par_qs_rec(a, lo, hi); // (1)
}
}
如果没有#pragma omp single
会发生什么情况,即par_qs_rec
的所有主题是否会调用初始(1)
?