OpenMP(Open Multi Processing)是由Open ARB发布的一种用于并行编程的规范,是建立在串行语言上的扩展,目前可以在C/C++/Fortran中使用。
OpenMP的格式非常简单,原理也不难。它的基本原理就是创建多个线程,操作系统把这几个线程分到几个核上面同时执行,从而可以达到快速执行代码的目的。
OpenMP由三部分组成:编译指导(compiler directive)、运行库(runtime library)和环境变量(environment variables)。其语言模型基于以下假设:执行单元是共享一个地址空间的线程,即OpenMP是基于派生/连接(fork/join)的编程模型。Fork/join的并行机制如下图所示:
Fork/join并行机制:并行区前,串行命令派生出多条并行命令并行执行,执行到并行区末等待,等所有并行任务都结束,再转到串行执行。
OpenMP有两种常用的并行开发形式:一是通过简单的fork/join对串行程序并行化;二是采用单程序多数据对串行程序并行化。
优点:第一,共享存储模型,使得程序员不必进行数据划分和分布,使得开发并行程序比较容易;第二,更适合于SMP系统;第三,主要面向循环级的并行开发,可以容易地实现增量性的并行化。
缺点:第一,OpenMP只适用于SMP结构;第二,OpenMP主要开发循环级的并行程序,受此限制,对某些应用并不适合;第三,OpenMP的编写、正确性调试和性能调度复杂。
在编写OpenMP程序之前,应注意下面三点,
- 使用vs2005或者以上的版本编写OpenMP程序
- 编写程序的时候,选择
Property Pages
->Configuration Properties
->c/c++
->language
->OpenMp Support
,打开开关 - 添加
#include
声明。
先看一个简单的使用了OpenMP程序
int main(int argc, char* argv[])
{
#pragma omp parallel for
for (int i = 0; i < 10; i++ )
{
printf("i = %d/n", i);
}
return 0;
}
这个程序执行后打印出以下结果:
i = 0
i = 5
i = 1
i = 6
i = 2
i = 7
i = 3
i = 8
i = 4
i = 9
可见for 循环语句中的内容被并行执行了。(每次运行的打印结果可能会有区别)
这里要说明一下,
#pragma omp parallel for
这条语句是用来指定后面的for循环语句变成并行执行的,当然for循环里的内容必须满足可以并行执行,即每次循环互不相干,后一次循环不依赖于前面的循环。
将for循环里的语句变成并行执行后效率会不会提高呢,我想这是我们最关心的内容了。下面就写一个简单的测试程序来测试一下:
void test()
{
int a = 0;
clock_t t1 = clock();
for (int i = 0; i < 100000000; i++)
{
a = i+1;
}
clock_t t2 = clock();
printf("Time = %d \n", t2-t1);
}
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;
}
在test()
函数中,执行了1亿次循环,主要是用来执行一个长时间的操作。
在main()
函数里,先在一个循环里调用test()
函数,只循环2次,我们还是看一下在四核CPU上的运行结果吧:
Time = 190
Time = 201
Total time = 202
Time = 194
可以看到在for循环里的两次test()
函数调用都花费了200ms, 但是打印出的总时间却只花费了200ms,后面那个单独执行的test()函数花费的时间也是200ms,可见使用并行计算后效率提高了整整一倍。
reference:
- 周伟明:OpenMP并行程序设计
- 五种主要多核并行编程方法分析与比较