我的GIS/CS学习笔记:https://github.com/yunwei37/ZJU-CS-GIS-ClassNotes <一个浙江大学本科生的计算机、地理信息科学知识库 >
(浙江大学编译原理课程的课程报告)
前言
并行性
是指计算机系统具有可以同时进行运算或操作的特性,在同一时间完成两种或两种以上工作。并行性等级可以分为作业级或程序级
、任务级或程序级
、指令之间级
和指令内部级
。
对于在一个具有指令级并行机制的处理器上程序的并行能力,需要考虑以下因素:
- 程序中潜在的并行性,或者说程序中预算之间的依赖关系;例如具有简单的控制结构和规则的数据访问模式的数值应用中的并行性就相对较多;
- 处理器上可用的并行性,比如可以用以计算的硬件资源的数目;
- 从原来的顺序程序中抽取并行性的能力;
- 在给定的指令调度约束下找到最好的并行调度方案的能力;
并行性抽取和并行执行的调度可以通过软件静态完成,也可以通过硬件动态完成,或二者互相结合。编译中主要涉及的就是软件相关的静态过程,即如何通过在编译的过程中进行指令抽取
和指令调度
,来达到更好的并行性和运行速度。
本文希望从并行性相关的处理器体系结构实现、基本块调度算法、全局调度算法等方面来介绍编译过程中的并行性问题。
并行相关的处理器体系结构
并行性的基础是现代高性能处理器的硬件能够在一个时钟周期能执行多条指令。现流行的并行技术大都可以从三个方面实现:资源重复、资源共享、时间重叠。其中主要应用到的技术如下:
流水线技术
: 计算机中的流水线技术是把一个重复的过程分解为若干个子过程,每个子过程与其他子过程并行进行。从本质上讲,流水线技术是一种时间并行技术。通常我们描述的指令级并行性指的是在一个时钟周期内能发射多条指令,但如果使用流水线技术,由于一个指令需要多个时钟周期完成,因此仍然存在指令级并行的情况:每个时钟周期都可以取得一个新指令,而前面的指令还在流水线中执行,例如一个简单的五级流水线:
如果后续指令所需要的结果在此时已经可用,那么流水线就可以流出一条指令。但对于部分存在数据相关或者分支跳转的指令而言,下一条指令所需要的内容依赖于上一条指令的执行结果,此时就需要进行一定的调度或数据传输来避免流水线停顿。
多指令发送
: 流水线技术虽然已经利用了一定的并行性来加速程序执行,但如果能通过配置多个可用的功能部件在每个周期发送多条指令,并行性还可继续提升,即多指令发送技术,也称多发射技术。常见的多发送机器有通过软件管理其并发性的VLIW
(Very Long Instruction Word,超长指令字) ,即通过一种非常长的指令组合,把许多条指令连在一起增加运算速度;或通过硬件管理的超标量
机器。 简单的硬件指令调度器根据指令获取的顺序执行指令,如果其碰到依赖先前指令的指令,需要等待依赖关系的解除(计算结果可用)才能进行下一步的计算。更加复杂的指令调度器可以通过动态地调整指令执行的序列来避免相关性造成的阻塞。多核处理器
: 近年来,由于摩尔定律的限制,仅仅提高单核芯片的速度会产生过多热量且无法带来相应的性能改善,因此引入了多核芯片来增加并行性。多核处理器是指在一枚处理器中集成两个或多个完整的计算引擎(内核),此时处理器能支持系统总线上的多个处理器,由总线控制器提供所有总线控制信号和命令信号。多核处理器对应于线程级并行性。
代码调度的相关约束
在讨论代码调度的相关算法之前,我们首先需要看一下代码调度所需要遵守的一些基本约束条件。约束可以大致分为三种类型:
控制依赖约束
:所有在源程序中执行的操作都必须在优化的程序中执行;数据依赖约束
:优化后的程序中的操作必须和源程序中的相应操作生成相同结果;资源约束
:特定机器上的资源是有限的,不能超额使用。
这些约束保证程序的优化可以正常进行,并生成和源程序相同的结果。但由于代码调度改变了指令执行的顺序,有可能优化后的程序在执行某一点上的内存状态与优化前任何一点都不匹配。
我们来看看具体的一些依赖问题。
数据依赖
简单来说,如果两个操作访问同一个变量,且这两个操作中有一个为写操作,此时这两个操作之间就存在数据依赖性,并且它们之间的相对执行顺序必须保持不变。在代码调度中可能出现的数据依赖有:
真依赖
:即写之后再读;反依赖
:读之后再写,如果调度时写操作在读操作前发生,就可能读到错误的值。输出依赖
:写之后再写,如果顺序调换则会导致被写位置上存放的是错误的值。
其中,后两者被称为存储相关的依赖
,可以通过在不同的内存位置存放不同的值来消除这些依赖关系。
内存访问依赖
如果两个不同的内存访问指向同一个位置,就有可能存在内存访问之间的依赖关系。内存访问依赖关系比较复杂,尤其是对于非类型安全的语言(如C语言),要证明任意一对基于指针的内存访问之间的独立性需要负债的分析过程。主要的分析可以有以下几种:
数组的数据依赖分析
:区分数组元素访问中的下标值;指针别名分析
:如果两个指针指向同一个对象,即互为别名;过程间分析
:关于全局变量与参数之间的问题。
寄存器使用与并行性的折衷
在并行分析和调度中的机器无关中间表示所使用的无限多个伪寄存器必须被映射到目标机器上的有限寄存器;而把几个伪寄存器映射到同一个物理寄存器会生成一定的存储依赖,导致限制了指令级的并行性。从另一方面来说,并行性也产生了更多的存储需求。因此,尽量降低寄存器使用数量的目标与最大化指令并行性的目标直接冲突。
寄存器分配阶段与代码调度阶段的顺序也会影响到并行性与存储器数量,因此在某些时候可以采用层次化
的方式来处理,例如从最内层循环开始进行代码优化,先进行指令调度,再进行寄存器分配,再对代码进行调度;对外层循环依次重复此过程。
控制依赖
如果说指令A的结果决定了指令B是否执行,那么就可以说指令B是控制依赖
于指令A的。一个优化后的程序必须执行源程序中所有的运算,也可以执行更多的指令来增加并行性。
投机执行
如果我们知道一条指令可能会执行,并且有空闲的资源来"免费"执行这个指令,就可以先投机地执行这个指令;如果这个投机是正确的,就能加速程序执行。如内存加载指令就能从中获取较大好处,很多现代高性能处理器都有对其的支持功能,如:
- 预取指令
- 毒药位
- 带断言的执行
下一篇:编译过程中的并行性优化(二):基本块与全局代码调度算法
我的GIS/CS学习笔记:https://github.com/yunwei37/myClassNotes <一个浙大GIS/CS小白的课程学习笔记 >