ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

高性能计算之OpenMP——超算习堂学习2

2021-06-08 22:01:25  阅读:499  来源: 互联网

标签:习堂 线程 ++ omp maxn OpenMP 超算 include parallel


OpenMP学习2——超算习堂

一、for指令的使用方法细嚼

1.1、parallel for指令的用法

在OpenMP并行程序设计中,for循环是一种独立的并行指令。它非常重要!它的指令格式是:

#include <omp.h>

#pragma omp parallel for
for(i = begin;i < end;++i)
{
	// Content
}

十分需要注意的是:

parallel for指令的后面必须要紧跟for语句块!!!!

并且for循环并行必须要处在parallel并行区块内!!!!否则会当作串行执行!

1.2、parallel for指令的执行机制

在这里插入图片描述

此前一篇博客已经说明了,OpenMP的并行计算模式是插入并行语句的方法,如上图。当我们的串行程序执行到并行语句块的时候,会从主线程中派生出线程组,然后线程组对计算任务进行均分,然后并行计算。并行计算结束后重新回到串行程序。

1.3、parallel for并行程序设计案例

案例1:计算两个向量的点乘
#include <omp.h>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <ctime>

const int maxn = 5e7;
const int mod = 10000;

int vec1[maxn], vec2[maxn], vec[maxn], i;

int main()
{
	srand((unsigned int)time(NULL));
	for (i = 0; i < maxn; ++i)
	{
		vec1[i] = rand() % mod;
		vec2[i] = rand() % mod;
	}
	printf("--------------before parallel compute---------------\n");
	clock_t s, t;
	s = clock();
	for (i = 0; i < maxn; ++i)
	{
		vec[i] = vec1[i] * vec2[i];
	}
	t = clock();
	printf("--------------used time = %d ms---------------\n", t - s);

	s = clock();
	printf("--------------enter parallel compute---------------\n");
#pragma omp parallel num_threads(20) shared(vec1, vec2, vec) private(i)
	{
#pragma omp for 
		for (i = 0; i < maxn; ++i)
		{
			vec[i] = vec1[i] * vec2[i];
		}
	}
	t = clock();
	printf("--------------used time = %d ms---------------\n", t - s);
	return 0;
}
计算效率对比:

在这里插入图片描述

案例2:parallel for并行计算矩阵乘法
#include <omp.h>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <ctime>

const int maxn = 1000;
const int mod = 10000;

int vec1[maxn][maxn], vec2[maxn][maxn], vec[maxn][maxn], i, j, k;

int main()
{
	srand((unsigned int)time(NULL));
	for (i = 0; i < maxn; ++i)
	{
		for (j = 0; j < maxn; ++j)
		{
			vec1[i][j] = rand() % mod;
			vec2[i][j] = rand() % mod;
		}
	}
	printf("--------------before parallel compute---------------\n");
	clock_t s1, t1, s2, t2;
	s1 = clock();
	for (i = 0; i < maxn; ++i)
	{
		for (j = 0; j < maxn; ++j)
		{
			for (k = 0; k < maxn; ++k)
			{
				vec[i][j] += (vec1[i][k] * vec2[k][j]);
			}
		}
	}
	t1 = clock();
	printf("----------------used time = %d ms-----------------\n", t1 - s1);


	printf("--------------enter parallel compute---------------\n");
	s2 = clock();
#pragma omp parallel for collapse(2) schedule(dynamic) private(i, j, k) shared(vec1, vec2, vec)
	for (i = 0; i < maxn; ++i)
	{
		for (j = 0; j < maxn; ++j)
		{
			for (k = 0; k < maxn; ++k)
			{
				vec[i][j] += (vec1[i][k] * vec2[k][j]);
			}
		}
	}
	t2 = clock();
	printf("----------------used time = %d ms-----------------\n", t2 - s2);

	printf("\n----------------the speedup ratio = %lf---------------\n", 1.0 * (t1 - s1) / (t2 - s2));
	return 0;
}
运行结果如下:

在这里插入图片描述

1.4、运用omp parallel for的一些注意事项

第一:omp parallel for 和 omp for 不要混用

一旦在前面有使用过

#pragma omp parallel ...

语句,并且当前还处在这个并行区,然后这个时候你想使用for循环并行,则千万不要再搞一次:

#pragma omp parallel for

这样的操作了,因为这样会让线程组重组,然后相当于有两重并行,举个例子看看:

在有#pragma omp parallel 的并行区下运行
#include <omp.h>
#include <iostream>
using namespace std;

int main()
{
#pragma omp parallel num_threads(10)
	{
#pragma omp for
		for (int i = 0; i < 5; ++i)
		{
#pragma omp critical
			{
				cout << "i = " << i << endl;
			}
		}
	}
	return 0;
}

此时的运行结果是:

在这里插入图片描述

然后如果你多此一举多搞一遍parallel命令就会:
#include <omp.h>
#include <iostream>
using namespace std;

int main()
{
#pragma omp parallel num_threads(4)
	{
#pragma omp parallel for // 注意看这里哦
		for (int i = 0; i < 5; ++i)
		{
#pragma omp critical
			{
				cout << "i = " << i << endl;
			}
		}
	}
	return 0;
}

看看这样的“多此一举”的运行结果:

在这里插入图片描述

我们会发现,这个0 ~ 4 被重复执行啦!这样会影响并行程序结果,还会误以为运行的开销变大哦!!!!

造成这种结果的原因就是:parallel命令会告诉操作系统,此时我要重组线程组,要重新开始并行程序运行。然后这下好啦,每个线程到了那句指令的时候都重组线程组,白白多执行4次(取决于线程数)

二、多线程下数据访问同步

2.1、同步一词在并行计算的含义

其实同步啊,在并行计算里有两种含义:

第一:线程/进程的运行有的快有的慢,我想要在某处各个线程/进程达到同样的状态,这叫并行程序的运行同步

第二:对于共享内存的模型,我们需要控制数据的访问,达到线程同步。这样做的目的是为了防止多个进程/线程同时访问某个数据、内存,导致该数据同时改变,这样的作用下会让数据失真!举个例子:初始有变量a = 2,比如线程A要让a++,线程B要让a*=2。如果不控制访问,让变量a(或者某语句块)的执行的时候只能让一个线程进入执行,其他线程等待执行。则会让资源出现同步问题。这就叫数据同步。

2.2用critical创建临界区的方式避免线程同步危害

#include <omp.h>
#include <iostream>
using namespace std;

int main()
{
	int i, len, cnt = 0;
#pragma omp parallel num_threads(6)
	{
		len = omp_get_num_threads();
#pragma omp for private(i)
		for (i = 0; i < len; ++i)
		{
#pragma omp critical
			{
				cout << "Current is " << i << endl;
				cnt += i;
			}
		}
	}
	cout << "cnt = " << cnt << endl;
	return 0;
}
利用critical指令完成线程同步的运行结果:

在这里插入图片描述

 

标签:习堂,线程,++,omp,maxn,OpenMP,超算,include,parallel
来源: https://blog.51cto.com/u_15262702/2883550

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有