C++ OpenMP中的并行合并排序

C++ OpenMP中的并行合并排序,c++,c,multithreading,parallel-processing,openmp,C++,C,Multithreading,Parallel Processing,Openmp,我在一篇论文中看到了并行合并排序算法。代码如下: void mergesort_parallel_omp (int a[], int size, int temp[], int threads) { if ( threads == 1) { mergesort_serial(a, size, temp); } else if (threads > 1) { #pragma omp parallel sections

我在一篇论文中看到了并行合并排序算法。代码如下:

void mergesort_parallel_omp (int a[], int size, int temp[], int threads) 
{  
    if ( threads == 1)       { mergesort_serial(a, size, temp); }
    else if (threads > 1) 
    {
         #pragma omp parallel sections
         {
             #pragma omp section
             mergesort_parallel_omp(a, size/2, temp, threads/2);
             #pragma omp section
             mergesort_parallel_omp(a + size/2, size - size/2, temp + size/2, threads - threads/2);
         }
         merge(a, size, temp); 
    } // threads > 1
}
我在多核上运行它。在树的叶子上,两个线程并行运行。在他们完成工作后,另外两个线程开始,依此类推。即使所有叶节点都有空闲的核心

我认为原因是这个OpenMP代码没有在并行区域内创建并行区域。我说得对吗

我认为原因是OpenMP无法创建并行区域 平行区域内

你可以有一个平行区域的平行区域

OpenMP并行区域可以嵌套在彼此内部。如果嵌套 并行性被禁用,然后由线程创建新团队 在并行区域内遇到并行构造包括 仅对遇到的线程执行。如果启用了嵌套并行, 那么新团队可能由多个线程组成

为了正确运行代码,需要调用
omp\u set\u nested(1)
omp\u set\u num\u threads(2)

通过设置 OMP_嵌套环境变量或调用OMP_set_NESTED()函数


为了获得更好的性能,您可以使用OpenMP任务(可以找到有关的详细信息和示例),如下所示:

void merge(int * X, int n, int * tmp) {
   ...
} 

void mergeSort(int *X, int n, int *tmp)
{  
   if (n < 2) return;
   
   #pragma omp task shared(X) if (n > TASK_SIZE)
   mergeSort(X, n/2, tmp);
   
   #pragma omp task shared(X) if (n > TASK_SIZE)
   mergeSort(X+(n/2), n-(n/2), tmp + n/2);
   
   #pragma omp taskwait
   mergeSortAux(X, n, tmp);
}



int main()
{
   ...
   #pragma omp parallel
   {
      #pragma omp single
      mergesort(data, n, tmp);
   }
} 
未来的改进将在上提供


一个C++版本的并行版本。最终的算法如下所示:

void mergeSortRecursive(vector<double>& v, unsigned long left, unsigned long right) {
   if (left < right) {
      if (right-left >= 32) {
         unsigned long mid = (left+right)/2; 
         #pragma omp taskgroup
         {
            #pragma omp task shared(v) untied if(right-left >= (1<<14))
            mergeSortRecursive(v, left, mid);
            #pragma omp task shared(v) untied if(right-left >= (1<<14))
            mergeSortRecursive(v, mid+1, right);
            #pragma omp taskyield
         }
         inplace_merge(v.begin()+left, v.begin()+mid+1, v.begin()+right+1);
      }else{
         sort(v.begin()+left, v.begin()+right+1);
     }
    }
  }
}


void mergeSort(vector<double>& v) { 
     #pragma omp parallel
     #pragma omp single
     mergeSortRecursive(v, 0, v.size()-1); 
}
void mergeSortRecursive(向量&v、无符号长左、无符号长右){
if(左<右){
如果(右-左>=32){
无符号长中=(左+右)/2;
#布拉格马omp任务组
{

#pragma omp task shared(v)untied if(right-left>=(1这个问题的现代答案是使用任务而不是节。任务是在OpenMP 3.0(2009)中添加的,比嵌套并行和节工作得更好/更容易,因为嵌套并行可能导致超额订阅(活动线程比可用CPU更多),这会导致性能显著降低。对于任务,您有一组与CPU数量匹配的线程,并且将处理这些任务。因此,您不需要手动处理
threads
参数。简单的解决方案如下所示:

// span parallel region outside once outside
void mergesort_omp(...) {
    #pragma omp parallel
    #pragma omp single
    mergesort_parallel_omp(...)
}


void mergesort_parallel_omp (int a[], int size, int temp[]) 
{  
    #pragma omp task
    mergesort_parallel_omp(a, size/2, temp);

    mergesort_parallel_omp(a + size/2, size - size/2, temp + size/2);

    #pragma omp taskwait
    merge(a, size, temp); 
}
但是,为太小的工作块创建任务仍然会有问题,因此根据工作粒度限制并行性非常有用,例如:

void mergesort_parallel_omp (int a[], int size, int temp[]) 
{  
    if (size < size_threshold) {
        mergesort_serial(a, size, temp);
        return;
    }
    #pragma omp task
    mergesort_parallel_omp(a, size/2, temp);

    mergesort_parallel_omp(a + size/2, size - size/2, temp + size/2);

    #pragma omp taskwait
    merge(a, size, temp); 
}
void mergesort\u parallel\u omp(int a[],int size,int temp[])
{  
如果(大小<大小\阈值){
mergesort_系列(a、尺寸、温度);
返回;
}
#pragma-omp任务
合并排序\u并行\u omp(a,尺寸/2,温度);
合并排序\u并行\u omp(a+size/2,size-size/2,temp+size/2);
#pragma omp taskwait
合并(a、大小、温度);
}
// span parallel region outside once outside
void mergesort_omp(...) {
    #pragma omp parallel
    #pragma omp single
    mergesort_parallel_omp(...)
}


void mergesort_parallel_omp (int a[], int size, int temp[]) 
{  
    #pragma omp task
    mergesort_parallel_omp(a, size/2, temp);

    mergesort_parallel_omp(a + size/2, size - size/2, temp + size/2);

    #pragma omp taskwait
    merge(a, size, temp); 
}
void mergesort_parallel_omp (int a[], int size, int temp[]) 
{  
    if (size < size_threshold) {
        mergesort_serial(a, size, temp);
        return;
    }
    #pragma omp task
    mergesort_parallel_omp(a, size/2, temp);

    mergesort_parallel_omp(a + size/2, size - size/2, temp + size/2);

    #pragma omp taskwait
    merge(a, size, temp); 
}