ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

c – C OpenMP并行quickSort

2019-10-05 07:17:17  阅读:313  来源: 互联网

标签:c-3 c quicksort openmp


在C中使用openMP时,我再次陷入困境.这次我正在尝试实现并行快速排序.

码:

#include <iostream>
#include <vector>
#include <stack>
#include <utility>
#include <omp.h>
#include <stdio.h>

#define SWITCH_LIMIT 1000

using namespace std;

template <typename T>
void insertionSort(std::vector<T> &v, int q, int r)
{
    int key, i;
    for(int j = q + 1; j <= r; ++j)
    {
        key = v[j];
        i = j - 1;
        while( i >= q && v[i] > key )
        {
            v[i+1] = v[i];
            --i;
        }
        v[i+1] = key;
    }
}

stack<pair<int,int> > s;

template <typename T>
void qs(vector<T> &v, int q, int r)
{
    T pivot;
    int i = q - 1, j = r;
    //switch to insertion sort for small data
    if(r - q < SWITCH_LIMIT) 
    {
        insertionSort(v, q, r);
        return;
    }

    pivot = v[r];
    while(true)
    {
        while(v[++i] < pivot);
        while(v[--j] > pivot);
        if(i >= j) break;
        std::swap(v[i], v[j]); 
    }
    std::swap(v[i], v[r]);

    #pragma omp critical
    {
        s.push(make_pair(q, i - 1));
        s.push(make_pair(i + 1, r));        
    }
}

int main()
{
    int n, x;
    int numThreads = 4, numBusyThreads = 0;
    bool *idle = new bool[numThreads];
    for(int i = 0; i < numThreads; ++i)
        idle[i] = true;
    pair<int, int> p;
    vector<int> v;
    cin >> n;
    for(int i = 0; i < n; ++i)
    {
        cin >> x;
        v.push_back(x);
    }
    cout << v.size() << endl;
    s.push(make_pair(0, v.size()));

    #pragma omp parallel shared(s, v, idle, numThreads, numBusyThreads, p) 
    {
        bool done = false;
        while(!done) 
        {
            int id = omp_get_thread_num();
            #pragma omp critical
            {
                if(s.empty() == false && numBusyThreads < numThreads) 
                {
                    ++numBusyThreads;
                    //the current thread is not idle anymore
                    //it will get the interval [q, r] from stack
                    //and run qs on it
                    idle[id] = false;
                    p = s.top();                    
                    s.pop();
                }
                if(numBusyThreads == 0)
                {
                    done = true;
                }
            }
            if(idle[id] == false)
            {

                qs(v, p.first, p.second);
                idle[id] = true;
                #pragma omp critical 
                --numBusyThreads;
            }

        }
    }
    return 0;
}

算法:

要将openMP用于递归函数,我使用堆栈来跟踪qs函数应运行的下一个时间间隔.我手动添加第一个间隔[0,size],然后在堆栈中添加新间隔时让线程开始工作.

问题:

程序结束得太早,没有在创建第一组间隔后对数组进行排序([q,i – 1],[i 1,r]如果查看代码.我的猜测是获得工作的线程,考虑默认共享的quicksort函数的局部变量(代码中的qs),因此它们会混淆它们并在堆栈中不添加间隔.

我如何编译:

g++ -o qs qs.cc -Wall -fopenmp

我如何运行:

./qs< in_100000> out_100000

其中in_100000是第1行包含100000的文件,后面是以空格分隔的下一行的100k个整数.

我在linux上使用gcc 4.5.2

谢谢您的帮助,

解决方法:

我实际上没有运行你的代码,但是我发现p上有一个错误,它应该是私有的而不是共享的.并行调用qs:qs(v,p.first,p.second);将在p上进行比赛,导致不可预测的行为. qs的局部变量应该没问题,因为所有线程都有自己的堆栈.但是,整体方法很好.你走在正确的轨道上.

以下是我对并行快速排序实施的一般性评论. Quicksort本身就是令人尴尬的并行,这意味着不需要同步. qs在分区数组上的递归调用令人尴尬地并行.

但是,并行性以递归形式公开.如果您只是在OpenMP中使用嵌套并行性,那么您将在一秒钟内拥有数千个线程.不会加速.因此,大多数情况下您需要将递归算法转换为迭代算法.然后,您需要实现一种工作队列.这是你的方法.而且,这并不容易.

对于您的方法,有一个很好的基准:OmpSCR.您可以在http://sourceforge.net/projects/ompscr/下载

在基准测试中,有几个版本的基于OpenMP的快速排序.他们中的大多数与你的相似.但是,为了增加并行性,必须最小化全局队列上的争用(在代码中,它是s).因此,可能会有一些优化,例如拥有本地队列.尽管算法本身纯粹是并行的,但实现可能需要同步伪像.而且,最重要的是,获得加速很难.

但是,您仍然可以通过两种方式直接在OpenMP中使用递归并行:(1)限制线程总数,以及(2)使用OpenMP 3.0的任务.

这是第一种方法的伪代码(这仅基于OmpSCR的基准):

void qsort_omp_recursive(int* begin, int* end)
{
  if (begin != end) {
    // Partition ...

    // Throttling
    if (...)  {
      qsort_omp_recursive(begin, middle);
      qsort_omp_recursive(++middle, ++end);
    } else {

#pragma omp parallel sections nowait
      {
#pragma omp section
        qsort_omp_recursive(begin, middle);
#pragma omp section
        qsort_omp_recursive(++middle, ++end);
      }
    }
  }
}

要运行此代码,您需要调用omp_set_nested(1)和omp_set_num_threads(2).代码非常简单.我们只是在工作的分工上产生两个线程.但是,我们插入一个简单的限制逻辑来防止过多的线程.请注意,我的实验显示了这种方法的适当加速.

最后,您可以使用OpenMP 3.0的任务,其中任务是逻辑上并发的工作.在上面所有OpenMP的方法中,每个并行构造产生两个物理线程.您可能会说任务与工作线程之间存在硬性的一对一映射.但是,任务将逻辑任务和工作者分开.

因为OpenMP 3.0还不流行,所以我将使用Cilk Plus,它非常适合表达这种嵌套和递归的并行性.在Cilk Plus中,并行化非常简单:

void qsort(int* begin, int* end)
{
  if (begin != end) {
    --end;
    int* middle = std::partition(begin, end,
      std::bind2nd(std::less<int>(), *end));
    std::swap(*end, *middle);

    cilk_spawn qsort(begin, middle);
    qsort(++middle, ++end);
    // cilk_sync; Only necessay at the final stage.
  }
}

我从Cilk Plus的示例代码中复制了这段代码.您将看到一个关键字cilk_spawn是并行化快速排序的一切.我正在跳过Cilk Plus的解释并生成关键字.但是,它很容易理解:两个递归调用被声明为逻辑并发任务.每当递归发生时,都会创建逻辑任务.但是,Cilk Plus运行时(实现高效的工作窃取调度程序)将处理各种脏工作.它最佳地将并行任务排队并映射到工作线程.

请注意,OpenMP 3.0的任务基本上类似于Cilk Plus的方法.我的实验表明,相当不错的加速是可行的.我在8核机器上获得了3~4倍的加速.而且,加速是规模. Cilk Plus的绝对加速比OpenMP 3.0更高.

Cilk Plus(和OpenMP 3.0)的方法和您的方法基本相同:并行任务和工作负载分配的分离.但是,有效实施起来非常困难.例如,您必须减少争用并使用无锁数据结构.

标签:c-3,c,quicksort,openmp
来源: https://codeday.me/bug/20191005/1855510.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有