1、fork/join并行执行模式的概念

2、OpenMP指令和库函数介绍

3、parallel 指令的用法

4、for指令的使用方法

5 sections和section指令的用法

1、fork/join并行执行模式的概念

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

前面一篇文章中已经试用了OpenMP的一个Parallel for指令。从上篇文章中我们也可以发现OpenMP并行执行的程序要全部结束后才能执行后面的非并行部分的代码。这就是标准的并行模式fork/join式并行模式,共享存储式并行程序就是使用fork/join式并行的。

标准并行模式执行代码的基本思想是,程序开始时只有一个主线程,程序中的串行部分都由主线程执行,并行的部分是通过派生其他线程来执行,但是如果并行部分没有结束时是不会执行串行部分的,如上一篇文章中的以下代码:

int main(int argc, char* argv[])

{

clock_t t1 = clock();

#pragma omp parallel for

for ( int j = 0; j < 2; j++ ){

test();

}

clock_t t2 = clock();

printf("Total time = %d\n", t2-t1);

test();

return 0;

}

在没有执行完for循环中的代码之前,后面的clock_t t2 = clock();这行代码是不会执行的,如果和调用线程创建函数相比,它相当于先创建线程,并等待线程执行完,所以这种并行模式中在主线程里创建的线程并没有和主线程并行运行。

2、OpenMP指令和库函数介绍

下面来介绍OpenMP的基本指令和常用指令的用法,

在C/C++中,OpenMP指令使用的格式为

#pragma omp 指令 [子句[子句]…]

前面提到的parallel for就是一条指令,有些书中也将OpenMP的“指令”叫做“编译指导语句”,后面的子句是可选的。例如:

#pragma omp parallel private(i, j)

parallel 就是指令, private是子句

为叙述方便把包含#pragma和OpenMP指令的一行叫做语句,如上面那行叫parallel语句。

OpenMP的指令有以下一些:

parallel,用在一个代码段之前,表示这段代码将被多个线程并行执行

for,用于for循环之前,将循环分配到多个线程中并行执行,必须保证每次循环之间无相关性。

parallel for, parallel 和 for语句的结合,也是用在一个for循环之前,表示for循环的代码将被多个线程并行执行。

sections,用在可能会被并行执行的代码段之前

parallel sections,parallel和sections两个语句的结合

critical,用在一段代码临界区之前

single,用在一段只被单个线程执行的代码段之前,表示后面的代码段将被单线程执行。

flush,

barrier,用于并行区内代码的线程同步,所有线程执行到barrier时要停止,直到所有线程都执行到barrier时才继续往下执行。

atomic,用于指定一块内存区域被制动更新

master,用于指定一段代码块由主线程执行

ordered, 用于指定并行区域的循环按顺序执行

threadprivate, 用于指定一个变量是线程私有的。

OpenMP除上述指令外,还有一些库函数,下面列出几个常用的库函数:

omp_get_num_procs, 返回运行本线程的多处理机的处理器个数。

omp_get_num_threads, 返回当前并行区域中的活动线程个数。

omp_get_thread_num, 返回线程号

omp_set_num_threads, 设置并行执行代码时的线程个数

omp_init_lock, 初始化一个简单锁

omp_set_lock, 上锁操作

omp_unset_lock, 解锁操作,要和omp_set_lock函数配对使用。

omp_destroy_lock, omp_init_lock函数的配对操作函数,关闭一个锁

OpenMP的子句有以下一些

private, 指定每个线程都有它自己的变量私有副本。

firstprivate,指定每个线程都有它自己的变量私有副本,并且变量要被继承主线程中的初值。

lastprivate,主要是用来指定将线程中的私有变量的值在并行处理结束后复制回主线程中的对应变量。

reduce,用来指定一个或多个变量是私有的,并且在并行处理结束后这些变量要执行指定的运算。

nowait,忽略指定中暗含的等待

num_threads,指定线程的个数

schedule,指定如何调度for循环迭代

shared,指定一个或多个变量为多个线程间的共享变量

ordered,用来指定for循环的执行要按顺序执行

copyprivate,用于single指令中的指定变量为多个线程的共享变量

copyin,用来指定一个threadprivate的变量的值要用主线程的值进行初始化。

default,用来指定并行处理区域内的变量的使用方式,缺省是shared

3、parallel 指令的用法

parallel 是用来构造一个并行块的,也可以使用其他指令如for、sections等和它配合使用。

在C/C++中,parallel的使用方法如下:

#pragma omp parallel [for | sections] [子句[子句]…]

{

//代码

}

parallel语句后面要跟一个大括号对将要并行执行的代码括起来。

void main(int argc, char *argv[]) {

#pragma omp parallel

{

printf(“Hello, World!\n”);

}

}

执行以上代码将会打印出以下结果

Hello, World!

Hello, World!

Hello, World!

Hello, World!

可以看得出parallel语句中的代码被执行了四次,说明总共创建了4个线程去执行parallel语句中的代码。

也可以指定使用多少个线程来执行,需要使用num_threads子句:

void main(int argc, char *argv[]) {

#pragma omp parallel num_threads(8)

{

printf(“Hello, World!, ThreadId=%d\n”, omp_get_thread_num() );

}

}

执行以上代码,将会打印出以下结果:

Hello, World!, ThreadId = 2

Hello, World!, ThreadId = 6

Hello, World!, ThreadId = 4

Hello, World!, ThreadId = 0

Hello, World!, ThreadId = 5

Hello, World!, ThreadId = 7

Hello, World!, ThreadId = 1

Hello, World!, ThreadId = 3

从ThreadId的不同可以看出创建了8个线程来执行以上代码。所以parallel指令是用来为一段代码创建多个线程来执行它的。parallel块中的每行代码都被多个线程重复执行。

和传统的创建线程函数比起来,相当于为一个线程入口函数重复调用创建线程函数来创建线程并等待线程执行完。

4、for指令的使用方法

for指令则是用来将一个for循环分配到多个线程中执行。for指令一般可以和parallel指令合起来形成parallel for指令使用,也可以单独用在parallel语句的并行块中。

#pragma omp [parallel] for [子句]

for循环语句

先看看单独使用for语句时是什么效果:

int j = 0;

#pragma omp for

for ( j = 0; j < 4; j++ ){

printf(“j = %d, ThreadId = %d\n”, j, omp_get_thread_num());

}

执行以上代码后打印出以下结果

j = 0, ThreadId = 0

j = 1, ThreadId = 0

j = 2, ThreadId = 0

j = 3, ThreadId = 0

从结果可以看出四次循环都在一个线程里执行,可见for指令要和parallel指令结合起来使用才有效果:

如以下代码就是parallel 和for一起结合成parallel for的形式使用的:

int j = 0;

#pragma omp parallel for

for ( j = 0; j < 4; j++ ){

printf(“j = %d, ThreadId = %d\n”, j, omp_get_thread_num());

}

执行后会打印出以下结果:

j = 0, ThreadId = 0

j = 2, ThreadId = 2

j = 1, ThreadId = 1

j = 3, ThreadId = 3

可见循环被分配到四个不同的线程中执行。

上面这段代码也可以改写成以下形式:

int j = 0;

#pragma omp parallel

{

#pragma omp for

for ( j = 0; j < 4; j++ ){

printf(“j = %d, ThreadId = %d\n”, j, omp_get_thread_num());

}

}

执行以上代码会打印出以下结果:

j = 1, ThreadId = 1

j = 3, ThreadId = 3

j = 2, ThreadId = 2

j = 0, ThreadId = 0

在一个parallel 块中也可以有多个for语句,如:

int j;

#pragma omp parallel

{

#pragma omp for

for ( j = 0; j < 100; j++ ){

}

#pragma omp for

for ( j = 0; j < 100; j++ ){

}

}

for 循环语句中,书写是需要按照一定规范来写才可以的,即for循环小括号内的语句要按照一定的规范进行书写,for语句小括号里共有三条语句

for( i=start; i < end; i++)

i=start; 是for循环里的第一条语句,必须写成 “变量=初值” 的方式。如 i=0

i < end;是for循环里的第二条语句,这个语句里可以写成以下4种形式之一:

变量 < 边界值

变量 <= 边界值

变量 > 边界值

变量 >= 边界值

如 i>10 i< 10   i>=10 i>10 等等

最后一条语句i++可以有以下9种写法之一
i++

++i

i--

--i

i += inc

i -= inc

i = i + inc

i = inc + i

i = i –inc

例如i += 2; i -= 2;i = i + 2;i = i - 2;都是符合规范的写法。

5 sections和section指令的用法

section语句是用在sections语句里用来将sections语句里的代码划分成几个不同的段,每段都并行执行。用法如下:

#pragma omp [parallel] sections [子句]

{

#pragma omp section

{

代码块

}

}

先看一下以下的例子代码:

void main(int argc, char *argv)

{

#pragma omp parallel sections {

#pragma omp section

printf(“section 1 ThreadId = %d\n”, omp_get_thread_num());

#pragma omp section

printf(“section 2 ThreadId = %d\n”, omp_get_thread_num());

#pragma omp section

printf(“section 3 ThreadId = %d\n”, omp_get_thread_num());

#pragma omp section

printf(“section 4 ThreadId = %d\n”, omp_get_thread_num());

}

执行后将打印出以下结果:

section 1 ThreadId = 0

section 2 ThreadId = 2

section 4 ThreadId = 3

section 3 ThreadId = 1

从结果中可以发现第4段代码执行比第3段代码早,说明各个section里的代码都是并行执行的,并且各个section被分配到不同的线程执行。

使用section语句时,需要注意的是这种方式需要保证各个section里的代码执行时间相差不大,否则某个section执行时间比其他section过长就达不到并行执行的效果了。

上面的代码也可以改写成以下形式:

void main(int argc, char *argv)

{

#pragma omp parallel {

#pragma omp sections

{

#pragma omp section

printf(“section 1 ThreadId = %d\n”, omp_get_thread_num());

#pragma omp section

printf(“section 2 ThreadId = %d\n”, omp_get_thread_num());

}

#pragma omp sections

{

#pragma omp section

printf(“section 3 ThreadId = %d\n”, omp_get_thread_num());

#pragma omp section

printf(“section 4 ThreadId = %d\n”, omp_get_thread_num());

}

}

执行后将打印出以下结果:

section 1 ThreadId = 0

section 2 ThreadId = 3

section 3 ThreadId = 3

section 4 ThreadId = 1

这种方式和前面那种方式的区别是,两个sections语句是串行执行的,即第二个sections语句里的代码要等第一个sections语句里的代码执行完后才能执行。

用for语句来分摊是由系统自动进行,只要每次循环间没有时间上的差距,那么分摊是很均匀的,使用section来划分线程是一种手工划分线程的方式,最终并行性的好坏得依赖于程序员。

本篇文章中讲的几个OpenMP指令parallel, for, sections, section实际上都是用来如何创建线程的,这种创建线程的方式比起传统调用创建线程函数创建线程要更方便,并且更高效。

当然,创建线程后,线程里的变量是共享的还是其他方式,主线程中定义的变量到了并行块内后还是和传统创建线程那种方式一样的吗?创建的线程是如何调度的?等等诸如此类的问题到下一篇文章中进行讲解。

OpenMP并行程序设计的更多相关文章

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

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

  2. OpenMP 并行程序设计入门

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

  3. openMP编程(上篇)之并行程序设计

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

  4. OpenMP并行编程

    什么是OpenMP?“OpenMP (Open Multi-Processing) is an application programming interface (API) that support ...

  5. 《并行程序设计导论》——MPI(Microsoft MPI)(1):Hello

    =================================版权声明================================= 版权声明:原创文章 禁止转载  请通过右侧公告中的“联系邮 ...

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

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

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

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

  8. OpenMP 并行编程

    OpenMP 并行编程 最近开始学习并行编程,目的是为了提高图像处理的运行速度,用的是VS2012自带的OpenMP. 如何让自己的编译器支持OpenMP: 1) 点击 项目属性页 2)点击 配置 3 ...

  9. 《并行程序设计导论》——OpenMP

    OpenMP看着很好,实际上坑很多. 如果真的要求性能和利用率,还是专门写代码吧.而且MS的VS里只有2.X的版本.

随机推荐

  1. nextDay、beforeDay以及根据nextDay(beforeDay)求解几天后的日期,几天前的日期和两个日期之间的天数

    实现代码: package com.corejava.chap02; public class Date { private int year; private int month; private ...

  2. mac 下maven的安装

    最近在学习mahout,这些安装相关软件的步骤先记下来,避免以后忘记. 1.首先在mac上查看本机的java版本,如果没有需要自己去安装: 我的电脑上安装的java是1.7.0_79 2.在http: ...

  3. filter过滤器执行顺序

    浏览器请求---->进入过滤器---->进入doFilter方法--->执行chain.doFilter()方法就会放行----->进入业务逻辑方法------>进入过滤 ...

  4. ios开发之通知事件

    每天学习一点点,总结一点点,成功从良好的习惯开始! 昨天学习了ios开发中的关于通知事件的一些东西,在这里简单总结下,仅供初学者学习,更多的是怕我自己忘了,咩哈哈~~~~ 通知(notificatio ...

  5. 源码来袭!!!基于jquery的ajax分页插件(demo+源码)

    前几天打开自己的博客园主页,无意间发现自己的园龄竟然有4年之久了.可是看自己的博客列表却是空空如也,其实之前也有写过,但是一直没发布(然而好像并没有什么卵用).刚开始学习编程时就接触到博客园,且在博客 ...

  6. 初涉JavaScript模式 (3) : 字面量

    什么是字面量? 在编程语言中,字面量是一种表示值的记法.例如,"Hello, World!" 在许多语言中都表示一个字符串字面量(string literal ),JavaScri ...

  7. phpcms v9用get-sql调用数据表中的图组图片示例代码

    {pc:get sql="SELECT pictureurls FROM phpcms_picture_data where id = $id" start="0&quo ...

  8. Ruby自学笔记(三)— 方法Method

    Ruby做为面向对象语言,肯定要对对象进行相关的操作,这时候就涉及到方法了. 调用方法 - 对象.方法名(实参1,实参2,...,实参n) 方法的分类: 1. 实例方法:顾名思义,就是由实例来调用的方 ...

  9. 新站如何做SEO及注意事项

    最近公司做了新网站,完成后运营优化的工作就落在我身上了,由于之前也没有.就去网上百度了一下,上了各种论坛查阅大牛的博客.自己也总结了一些要点,在这里和大家分享一下.新网站大家可以点击查看牛羊养殖在线. ...

  10. ExtJS简单的动画效果(ext js淡入淡出特效)

    1.html页面:Application HTML file - index.html <html> <head> <title>ExtJs fadeIn() an ...