并行计算2 共享存储系统并行编程

前面省去了基于共享变量的共享存储并行编程和POSIX线程,这两个在分布式系统中,应该是需要学习的点. 包括像面试时,分布式也有可能问到POSIX作用机制,还是应该留意一下. 这篇从OpenMP并行编程讲起.

OpenMP概述

历史

  • 1994年,第一个ANSI X3H5草案被否决
  • 1997年,OpenMP标准规范代替原先被否决的ANSI X3H5
  • 1997年10月公布了与Fortran语言捆绑的第一个标准规范
  • 1998年11月9日公布了支持C和C++的标准规范
  • 2000年11月推出FORTRAN version a2.0。
  • 2002年3月推出C/C++ version 2.0
  • 2005年5月OpenMP2.5将原来的Fortran和C/C++标准规范相结合
  • 更详尽的信息可以访问http://www.openmp.org
    在这里插入图片描述

编程特点

  • OpenMP应用编程接口API是在共享存储体系结构上的一个编程模型
  • 包含编译制导、运行库例程和环境变量三个部分
  • 已经被大多数计算机硬件和软件厂家所标准化
  • 易于在不同的共享存储体系结构系统间移植
  • 支持增量并行化

OpenMP编程风格

OpenMP并行编程模型

  • 基于线程的并行编程模型
  • 一个外部的编程模型:程序员完全控制并行化
  • 使用FORK-JOIN并行执行模型
  • 通过使用编译制导语句来实现并行
    在这里插入图片描述
    OpenMP中的HelloWorld
    这里使用windows下的visual studio编写运行OpenMP程序十分容易,新建一个控制台程序,点击项目属性 - C/C++ - 语言 - OpenMP支持. 如果是在Linux下,使用gcc或g++选项编译:gcc -fopenmp -o a.out a.c,也就是加上-fopenmp选项.

编写如下代码:

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

int main() {
	int pid;
	omp_set_num_threads(4);  // 设置4个线程
	#pragma omp parallel private(pid)  // 进入并行域,每个线程私有pid
	{
		pid = omp_get_thread_num();
		cout << "The current running process is " << pid << endl;
		if (pid == 0) {  // 只有最初父进程可以进入这个代码块
			cout << "Now I'm in Main Process!!" << endl;
			int total_num = omp_get_num_threads();
			cout << "And I have " << total_num << " children" << endl;
		}
	}
	return 0;
}

运行结果:
在这里插入图片描述
这里就注意一下:omp_set_thread_numsomp_get_thread_numomp_get_num_threads 的函数调用,以及编译制导语句#pragma omp parallel private(pid)的使用.

并行域结构

  • 当并行域开始时,多个线程都会执行并行域中的代码
  • 当并行域结束时,只有主线程继续执行
  • 并行域的线程数由下列因素决定,且优先级递减:
    • 使用库函数omp_set_num_threads
    • 设置环境变量OMP_NUM_THREADS
    • 由实现决定的缺省值
  • 并行域中的线程号依次为0(主线程)到n-1

运行库例程

  • OpenMP定义了一套API对外提供多种库函数调用
  • C/C++程序需要引用文件<omp.h>
  • “Hello World”程序中出现的两个例程:
    • omp_get_thread_num:得到当前执行线程在并行域中的线程号
    • omp_get_num_threads:得到当前并行域使用的线程数

环境变量

  • OMP_SCHEDULE:for或parallel for中的调度方式
  • OMP_NUM_THREADS:执行中最大的线程数
  • OMP_DYNAMIC:是否动态设定并行域执行部分的线程数
  • OMP_NESTED:是否允许嵌套并行

共享任务结构

  • 将它所包含的代码划分给线程组的各成员来执行
  • 三种典型的共享任务结构:
    • for:代表数据并行性
    • sections:代表功能并行
    • single

在这里插入图片描述
for语句 指定紧随它的循环语句必须由线程组并行执行

scedule子句:

  • 描述如何将循环的迭代划分给线程组中的线程
  • 如果没有指定chunk的大小,迭代会尽可能地平均分配给每个线程
  • type为static:循环被分成大小为 chunk的块,静态分配给线程
  • type为dynamic:循环被划分为大小为chunk的块,动态分配给线程
#include <iostream>
#include <omp.h>
#define N 100
using namespace std;

int a[N], b[N], c[N];

int main() {
	int i; 
	omp_set_num_threads(4);
	#pragma omp parallel shared(a,b,c) private(i)
	{
		#pragma omp for
		for (i = 0; i < N; ++i) {
			cout << "in Thread " << omp_get_thread_num() << ", excute " << i << endl;
			c[i] = a[i] + b[i];
		}
	}
	return 0;
}

在这里插入图片描述
在这里插入图片描述
在哪看处理器数?任务管理器,有几个小窗口,就有几核.
在这里插入图片描述

OpenMP其他编程要素

同步结构

多个线程同时直接读写共享变量而导致错误的结果.

例如,下面程序对x进行共享读写而不加任何保护,会导致奇怪的结果,这在Java多线程编程中我们也遇到过.

#include <iostream>
#include <omp.h>
using namespace std;
const int N = 1000000;

int main() {
	int i; 
	int x;
	omp_set_num_threads(4);
	#pragma omp parallel shared(x) private(i)
	{
		#pragma omp for
		for (i = 0; i < N; ++i) {
			++x;
		}
	}
	cout << x << endl;
	return 0;
}

单线程执行,x的值就是 1000000,但程序执行的结果是:362451,显然就是发生了读写不同步.

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值