并行程序设计——概论和背景知识

为什么要并行计算?

为什么要关心并行,单处理器已经足够快了吗?

为什么不研制更快的单处理器系统,为什么要研制并行系统,为什么研制多处理器系统?

为什么不能编写程序,将串行程序自动转换成可以充分使用多处理器的的并行程序?

1.1 为什么需要不断提升的性能

我们现在所需要考虑的问题复杂性增加,例如:气候模拟,蛋白质折叠,药物发现,能源研究,数据分析等问题。需要更加强大的计算能力,普通的串行计算过于缓慢。


1.2 为什么需要构建并行系统

单处理器性能大幅度提升到主要原因之一是日益增长的集成电路的晶体管密度。随着尺寸的减小,处理器主频增加,能够提供更高的性能,但是却导致了能耗增加 ,大量能源以热能形式散失。而处理器等电子元件工作环境的温度越高,其不可靠性会增加,但是当前空气冷却已经达到了极限。

所以如果要构造更快、更加复杂的单处理器,不如防止在一个芯片上放置多个简单的处理器。这称作多核处理器。

约定:本文中 核(core)处理器(processer/CPU) 代表同一意思。


1.3 为什么要编写并行程序

1.3.1 简介

为了达到充分利用多核处理器,使得程序更快的运行,渲染的图像更加逼真,需要将串行程序改写为并行程序,以提高运行速度和处理器的利用率。串行程序同一时间实际上只能使用一个CPU,无法发挥多核处理器对于程序速度的提升效果,所以我们不能简单地编写串行程序,我们应该编写并行程序发掘多核处理器的潜能。

对于一个串行程序:除非事先定义好的高效的全局算法,编译器很难发现一个高效的方法充分利用多核处理器。所以我们需要编写程序,对于某一串行结构,将其进行 并行化 。但是当程序复杂性增加,这一改写的过程会更加的复杂,追求高效的并行化将变得更加困难。

1.3.2 实例

计算n个数的值然后进行累加求和。串行程序代码:

sum = 0;
for(int i=0; i<n; i++)
{
    
    
	x = Compute_value(i)
	sum += x;
}

假设我们有p个核,且 p<<n,那么可以将这n个数据分成p份,每个核只大约需要求n/p个值以及它们的和。

my_sum = 0;
my_first_i = ... ;
my_last_i = ... ;
for(int my_i = my_first_i; my_i < my_last_i; my_i++) 
{
    
    
	my_x = Compute_value(my_i)
	my_sum += my_x;
}

其中my_为当前核的私有变量。

所有核完成计算之后,可以由0号核将其结果进行汇总:

if(It is core 0) 
{
    
    
	sum = my_sum;
	for(each core other than itself) 
	{
    
    
		value = recieve_Value_From_Cores();
		sum += value
	}
}
else
	send_MySum();

或者是采用另一种方式汇总。每两个核之间,由一个核进行结果相加。然后得到 ⌈ p / 2 ⌉ \lceil p/2 \rceil p/2个结果,反复这一操作,得到最终结果,如下图:
在这里插入图片描述
显然,对于p个核,方法2需要进行p次操作,方法1只需要 log ⁡ 2 p \log_2 p log2p次。
但是除非我们定义好使用方法2,一般来说,编译器很难采用方法2而不是方法1。这就是为什么我们需要编写并行程序,而不能指望编译器或者其他软件完成这一项操作。


1.4 怎样编写并行程序

1.4.1 简介

编写并行程序,大部分的基本思想都是将任务 分配 给各个核。有两种广泛的方法: 任务并行 (task-parallelism)数据并行 (data-parallelism)。任务并行是指将解决问题的各个任务分配到各个核上执行,每个核执行的操作不同;数据并行是将解决问题所需要处理的数据分配给各个核,每个核执行的操作大致一致。

不同的核之间的需要进行的协调包括:

  • 通讯:一个或者多个核将自己的部分或全部结果发送给其他的核。
  • 负载平衡:即期望每个核分配的工作数据的数目大致相同。
  • 同步:大多数系统中,核不会自动同步,而是在自己的空间内部工作。

目前,功能最强大的并行程序是通过 显式 并行结构编写的,即使用扩展C/C++ 编写的。但是这样的并行程序即便只是在单核处理器上运行,也会遵照并行的要求运行,比较复杂。

1.4.2 根据1.3.2实例讲解

通讯: 可以看到,最终某些核需要将计算结果发送其他核,这就是一种通讯。

负载平衡: 所有核的工作量大致相等,可以避免由于工作分配不平均导致某些核运行较快,会闲置等待其他核完成工作。

同步: 假设所有的数据不需要计算,而是从标准输入stdin中读入,使用0号核来初始化数组x。那么我们期望其他的核不会立即开始运行,而是等到0号核完成读入数据、初始化x数组之后才开始运行。我们期望有一个同步点,在这个点的时候核会在这里等待,知道所有的核都到达之和才继续执行。例如:

if(It is core 0)
	for(int i=0; i<n; i++)
		scanf("%d", &x[i]);
Synchronize_core();		//	同步点
Calculate();			//	计算

1.5 学习目标

学习编写显示并行程序。学习目标如下:

  • 学会利用C语言及C原因三个扩展: 消息传递接口 (Message-Passing Interface, MPI)POSIX线程 (POSIX threads, Pthreads)OpenMP 来编写基本的并行程序。

这三种拓展与并行系统相关,我们关注两种并行系统,详细可见第二章:

  • 共享内存系统 (shared-memory system):每个核能够共享访问计算机内存,每一个核都能够读写内存所有区域。可以通过检测更新共享内存数据来协调各个核。PthreadsOpenMP为共享内存系统设计。
  • 分布式内存系统 (distributed-memory system):每个核拥有自己的私有内存,核之间的通讯是显示的,必须使用类似于网络中发送消息的机制进行通讯。MPI为分布式内存系统设计。

分布式内存和共享内存

1.6 并发、并行、分布式

并发计算 (concurrent computing)并行计算 (parallel computing)分布式计算 (distributed computing) 的主要区别如下:

  • 并发计算:一个程序的多个任务在同一个时段内可以 同时执行
  • 并行计算:一个程序通过多个任务 紧密协作 来解决某个问题。
  • 分布式计算:一个程序需要与其他程序协作来解决某个问题。

并行程序和分布式程序都是并发的,某些程序例如多任务操作系统也是并发的,因为即使在单核计算机上,多个任务也能在同一时间段内 同时执行。并行程序和分布式程序没有明确的分界线。

并行程序往往在多个核上执行多个任务,这些核在物理空间上更靠近,通过共享内存或者是高速网络连接。并行计算核之间的协调比较频繁,开销较低,是细粒度的,并且比较可靠。并行计算过程执行时间较短。

分布式程序更加“松耦合”(loosely coupled),任务是在多个计算节点上执行的,计算节点相隔较远且任务是独立创建的进程完成的。分布式计算核之间的协调不频繁,粗粒度的,并且不是很可靠。分布式计算过程执行时间较长。

猜你喜欢

转载自blog.csdn.net/weixin_43762938/article/details/108741244