在上一篇文章中介绍了并行计算的基础概念,也顺便介绍了OpenMP。

  OpenMp提供了对于并行描述的高层抽象,降低了并行编程的难度和复杂度,这样程序员可以把更多的精力投入到并行算法本身,而非其具体实现细节。对基于数据分集的多线程程序设计,OpenMP是一个很好的选择。同时,使用OpenMP也提供了更强的灵活性,可以较容易的适应不同的并行系统配置。线程粒度和负载平衡等是传统多线程程序设计中的难题,但在OpenMp中,OpenMp库从程序员手中接管了部分这两方面的工作。但是,作为高层抽象,OpenMp并不适合需要复杂的线程间同步和互斥的场合。OpenMp的另一个缺点是不能在非共享内存系统(如计算机集群)上使用,一般在这样的系统上,MPI使用较多。

  在Visual Studio中使用OpenMP其实很简单,只要将 Project 的Properties中C/C++里Language的OpenMP Support开启(参数为 /openmp),就可以让VC++在编译时就可以支持OpenMP 的语法了。而在编写使用OpenMP 的程序时,添加#include <omp.h>即可。下面是一个实例:

#include <stdio.h>
#include <omp.h>
#include <windows.h>
#define MAX_VALUE 10000000 double _test(int value)
{
int index = ;
double result = 0.0;
for(index = value + ; index < MAX_VALUE; index += )
result += 1.0 / index; return result;
} void OpenMPTest()
{
int index= ;
int time1 = ;
int time2 = ;
double value1 = 0.0, value2 = 0.0;
double result[]; time1 = GetTickCount();
for(index = ; index < MAX_VALUE; index ++)
value1 += 1.0 / index; time1 = GetTickCount() - time1;
memset(result , , sizeof(double) * );
time2 = GetTickCount(); #pragma omp parallel for
for(index = ; index < ; index++)
result[index] = _test(index); value2 = result[] + result[];
time2 = GetTickCount() - time2; printf("time1 = %d,time2 = %d\n",time1,time2);
return;
} int main()
{
OpenMPTest(); system("pause");
return ;
}

  在这里例子中用到了一个关键的语句:

#pragma omp parallel for

  这个句子代表了C++中使用OpenMP的基本语法规则:pragma omp 指令 [子句[子句]…]

1. OpenMP指令与库函数

  OpenMP包括以下指令

  • parallel:用在一个代码段之前,表示这段代码将被多个线程并行执行
  • for:用于for循环之前,将循环分配到多个线程中并行执行,必须保证每次循环之间无相关性
  • parallel for:parallel 和 for语句的结合,也是用在一个for循环之前,表示for循环的代码将被多个线程并行执行
  • sections:用在可能会被并行执行的代码段之前
  • parallel sections:parallel和sections两个语句的结合
  • critical:用在一段代码临界区之前
  • single:用在一段只被单个线程执行的代码段之前,表示后面的代码段将被单线程执行
  • 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

2. parallel指令用法

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

#pragma omp parallel [for | sections] [子句[子句]…]
{
  // 需要并行执行的代码
}

  例如,可以写一个简单的并行输出提示信息的代码:

#pragma omp parallel num_threads(8)
{
printf(“Hello, World!, ThreadId=%d\n”, omp_get_thread_num() );
}

  在本机测试将会得到如下结果:

  结果表明,printf函数被创建了8个线程来执行,并且每一个线程执行的先后次序并不确定。和传统的创建线程函数比起来,OpenMP相当于为一个线程入口函数重复调用创建线程函数来创建线程并等待线程执行完。如果在上面的代码中去掉num_threads(8)来指定线程数目,那么将根据实际CPU核心数目来创建线程数。

3. for指令用法

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

#pragma omp [parallel] for [子句]
for循环语句

  例如有这样一个例子:

#pragma omp parallel for
for ( int j = ; j < ; j++ )
{
printf("j = %d, ThreadId = %d\n", j, omp_get_thread_num());
}

  可以得到如下结果:

  从结果可以看出,for循环的语句被分配到不同的线程中分开执行了。需要注意的是,如果不添加parallel关键字,那么四次循环将会在同一个线程里执行,结果将会是下面这样的:

4. sections和section的用法

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

#pragma omp [parallel] sections [子句]
{
#pragma omp section
{
// 代码块
}
}

  例如有这样一个例子:

#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内部的代码都是(分配到不同的线程中)并行执行的。使用section语句时,需要注意的是这种方式需要保证各个section里的代码执行时间相差不大,否则某个section执行时间比其他section长太多就达不到并行执行的效果了。

  如果将上面的代码拆分成两个sections,即:

#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 parallel 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());
}

  产生的结果将会是这样的:

  可以看出,两个sections之间是串行执行的,而section内部则是并行执行的。

小节:

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

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

并行计算之OpenMP入门简介的更多相关文章

  1. OpenMP 入门

    OpenMP 入门 简介 OpenMP 一个非常易用的共享内存的并行编程框架,它提供了一些非常简单易用的API,让编程人员从复杂的并发编程当中释放出来,专注于具体功能的实现.openmp 主要是通过编 ...

  2. 掌握 Ajax,第 1 部分: Ajax 入门简介

    转:http://www.ibm.com/developerworks/cn/xml/wa-ajaxintro1.html 掌握 Ajax,第 1 部分: Ajax 入门简介 理解 Ajax 及其工作 ...

  3. MongoDB入门简介

    MongoDB入门简介 http://blog.csdn.net/lolinzhang/article/details/4353699 有关于MongoDB的资料现在较少,且大多为英文网站,以上内容大 ...

  4. (转)Web Service入门简介(一个简单的WebService示例)

    Web Service入门简介 一.Web Service简介 1.1.Web Service基本概念 Web Service也叫XML Web Service WebService是一种可以接收从I ...

  5. NodeJS入门简介

    NodeJS入门简介 二.模块 在Node.js中,以模块为单位划分所有功能,并且提供了一个完整的模块加载机制,这时的我们可以将应用程序划分为各个不同的部分. const http = require ...

  6. ASP.NET Core学习之一 入门简介

    一.入门简介 在学习之前,要先了解ASP.NET Core是什么?为什么?很多人学习新技术功利心很重,恨不得立马就学会了. 其实,那样做很不好,马马虎虎,联系过程中又花费非常多的时间去解决所遇到的“问 ...

  7. webservice入门简介

    为了梦想,努力奋斗! 追求卓越,成功就会在不经意间追上你 webservice入门简介 1.什么是webservice? webservice是一种跨编程语言和跨操作系统平台的远程调用技术. 所谓的远 ...

  8. Web Service入门简介(一个简单的WebService示例)

    Web Service入门简介 一.Web Service简介 1.1.Web Service基本概念 Web Service也叫XML Web Service WebService是一种可以接收从I ...

  9. Android精通教程-第一节Android入门简介

    前言 大家好,给大家带来Android精通教程-第一节Android入门简介的概述,希望你们喜欢 每日一句 If life were predictable it would cease to be ...

随机推荐

  1. 转:C#精髓 第四讲 GridView 72般绝技

    说明:准备出一个系列,所谓精髓讲C#语言要点.这个系列没有先后顺序,不过尽量做到精.可能会不断增删整理,本系列最原始出处是csdn博客,谢谢关注. C#精髓 第四讲 GridView 72般绝技 作者 ...

  2. 实例讲解Oracle数据库设置默认表空间问题

    实例讲解Oracle数据库设置默认表空间问题   实例讲解Oracle数据库设置默认表空间问题,阅读实例讲解Oracle数据库设置默认表空间问题,DBA们经常会遇到一个这样令人头疼的问题:不知道谁在O ...

  3. supervisor很赞!

    最近,公司进行了新的架构设计,原来一个区服一组进程,变成了对外只有一台服,后面N组多进程进行服务的模式.于是,管理进程就变成了一个头痛的问题.原来是在写代码的目录里放置各种脚本解决的,关闭脚本,开启脚 ...

  4. HTML-day-1-HTML基础知识

    HTML基础知识 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://ww ...

  5. 精美的HTML5 Loadding页面

    以前我们大部分的Loading动画都是利用gif图片实现的,这种图片实现Loading动画的方法虽然也很不错,但是作为HTML5开发者来说,如果能利用HTML5和CSS3实现这些超酷的Loading动 ...

  6. js部分---数组及练习题;

    数据存储--数组: 强类型语言数组 1.同一类型的数据存储的集合,在内存中是连续的 2.定义的时候需要制定长度 弱类型语言数组 1.可以存储任意类型的数据 2.在内存中不连续,不需要制定长度 定义一个 ...

  7. Eclipse反编译插件jad安装

    下载jadClipse地址: 链接: http://pan.baidu.com/s/1kTN4TPd  提取码: 3fvd 将net.sf.jadclipse_3.3.0.jar拷贝到eclipse的 ...

  8. leetcode 143. Reorder List ----- java

    Given a singly linked list L: L0→L1→-→Ln-1→Ln,reorder it to: L0→Ln→L1→Ln-1→L2→Ln-2→- You must do thi ...

  9. HDU 1241 Oil Deposits --- 入门DFS

    HDU 1241 题目大意:给定一块油田,求其连通块的数目.上下左右斜对角相邻的@属于同一个连通块. 解题思路:对每一个@进行dfs遍历并标记访问状态,一次dfs可以访问一个连通块,最后统计数量. / ...

  10. 翻译:Universal Image Loader

    本文转载于:http://blog.csdn.net/tianxiangshan/article/details/9399183 All manipulations are held by the I ...