使用omp并行技术实现快排加速

快排基本原理:

快速排序可以说是最为常见的排序算法,冒泡排序时间复杂度达到了O(N2),而桶排序容易造成浪费空间。快排(Quicksort)就成为了不错的选择。

1、原理:快排需要找一个数作为基准数,用来参照。(可取第一个数为参照)

        基准数在中间某位置,两端有指针,找到相应数后,交换。

注意:若令第一个数为基准数,先从右往左找,再从左往右找。

2、优点:平均时间复杂度O(NlogN),相比冒泡排序每次交换可以是跳跃式的

排序过程:

OpenMP基本概念
OpenMP是一种用于共享内存并行系统的多线程程序设计方案,支持的编程语言包括C、C++和Fortran。OpenMP提供了对并行算法的高层抽象描述,特别适合在多核CPU机器上的并行程序设计。编译器根据程序中添加的pragma指令,自动将程序并行处理,使用OpenMP降低了并行编程的难度和复杂度。当编译器不支持OpenMP时,程序会退化成普通(串行)程序。程序中已有的OpenMP指令不会影响程序的正常编译运行。

在VS中启用OpenMP很简单,很多主流的编译环境都内置了OpenMP。在项目上右键->属性->配置属性->C/C++->语言->OpenMP支持,选择“是”即可。

OpenMP执行模式
OpenMP采用fork-join的执行模式。开始的时候只存在一个主线程,当需要进行并行计算的时候,派生出若干个分支线程来执行并行任务。当并行代码执行完成之后,分支线程会合,并把控制流程交给单独的主线程。

一个典型的fork-join执行模型的示意图如下:

OpenMP编程模型以线程为基础,通过编译制导指令制导并行化,有三种编程要素可以实现并行化控制,他们分别是编译制导、API函数集和环境变量。

编译制导
编译制导指令以#pragma omp 开始,后边跟具体的功能指令,格式如:#pragma omp 指令[子句[,子句] …]。常用的功能指令如下:

parallel:用在一个结构块之前,表示这段代码将被多个线程并行执行;
for:用于for循环语句之前,表示将循环计算任务分配到多个线程中并行执行,以实现任务分担,必须由编程人员自己保证每次循环之间无数据相关性;
parallel for:parallel和for指令的结合,也是用在for循环语句之前,表示for循环体的代码将被多个线程并行执行,它同时具有并行域的产生和任务分担两个功能;
sections:用在可被并行执行的代码段之前,用于实现多个结构块语句的任务分担,可并行执行的代码段各自用section指令标出(注意区分sections和section);
parallel sections:parallel和sections两个语句的结合,类似于parallel for;
single:用在并行域内,表示一段只被单个线程执行的代码;
critical:用在一段代码临界区之前,保证每次只有一个OpenMP线程进入;
flush:保证各个OpenMP线程的数据影像的一致性;
barrier:用于并行域内代码的线程同步,线程执行到barrier时要停下等待,直到所有线程都执行到barrier时才继续往下执行;
atomic:用于指定一个数据操作需要原子性地完成;
master:用于指定一段代码由主线程执行;
threadprivate:用于指定一个或多个变量是线程专用,后面会解释线程专有和私有的区别。
相应的OpenMP子句为: 


private:指定一个或多个变量在每个线程中都有它自己的私有副本;
firstprivate:指定一个或多个变量在每个线程都有它自己的私有副本,并且私有变量要在进入并行域或任务分担域时,继承主线程中的同名变量的值作为初值;
lastprivate:是用来指定将线程中的一个或多个私有变量的值在并行处理结束后复制到主线程中的同名变量中,负责拷贝的线程是for或sections任务分担中的最后一个线程; 
reduction:用来指定一个或多个变量是私有的,并且在并行处理结束后这些变量要执行指定的归约运算,并将结果返回给主线程同名变量;
nowait:指出并发线程可以忽略其他制导指令暗含的路障同步;
num_threads:指定并行域内的线程的数目; 
schedule:指定for任务分担中的任务分配调度类型;
shared:指定一个或多个变量为多个线程间的共享变量;
ordered:用来指定for任务分担域内指定代码段需要按照串行循环次序执行;
copyprivate:配合single指令,将指定线程的专有变量广播到并行域内其他线程的同名变量中;
copyin:用来指定一个threadprivate类型的变量需要用主线程同名变量进行初始化;
default:用来指定并行域内的变量的使用方式,缺省是shared。
利用omp_set_num_threads()来设置线程数,

利用#pragma omp parallel sections 声明下面大括号中的语句要并行多线程执行;

利用#pragma omp section 分配线程。

代码实现:

#include<stdio.h>
#include<omp.h>
#include<time.h>
#include<stdlib.h>

int Partition(int* data, int start, int end)   //划分数据
{
    int temp = data[start];   //以第一个元素为基准
    while (start < end) {
        while (start < end && data[end] >= temp)end--;   //找到第一个比基准小的数
        data[start] = data[end];
        while (start < end && data[start] <= temp)start++;    //找到第一个比基准大的数
        data[end] = data[start];
    }
    data[start] = temp;   //以基准作为分界线
    return start;
}

void quickSort(int* data, int start, int end)  //并行快排
{
    if (start < end) {
        int pos = Partition(data, start, end);
        #pragma omp parallel sections    //设置并行区域
        {
            #pragma omp section          //该区域对前部分数据进行排序
            quickSort(data, start, pos - 1);
            #pragma omp section          //该区域对后部分数据进行排序
            quickSort(data, pos + 1, end);
        }
    }
}

int main(int argc, char* argv[])
{
    int n = atoi(argv[2]), i;   //线程数
    int size = atoi(argv[1]);   //数据大小
    int* num = (int*)malloc(sizeof(int) * size);

    double starttime = omp_get_wtime();
    srand(time(NULL) + rand());   //生成随机数组
    for (i = 0; i < size; i++)
        num[i] = rand();
    omp_set_num_threads(n);   //设置线程数
    quickSort(num, 0, size - 1);   //并行快排
    double endtime = omp_get_wtime();

    for (i = 0; i < 10 && i<size; i++)//输出前十个元素
        printf("%d ", num[i]);
    printf("\n并行时间:%lfs\n", endtime - starttime);
    return 0;
}

 主要用到的omp加速代码为:

void quickSort(int* data, int start, int end)  //并行快排
{
    if (start < end) {
        int pos = Partition(data, start, end);
        #pragma omp parallel sections    //设置并行区域
        {
            #pragma omp section          //该区域对前部分数据进行排序
            quickSort(data, start, pos - 1);
            #pragma omp section          //该区域对后部分数据进行排序
            quickSort(data, pos + 1, end);
        }
    }
}

运行结果:

Omp快排结果:

仅输出前十个数字。

 编译:g++ ./filename.cpp -o ./filename -fopenmp

运行:./filename n   n为所设置的线程数

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
压缩感知是一种新型的信号采样和压缩方法,可以在不牺牲信号质量的前提下大大降低采样率。其中OMP(Orthogonal Matching Pursuit)算法是一种常见的压缩感知算法。在Matlab上实现使用OMP算法的压缩感知,可以按照以下步骤进行: 1.生成稀疏信号:可以使用randn函数生成服从高斯分布的随机向量,然后使用sort函数将向量按绝对值从大到小排序,取出其中前K个元素作为非零元素,其余元素设为0,即可得到一个稀疏信号。 2.生成测量矩阵:可以使用randn函数生成服从高斯分布的随机矩阵,然后使用Gram-Schmidt正交化方法将其转化为正交矩阵。 3.生成观测向量:将稀疏信号与测量矩阵相乘,得到观测向量。 4.使用OMP算法进行信号恢复:根据观测向量和测量矩阵,使用OMP算法进行信号恢复。具体实现可以参考Matlab中的OMP函数。 5.进行误差分析:将恢复的信号与原始信号进行比较,计算误差并进行分析。 下面是一个基于Matlab的OMP算法实现示例: ``` % 生成稀疏信号 N = 1000; % 信号长度 K = 50; % 非零元素个数 x = zeros(N,1); x(randperm(N,K)) = randn(K,1); % 生成测量矩阵 M = 200; % 观测向量长度 A = randn(M,N); A = orth(A')'; % 生成观测向量 y = A*x; % 使用OMP算法进行信号恢复 tol = 1e-6; % 迭代停止条件 maxiter = 100; % 最大迭代次数 x0 = zeros(N,1); % 初始解 [xhat, idx] = omp(A, y, tol, maxiter, x0); % 进行误差分析 err = norm(x-xhat)/norm(x); disp(['Recovery error: ', num2str(err)]); ``` 其中,omp函数是Matlab自带的OMP算法实现函数,可以直接调用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值