openMP简介

openMP是一个编译器指令和库函数的集合,主要是为共享式存储计算机上的并行程序设计使用的。

  • 当计算机升级到多核时,程序中创建的线程数量需要随CPU核数变化,如在CPU核数超过线程数量的机器上运行,则不能很好的完全利用机器性能,虽然通过可以通过操作系统的API创建可变化数量的线程,但是比较麻烦,不如openMP方便
  • 操作系统API创建线程时,需要线程函数入口,如pthread编程。对于同一函数或者同一循环内的并行非常不利,函数入口非常之多,而openMP不需要函数入口。
  • 现在主流的操作系统的API 互不兼容,移植性非常差。openMP是标准规范,支持它的编译器都执行同一套标准,很好的解决了这个问题。

openMP的库函数和指令

指令的格式: #pragma omp 指令 [字句]

常见库函数:

指令 含义 库函数 含义
parallel 所表示的代码将被多个线程并行执行 omp_set_num_threads(parameter) 设置线程数目
parallel for 对for循环进行拆分并行 omp_get_threads_num() 返回线程号
barrier 执行到barrier时,等待所有线程执行完,再往下执行 omp_get_num_threads() 返回并行区域中的活动线程个数
master / single 指定代码块由主线程/随机一个单线程执行 omp_get_num_procs() 返回运行本线程的处理器个数
parallel sections section语句用在sections语句里面,将sections语句里的代码分成几个不同的段,每段都并行执行 omp_init_lock (parameter) 初始化一个简单的锁
ordered 指定并行区域的循环按顺序执行 omp_set_lock(parameter) 上锁
critical 用在代码临界区之前,让线程互斥的访问资源 omp_unset_lock(parameter) 解锁
omp_destroy_lock(parameter) 关闭一个锁

openMP指令和库函数的用法示例

parallel :

#include "omp.h"    //openmp的头文件
#include "stdio.h"
#define NUM_THREADS 4
int main()
{
int i ;
omp_set_num_threads(NUM_THREADS) ; //设置线程的个数
#pragma omp parallel
{
// 被parallel所表示的这个区域叫做并行块,每个线程都会执行这个块中的所有代码
printf ("hello world! \n");
for (i=0;i<5;i++)
printf("i=%d,thread = %d\n",i,omp_get_thread_num());
}
}
hello world!
i=0,thread = 0
i=1,thread = 0
i=2,thread = 0
i=3,thread = 0
i=4,thread = 0
hello world!
i=0,thread = 3
i=1,thread = 3
i=2,thread = 3
i=3,thread = 3
i=4,thread = 3
hello world!
i=0,thread = 1
i=1,thread = 1
i=2,thread = 1
i=3,thread = 1
i=4,thread = 1
hello world!
i=0,thread = 2
i=1,thread = 2
i=2,thread = 2
i=3,thread = 2
i=4,thread = 2

parallel for :

牵扯到for循环时,往往需要用到parallel for指令。

#include "omp.h"
#include "stdio.h"
#define NUM_THREADS 3
int main()
{
int i,j,k ;
omp_set_num_threads(NUM_THREADS);
#pragma omp parallel for
//此并行块中的for循环,把for循环体中的代码并行执行,即整个for循环被拆分为多个线程执行
//注意,parallel是连for循环一起并行
for (i = 0;i<5;i++)
printf("i= %d,thread=%d\n",i,omp_get_thread_num());
for (j=0;j<4;j++) //普通循环,仅一个线程
printf("j= %d,thread=%d\n",j,omp_get_thread_num());
return 0;
}
i= 0,thread=0
i= 1,thread=0
i= 4,thread=2
i= 2,thread=1
i= 3,thread=1
j= 0,thread=0
j= 1,thread=0
j= 2,thread=0
j= 3,thread=0

这种写法很有局限,就是#pragma omp parallel for 只能作用到紧跟着的for循环,也就是说,并行块中第一句话只能是for循环,不能是其他代码。因为这个写法为for循环专属。可以将上述写成如下形式:

#include "omp.h"
#include "stdio.h"
#define NUM_THREADS 3
int main()
{
int i,j,k ;
omp_set_num_threads(NUM_THREADS);
#pragma omp parallel
{
printf("HelloWorld! , thread=%d\n",omp_get_thread_num()); //每个线程都执行这条语句
#pragma omp for
//这个并行块中的代码,对for循环体中的代码进行并行执行
for (i = 0;i<5;i++){
printf("i= %d,thread=%d\n",i,omp_get_thread_num());
}
#pragma omp for
//这个并行块中的代码,对for循环体中的代码进行并行执行
for (j=0;j<4;j++){
printf("j= %d,thread=%d\n",j,omp_get_thread_num());
}
}
return 0;
}
HelloWorld! , thread=0
i= 0,thread=0
i= 1,thread=0
HelloWorld! , thread=2
i= 4,thread=2
HelloWorld! , thread=1
i= 2,thread=1
i= 3,thread=1
j= 0,thread=0
j= 1,thread=0
j= 2,thread=1
j= 3,thread=1

可见,第二种写法完全能够完成对for循环的拆分并行,而且能够多次对多个for循环进行操作,更好的是,这种写法衍生了另一种功能,就是能够输出helleworld的那条输出语句,这条语句能够被所有的线程执行,如果for循环需要为每个线程赋值一个变量,那么这个变量可以放在此输出语句的位置,示例请看文章最后的例子。

barrier:

#include <stdio.h>
#include "omp.h"
int main (){
int i,j ;
omp_set_num_threads (5);
#pragma omp parallel
{
printf ("hello world!,thread=%d\n", omp_get_thread_num ());
#pragma omp barrier //执行到此代码时,程序暂停,直到上一条输出语句被所有线程都执行完后,才开始执行下面的语句。
#pragma omp for
for ( i = 0; i < 5; i++)
printf ("i= %d,thread=%d\n",i, omp_get_thread_num ());
#pragma omp barrier //执行到此代码时,程序暂停,直到上一条的for循环语句被所有线程都并行执行完后,才开始执行下面的语句。
#pragma omp for
for ( j = 0; j < 5; j++)
printf ("j= %d ,thread= %d\n", j,omp_get_thread_num ());
}
}
hello world!,thread=4
hello world!,thread=1
hello world!,thread=3
hello world!,thread=2
hello world!,thread=0
i= 4,thread=4
i= 0,thread=0
i= 3,thread=3
i= 1,thread=1
i= 2,thread=2
j= 0 ,thread= 0
j= 1 ,thread= 1
j= 2 ,thread= 2
j= 4 ,thread= 4
j= 3 ,thread= 3

master / single :

看了对于for循环的并行之后,产生了一个新的问题,如果要在两个并行的for循环之间插入一个单线程执行的语句,应该如下做:

#include "omp.h"
#include "stdio.h"
#define NUM_THREADS 5
int main()
{
int i ,j ;
omp_set_num_threads(NUM_THREADS) ;
#pragma omp parallel for
for (i=0;i<4;i++)
printf ("i = %d ,thread=%d \n",i,omp_get_thread_num());
//以下输出语句位于两个for循环之间的代码,只能由一个线程来执行
printf ("I am a single thread %d \n",omp_get_thread_num());
#pragma omp parallel for
for (j=0;j<4;j++)
printf ("j = %d ,thread=%d \n",j,omp_get_thread_num()); return 0;
}
i = 0 ,thread=0
i = 3 ,thread=3
i = 2 ,thread=2
i = 1 ,thread=1
I am a single thread 0
j = 3 ,thread=3
j = 1 ,thread=1
j = 0 ,thread=0
j = 2 ,thread=2

但是上述的程序看起来很麻烦,master和single指令就是解决这个问题的:

#include <stdio.h>
#include "omp.h"
#define NUM_THREADS 5
int main (){
int i ,j;
omp_set_num_threads (NUM_THREADS);
#pragma omp parallel
{
#pragma omp for
for (i = 0; i < 4; i++)
printf ("i= %d, thread= %d\n",i, omp_get_thread_num ());
#pragma omp barrier
// #pragma omp master //下面的程序由主线程执行
#pragma omp single //下面的程序由随便一个单线程执行
printf ("I am a single thread ! thread= %d\n", omp_get_thread_num ());
#pragma omp barrier
#pragma omp for
for (j = 0; j < 5; j++)
printf ("j= %d, thread= %d\n",j, omp_get_thread_num ());
}
}
i= 2, thread= 2
i= 0, thread= 0
i= 1, thread= 1
i= 3, thread= 3
I am a single thread ! thread= 2
j= 2, thread= 2
j= 0, thread= 0
j= 3, thread= 3
j= 1, thread= 1
j= 4, thread= 4

效果是一样的,master 是指定用主线程0,而single是随机的一个单线程执行

parallel sections:

#include <stdio.h>
#include "omp.h"
#define NUM_THREADS 10
int main () {
omp_set_num_threads (NUM_THREADS);
#pragma omp parallel sections
{
#pragma omp section //并行执行
printf ("thread %d section A!\n", omp_get_thread_num ());
#pragma omp section //并行执行
printf ("thread %d section B!\n", omp_get_thread_num ());
#pragma omp section //并行执行
printf ("thread %d section C!\n", omp_get_thread_num ());
#pragma omp section //并行执行
printf ("thread %d section D!\n", omp_get_thread_num ());
#pragma omp section //并行执行
printf ("thread %d section E!\n", omp_get_thread_num ()); }
}
thread 4 section A!
thread 4 section E!
thread 8 section D!
thread 3 section C!
thread 0 section B!

parallel for 相似,可以写成如下形式:

#include <stdio.h>
#include "omp.h"
#define NUM_THREADS 3
int main () {
omp_set_num_threads (NUM_THREADS);
#pragma omp parallel
{
#pragma omp sections
{
#pragma omp section
printf ("thread %d section A!\n", omp_get_thread_num ());
#pragma omp section
printf ("thread %d section B!\n", omp_get_thread_num ());
}
#pragma omp sections
{
#pragma omp section
printf ("thread %d section C!\n", omp_get_thread_num ());
#pragma omp section
printf ("thread %d section D!\n", omp_get_thread_num ());
#pragma omp section
printf ("thread %d section E!\n", omp_get_thread_num ());
}
}
}

ordered:

#include <stdio.h>
#include <omp.h>
main ()
{
int i ;
omp_set_num_threads(5) ;
#pragma omp parallel for ordered
for ( i = 1; i <= 5; i++)
{
#pragma omp ordered //指定以下的循环体按照顺序执行
printf ("i=%d,thread=%d\n", i,omp_get_thread_num());
}
}
i=1,thread=0
i=2,thread=1
i=3,thread=2
i=4,thread=3
i=5,thread=4

openMP中的互斥(锁)

critical:

这个指令可以有枷锁的效果,所指定的代码表示只允许一个线程进行操作

/*
*加和程序,从1一直加到100的和
*
* */
#include <stdio.h>
#include "omp.h"
int main(){
int sum=0;
#pragma omp parallel
{
int i=0;
int id=omp_get_thread_num(); //获得当前并行区域中活动线程个数
int nthread=omp_get_num_threads(); //返回当前的线程号
for(i=id+1;i<=100;i+=nthread)
#pragma omp critical //对sum进行互斥的操作,同一时间,只允许一个线程对sum变量进行操作
sum=sum+i;
}
printf("sum=%d\n",sum);
}
sum=5050

使用锁

另一个互斥访问资源的方法就是使用锁

#include <stdio.h>
#include <omp.h>
int main(){
int sum=0;
int i ;
omp_lock_t lck ; //定义一把锁
omp_init_lock(&lck); //初始化一把锁
#pragma omp parallel for
for( i=1;i<=100;i++)
{
omp_set_lock(&lck); //给下面的sum上锁,同一时间只有一个线程能对sum变量操作
sum=sum+i;
omp_unset_lock(&lck); // 解锁
}
printf("sum=%d\n",sum);
omp_destroy_lock(&lck); //关闭这把锁
}
sum=5050

上述代码中,只定义的了一把锁,如果要定义多把锁,并使用多把锁,看下面的代码:

/*
*随机产生0~9之间1000个数,统计0~9的个数。
*histogram[]存放统计的个数
*
* */
#include <stdio.h>
#include <stdlib.h>
#include "omp.h"
int main ()
{
int array[1000];
omp_lock_t locks[10]; //定义10把锁
int histogram[10];
omp_set_num_threads (5);
srandom (10);
int i ;
#pragma omp parallel for
// 多线程随机产生1000个数放在array数组中
for ( i = 0; i < 1000; i++)
array[i] = random () % 10;
#pragma omp parallel for
// 多线程初始化10把锁和初始化histogram数组
for ( i = 0; i < 10; i++)
{
omp_init_lock (&locks[i]);
histogram[i] = 0;
}
#pragma omp parallel for
// 统计出现0~9的个数
for ( i = 0; i < 1000; i++)
{
omp_set_lock(&locks[array[i]]); //上锁
histogram[array[i]] += 1 ;
omp_unset_lock(&locks[array[i]]); //解锁
}
for ( i = 0; i < 10; i++)
printf ("histogram[%d]=%d\n", i, histogram[i]);
//普通方式(单线程)关闭10把锁
for ( i = 0; i < 10; i++)
omp_destroy_lock (&locks[i]); }
histogram[0]=97
histogram[1]=109
histogram[2]=95
histogram[3]=108
histogram[4]=89
histogram[5]=103
histogram[6]=85
histogram[7]=111
histogram[8]=110
histogram[9]=93

openMP编程,求pi的值

求pi的方法是利用积分推导出Pi的值,如下图所示:

/*
* 普通方式求Pi,不利用多线程技术
*/
#include <stdio.h>
static long num_steps = 100000;//分成1000份
void main()
{
int i;
double x, pi, sum = 0.0;
double step = 1.0/(double)num_steps;
for(i=1;i<= num_steps;i++){
x = (i-0.5)*step;
sum=sum+4.0/(1.0+x*x);
}
pi=step*sum;
printf("%lf\n",pi);
}
~
3.141593
/*
*利用 parallel for 进行多线程求解
* */
#include <stdio.h>
#include <omp.h>
static long num_steps = 100000;
double step;
#define NUM_THREADS 2
void main ()
{
int i;
double x, pi, sum[NUM_THREADS];
double step = 1.0/(double) num_steps;
omp_set_num_threads(NUM_THREADS); //设置2线程
#pragma omp parallel
{
double x;
int id;
id = omp_get_thread_num();
sum[id]=0;
#pragma omp for
for (i=0;i< num_steps; i++){
x = (i+0.5)*step;
sum[id] += 4.0/(1.0+x*x);
}
}
for(i=0, pi=0.0;i<NUM_THREADS;i++)
pi += sum[i] * step; printf("%lf\n",pi);
}

openMP编程(上篇)之并行程序设计的更多相关文章

  1. openMP编程(上篇)之指令和锁

    openMP简介 openMP是一个编译器指令和库函数的集合,主要是为共享式存储计算机上的并行程序设计使用的. 当计算机升级到多核时,程序中创建的线程数量需要随CPU核数变化,如在CPU核数超过线程数 ...

  2. 【CUDA并行程序设计系列(1)】GPU技术简介

    http://www.cnblogs.com/5long/p/cuda-parallel-programming-1.html 本系列目录: [CUDA并行程序设计系列(1)]GPU技术简介 [CUD ...

  3. OpenMP并行程序设计

    1.fork/join并行执行模式的概念 2.OpenMP指令和库函数介绍 3.parallel 指令的用法 4.for指令的使用方法 5 sections和section指令的用法 1.fork/j ...

  4. OpenMP并行程序设计——for循环并行化详解

    在C/C++中使用OpenMP优化代码方便又简单,代码中需要并行处理的往往是一些比较耗时的for循环,所以重点介绍一下OpenMP中for循环的应用.个人感觉只要掌握了文中讲的这些就足够了,如果想要学 ...

  5. 《CUDA并行程序设计:GPU编程指南》

    <CUDA并行程序设计:GPU编程指南> 基本信息 原书名:CUDA Programming:A Developer’s Guide to Parallel Computing with ...

  6. OpenMP 并行程序设计入门

    OpenMP 是一个编译器指令和库函数的集合,主要是为共享式存储计算机上的并行程序设计使用的. 0. 一段使用 OpenMP 的并行程序 #include <stdio.h> #inclu ...

  7. OpenMP编程总结表

    本文对OpenMP 2.0的全部语法——Macro(宏定义).Environment Variables(环境变量).Data Types(数据类型).Compiler Directives(编译指导 ...

  8. C#并发编程之初识并行编程

    写在前面 之前微信公众号里有一位叫sara的朋友建议我写一下Parallel的相关内容,因为手中商城的重构工作量较大,一时之间无法抽出时间.近日,这套系统已有阶段性成果,所以准备写一下Parallel ...

  9. 5天玩转C#并行和多线程编程 —— 第二天 并行集合和PLinq

    5天玩转C#并行和多线程编程系列文章目录 5天玩转C#并行和多线程编程 —— 第一天 认识Parallel 5天玩转C#并行和多线程编程 —— 第二天 并行集合和PLinq 5天玩转C#并行和多线程编 ...

随机推荐

  1. css中的左右垂直居中的问题,可兼容各种版本浏览器的写法

    如题分为垂直居中,左右居中,先挑简单的记录. 一.左右居中 1.我刚开始写代码的时候,老师就直接告诉我一个简单的方法,那就是: width:500px; height:200px; margin:0 ...

  2. centos下部署NFS

        一. NFS简介   NFS---Network File System:主要功能是通过网络让不同的linux主机系统间可以彼此共享文件和目录.NFS客户端可以通过挂载的方式将NFS服务器端共 ...

  3. 关于word转化成xml,图片的转换

    当word另存为xml的时候,其中的图片会以Base64编码形式展示在xml文件的特定位置, java中操作图片转换成64位编码的方式: 可将返回的64直接放在前面的未知即可:

  4. macdown快速上手

    1.断句 在结尾处输入两个空格并使用回车. 2.标题分级 使用#来进行分级,#越多级数越低 3.链接 可以使用<>里面直接加上地址 或者使用[}里面加上链接名字然后后面接上()里面就是地址 ...

  5. 手动博客重定向 https://www.cnblogs.com/kelthuzadx/

    https://www.cnblogs.com/kelthuzadx/ 博客狂魔又㕛叒换地址了

  6. 集合之四:List接口

    查阅API,看List的介绍.有序的 collection(也称为序列).此接口的用户可以对列表中每个元素的插入位置进行精确地控制.用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的 ...

  7. Navicat设定mysql定时任务

    有个需求:每天将一张表的前一天的数据抽取到另一张表中,使用Mysql数据库的客户端Navicat配置 第一步,创建过程cust_report,直接在查询窗口中执行,保存后函数列表中就会出现. 第二步, ...

  8. Windows搭建Go语言环境·

    对于Windows用户,Go语言提供两种安装方式(源码安装除外): .MSI安装:程序会自动配置你的安装 .ZIP安装:需要你手动设置一些环境变量 一.MSI安装 1.下载安装包(根据操作系统选择相应 ...

  9. Python解释器的配置

    1.准备工作 安装好Pycharm2017版本 电脑上安装好Python解释器 2.本地解释器配置 配置本地解释器的步骤相对简洁直观: (1)单击工具栏中的设置按钮. (2)在Settings/Pre ...

  10. 关于z-index问题

    关于z-inde,这个网址还是对我受益匪浅的,https://www.cnblogs.com/bigboyLin/p/4621335.html z-index 起作用得有一个前提: 就是和定位一起用, ...