基于OpenMP的并行计算
本人所有博客仅用于个人的知识积累,禁止商业与非商业用途的剽窃和抄袭,如有错误,欢迎各位大佬批评指正,我们一起交流讨论、共同成长。
序言:本学期选修了《GPU并行计算》这门课程,借此来学习一下并行计算的硬件基础、并行程序设计思想与方法、并行算法设计与分析等内容,提高个人的编码能力和底层硬件与上层软件的联系能力。闲话不多说,我们开始吧!
基于OpenMP(Open Multi-Processing)的并行程序主要是在原来的串行程序基础上,添加编译指导指令(类似于C/C++中带“#”的语句)。因此,编程任务量小,上手比较容易。而实现并行行为主要是依靠底层运行支持库,程序员不需要关注这些,只需要考虑并行区域的划分与并行算法的设计。
首先,使用OpenMP(以下简称“omp”)时,需要包含的头文件如下代码块所示,第一个头文件包含了omp的编译指导语句,第二个头文件主要是为了测试并行计算的时间,使用其中的数据类型clock_t。
#include<omp.h>
#include<time.h>
基于omp的并行语句都是以如下关键词组开始的:
#pragma omp
若出现如下语句,则表明代码段进入了并行模式:
#pragma omp parallel
利用下方的主函数可以测试在未指定线程个数的时候,CPU一共创建了多少线程来执行并行代码段。parallel块中的每行代码都被多个线程重复执行了。
int main()
{
#pragma omp parallel
cout << "hello world" << endl;
return 0;
}
下面给出几组简单且常见的并行测试程序来体会基于omp的并行编程方法。
在循环语句中开启多个线程需要用到下述语句,且两组语句等价。
#pragma omp parallel for
#pragma omp parallel
{
#pragma omp for
for(...)
{
...
}
...
}
值得注意的是,
1、第二组代码段中,若没有#pragma omp parallel,则无法实现并行,只有二者结合起来使用才会有效;
2、#pragma omp for语句的作用域仅在当前循环,对于并行区域的其他循环不起作用,若并行块中的所有for循环都需要并行计算的话,则每组循环开始前都应添加#pragma omp for语句;
3、参与并行计算的线程必须保证具有独立性,即各个循环之间互不相关,后一个循环不依赖于前面若干个循环的结果;
4、此类并行模式属于数据级并行。
若指定线程数为n,则
#pragma omp parallel num_threads(n)
{
#pragma omp for
for(...)
{
...
}
...
}
测试程序1:并行输出测试
#include<iostream>
#include<Windows.h>
#include<omp.h>
using namespace std;
int main()
{
#pragma omp parallel
{
#pragma omp for
for (int j = 0; j < 1000; j++)
{
Sleep(5000);
cout << "j=" << j << ", Thread_id = " << omp_get_thread_num() << endl;
}
}
return 0;
}
测试程序2:串行累加与并行累加的用时比较
#include<iostream>
#include<time.h>
#include<omp.h>
using namespace std;
void SumNumber()
{
int sum = 0;
for(int i = 0; i < 10000000; i++)
sum ++;
}
void SerialTest()
{
clock_t t1 = clock();
for (int i = 0; i < 100; i++)
SumNumber();
clock_t t2 = clock();
cout << "Serial Time: " << t2 - t1 << endl;
}
void ParallelTest()
{
clock_t t1 = clock();
#pragma omp parallel for
for (int i = 0; i < 100; i++)
SumNumber();
clock_t t2 = clock();
cout << "Parallel Time: " << t2 - t1 << endl;
}
int main()
{
SerialTest();
ParallelTest();
return 0;
}
void ParallelTest()
{
clock_t t1 = clock();
#pragma omp parallel num_threads(5)
{
#pragma omp for
for (int i = 0; i < 100; i++)
SumNumber();
}
clock_t t2 = clock();
cout << "Parallel Time: " << t2 - t1 << endl;
}
sections结构是omp中常用的任务划分语句,将并行区域的代码结构划分为离散的代码块(section),每个代码块(section)都由一个线程执行,属于任务级并行。
#pragma omp parallel sections
{
#pragma omp section
{
...
}
#pragma omp section
{
...
}
...
}
测试程序3:向量的加法