openMP编程(上篇)之并行程序设计
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编程(上篇)之并行程序设计的更多相关文章
- openMP编程(上篇)之指令和锁
openMP简介 openMP是一个编译器指令和库函数的集合,主要是为共享式存储计算机上的并行程序设计使用的. 当计算机升级到多核时,程序中创建的线程数量需要随CPU核数变化,如在CPU核数超过线程数 ...
- 【CUDA并行程序设计系列(1)】GPU技术简介
http://www.cnblogs.com/5long/p/cuda-parallel-programming-1.html 本系列目录: [CUDA并行程序设计系列(1)]GPU技术简介 [CUD ...
- OpenMP并行程序设计
1.fork/join并行执行模式的概念 2.OpenMP指令和库函数介绍 3.parallel 指令的用法 4.for指令的使用方法 5 sections和section指令的用法 1.fork/j ...
- OpenMP并行程序设计——for循环并行化详解
在C/C++中使用OpenMP优化代码方便又简单,代码中需要并行处理的往往是一些比较耗时的for循环,所以重点介绍一下OpenMP中for循环的应用.个人感觉只要掌握了文中讲的这些就足够了,如果想要学 ...
- 《CUDA并行程序设计:GPU编程指南》
<CUDA并行程序设计:GPU编程指南> 基本信息 原书名:CUDA Programming:A Developer’s Guide to Parallel Computing with ...
- OpenMP 并行程序设计入门
OpenMP 是一个编译器指令和库函数的集合,主要是为共享式存储计算机上的并行程序设计使用的. 0. 一段使用 OpenMP 的并行程序 #include <stdio.h> #inclu ...
- OpenMP编程总结表
本文对OpenMP 2.0的全部语法——Macro(宏定义).Environment Variables(环境变量).Data Types(数据类型).Compiler Directives(编译指导 ...
- C#并发编程之初识并行编程
写在前面 之前微信公众号里有一位叫sara的朋友建议我写一下Parallel的相关内容,因为手中商城的重构工作量较大,一时之间无法抽出时间.近日,这套系统已有阶段性成果,所以准备写一下Parallel ...
- 5天玩转C#并行和多线程编程 —— 第二天 并行集合和PLinq
5天玩转C#并行和多线程编程系列文章目录 5天玩转C#并行和多线程编程 —— 第一天 认识Parallel 5天玩转C#并行和多线程编程 —— 第二天 并行集合和PLinq 5天玩转C#并行和多线程编 ...
随机推荐
- css中的左右垂直居中的问题,可兼容各种版本浏览器的写法
如题分为垂直居中,左右居中,先挑简单的记录. 一.左右居中 1.我刚开始写代码的时候,老师就直接告诉我一个简单的方法,那就是: width:500px; height:200px; margin:0 ...
- centos下部署NFS
一. NFS简介 NFS---Network File System:主要功能是通过网络让不同的linux主机系统间可以彼此共享文件和目录.NFS客户端可以通过挂载的方式将NFS服务器端共 ...
- 关于word转化成xml,图片的转换
当word另存为xml的时候,其中的图片会以Base64编码形式展示在xml文件的特定位置, java中操作图片转换成64位编码的方式: 可将返回的64直接放在前面的未知即可:
- macdown快速上手
1.断句 在结尾处输入两个空格并使用回车. 2.标题分级 使用#来进行分级,#越多级数越低 3.链接 可以使用<>里面直接加上地址 或者使用[}里面加上链接名字然后后面接上()里面就是地址 ...
- 手动博客重定向 https://www.cnblogs.com/kelthuzadx/
https://www.cnblogs.com/kelthuzadx/ 博客狂魔又㕛叒换地址了
- 集合之四:List接口
查阅API,看List的介绍.有序的 collection(也称为序列).此接口的用户可以对列表中每个元素的插入位置进行精确地控制.用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的 ...
- Navicat设定mysql定时任务
有个需求:每天将一张表的前一天的数据抽取到另一张表中,使用Mysql数据库的客户端Navicat配置 第一步,创建过程cust_report,直接在查询窗口中执行,保存后函数列表中就会出现. 第二步, ...
- Windows搭建Go语言环境·
对于Windows用户,Go语言提供两种安装方式(源码安装除外): .MSI安装:程序会自动配置你的安装 .ZIP安装:需要你手动设置一些环境变量 一.MSI安装 1.下载安装包(根据操作系统选择相应 ...
- Python解释器的配置
1.准备工作 安装好Pycharm2017版本 电脑上安装好Python解释器 2.本地解释器配置 配置本地解释器的步骤相对简洁直观: (1)单击工具栏中的设置按钮. (2)在Settings/Pre ...
- 关于z-index问题
关于z-inde,这个网址还是对我受益匪浅的,https://www.cnblogs.com/bigboyLin/p/4621335.html z-index 起作用得有一个前提: 就是和定位一起用, ...