http://openmp.org/wp/
传统的单线程编程方式难以发挥多核CPU的强大功能,于是多核编程应运而生。多核编程可以认为是对多核环境下编程做了一些多线程抽象,提供一些简单的API,使得用户不必费太多精力来了解太多底层的知识。多核编程的工具有OpenMP和TBB,OpenMP支持的编程语言包括C、C++和Fortran,支持OpenMP的编译器包括Sun Studio、Intel Compiler、Microsoft Visual Studio、GCC。
在VS上配置openMP非常简单,总共分成2步:
(1)新建一个工程
(2)建立工程后,点击菜单栏->Project->Propertier,弹出菜单里,点击Configuration Properties->C/C++->Language->OpenMP Support,在下拉菜单里选择YES。
下面通过一个小例子说明openMP的易用性。
#include <iostream> #include <time.h> void test() { int a = 0; for (int i=0;i<100000000;i++) a++; } int main() { clock_t t1 = clock(); for (int i=0;i<8;i++) test(); clock_t t2 = clock(); std::cout<<"time: "<<t2-t1<<std::endl; }
#include <iostream> #include <time.h> void test() { int a = 0; for (int i=0;i<100000000;i++) a++; } int main() { clock_t t1 = clock(); #pragma omp parallel for for (int i=0;i<8;i++) test(); clock_t t2 = clock(); std::cout<<"time: "<<t2-t1<<std::endl; }
对上面代码做一个简单的剖析。当编译器发现#pragm omp parallel for后,自动将下面的for循环分成N份(N为CPU核数),然后把每份指派给一个核去执行,而且多核之间并行执行。
下面谈谈竟态条伯的问题,当多个线程并行执行时,有可能多个线程同时对某变量进行了读写操作,从而导致不可预知的结果。比如数组求和,那么openMP怎么实现并行数组求和呢?下面给出一个基本的解决方案,该方案的思想是,首先生成一个数组sumArray,其长度为并行执行的线程的个数(默认情况下,该个数等于CPU的核数),在for循环里,让各个线程更新自己线程对应的sumArray里的无素,最后再将sumArray里的元素累加到sum里,如下:
#include <iostream> #include <omp.h> int main(){ int sum = 0; int a[10] = {1,2,3,4,5,6,7,8,9,10}; int coreNum = omp_get_num_procs();//获得处理器个数 int* sumArray = new int[coreNum];//对应处理器个数,先生成一个数组 for (int i=0;i<coreNum;i++)//将数组各元素初始化为0 sumArray[i] = 0; #pragma omp parallel for for (int i=0;i<10;i++) { int k = omp_get_thread_num();//获得每个线程的ID sumArray[k] = sumArray[k]+a[i]; } for (int i = 0;i<coreNum;i++) sum = sum + sumArray[i]; std::cout<<"sum: "<<sum<<std::endl; return 0; }
上面的代码虽然达到了目的,但它产生了较多的额外操作,比如要先生成数组sumArray,最后还要用一个for循环将它的各元素累加起来,但openMP为我们提供了另一个工具,归约(reduction),见下面代码:
#include <iostream> int main(){ int sum = 0; int a[10] = {1,2,3,4,5,6,7,8,9,10}; #pragma omp parallel for reduction(+:sum) for (int i=0;i<10;i++) sum = sum + a[i]; std::cout<<"sum: "<<sum<<std::endl; return 0; }
reduction虽然很方便,但它只支持一些基本操作,比如+、-、*、&、|、&&、||等。有些情况下,我们既要避免race condition,但涉及到的操作又超出了reduction的能力范围,这时就要用到openMP的另一个工具,critical。
#include <iostream> int main(){ int max = 0; int a[10] = {11,2,33,49,113,20,321,250,689,16}; #pragma omp parallel for for (int i=0;i<10;i++) { int temp = a[i]; #pragma omp critical { if (temp > max) max = temp; } } std::cout<<"max: "<<max<<std::endl; return 0; }上例中,for循环还是被自动分成N份来并行执行,但我们用#pragm omp critical 将if (temp>max) max = temp括起来,它的意思是:各个线程还是并行执行for里面的语句,但当执行到critical里面时,要注意有没有其他线程正在里面执行,如果有的话,要等其他线程执行完再去执行。这样就避免了race condition问题,但显而易见,它的执行速度会变低,因为可能存在线程等待的情况。
以上内容参考:http://www.cnblogs.com/yangyangcv/archive/2012/03/23/2413335.html
OpenMP API C/C++语法快速浏览
编译器指令用以告诉编译器哪一段代码需要并行,所有的OpenMP编译器指令都以#pragma omp开始。就像其它编译器指令一样,在编译器不支持这些特征的时候OpenMP指令将被忽略。
#pragma omp <directive> [clause,[[,]clause...]
对directive(指令)而言clause(子句)是可选的,但子句可以影响到指令的行为。每一个指令有一系列适合它的子句,但有5个指令(master、cirticle、flush、ordered和atomic)不能使用子句。
(1)Parallel
parallel结构由一组线程组成,并行执行
#pragma omp paralle [clause[[,]clause]...]
structured-block
clause:
if(scalar-expression)
num_threads(integer-expression)
default(shared|none) //控制在parallel或task结构中共享数据的默认共享属性
private(list) //声明一个或多个列表是一个任务所私有的
firstprivate(list)
shared(list) //声明一个或多个list项用于parallel或task结构中生成的任务共享
copyin(list)//复制主线程中线程私有量到同组中的其他线程中
reduction(operator:list)
(2)Loop
#pragma omp for [clause[[,]clause]...]
for-loops
clause:
private(list)
firstprivate(list)
lastprivate(list)
reduction(operator:list)
schedule(kind[,chunk_size])
collapse(n)
ordered
nowait
kind:
--static: 循环根据chunk_size大小分成块,这些块以round-robin方式分配给线程
--dynamic: 每个线程执行完一块循环后,再请求下一个块
--guided:每个线程先执行一块循环后,再请求下一个块,在这个过程中块的大小在变小
--auto:由编译器或运行系统来决定
--runtime:调度程序 和块大小从run-sched-var ICV中取得
(3)Sections
sections是一种组织块
#pragma omp sections [clause[[,]clause]...]
{
[#pragma omp section]
structured-block
[#pragma omp section]
structured-block
...
}
clause:
private(list)
firstprivate(list)
lastprivate(list)
reduction(operator:list)
nowait
(4)Single
用于指定一个结构块只被一个线程组中的一个线程招待
#pragma omp single [clause[[,]clause]...]
structrued-block
clause:
private(list)
firstprivate(list)
copyprivate(list)
nowait
(5)Parallel Loop
loop在parrallel后的一种简短写法
#Parallel Loop
for-loop
clause:
除了nowait外,包括parallel和for中的所有clause
(6)Parallel Sections
是parallel后接sections的简写
(7)Task
指定一个详细的任务
#pragma omp task [clause[[,]clause]...]
structured-block
clause:
if(scalar-expression)
final(scalar-expression)
united
default(shared|none)
mergeable
private(list)
firstprivate(list)
shared(list)
(8)Taskyield
表示当前任务可以暂停(suspended)以执行另一个任务
#pragma omp taskyield
(9)Master
指定一个结构块由主线程执行
#pragma omp master
(10)Critical
设置临界区
#pragma omp critical [(name)]
structrued-block
(11)Barrier
(12)Taskwait
表示等待当前任务的子任务完成
#pragma omp taskwait
(13)Atomic
表示存储位置的更新是原子性的
#pragma omp atomic [read|write|update|capture]
expression-stmt
#pragma omp atomic capture
structured-block
(14)Flush
使线程的数据与内存一致
#pragma omp flush [(list)]
(15)Ordered
表示一个结构块以一个循环顺序进行
#pragma omp ordered
structured-block
(16)Threadprivate
表示一个变量在是线程私有的
#pragma omp threadprivate(list)
环境变量
(1)OMP_SCHEDULE
(2)OMP_NUM_THREAD
(3)OMP_DYNAMIC
(4)OMP_PROC_BIND
(5)OMP_NESTED
(6)OMP_STACKSIZE
(7)OMP_WAIT_POLICY
(8)OMP_MAX_ACTIVE_LEVELS
(9)OMP_THREAD_LIMIT