标签:des style blog http io color os ar 使用
在上一篇文章中介绍了并行计算的基础概念,也顺便介绍了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 = 0; double result = 0.0; for(index = value + 1; index < MAX_VALUE; index +=2 ) result += 1.0 / index; return result; } void OpenMPTest() { int index= 0; int time1 = 0; int time2 = 0; double value1 = 0.0, value2 = 0.0; double result[2]; time1 = GetTickCount(); for(index = 1; index < MAX_VALUE; index ++) value1 += 1.0 / index; time1 = GetTickCount() - time1; memset(result , 0, sizeof(double) * 2); time2 = GetTickCount(); #pragma omp parallel for for(index = 0; index < 2; index++) result[index] = _test(index); value2 = result[0] + result[1]; time2 = GetTickCount() - time2; printf("time1 = %d,time2 = %d\n",time1,time2); return; } int main() { OpenMPTest(); system("pause"); return 0; }
在这里例子中用到了一个关键的语句:
#pragma omp parallel for
这个句子代表了C++中使用OpenMP的基本语法规则:#pragma omp 指令 [子句[子句]…]
1. OpenMP指令与库函数
OpenMP包括以下指令:
OpenMP除上述指令外,还有一些库函数,下面列出几个常用的库函数:
OpenMP还包括以下子句:
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 = 0; j < 4; 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实际上都是用来如何创建线程的,这种创建线程的方式比起传统调用创建线程函数创建线程要更方便,并且更高效。
标签:des style blog http io color os ar 使用
原文地址:http://www.cnblogs.com/kuliuheng/p/4059133.html