欢迎光临散文网 会员登陆 & 注册

自编教材分享:第七章—指令级并行(二)

2023-10-23 15:07 作者:先进编译实验室  | 我要投稿


指令级并行

程序的执行由一系列指令操作组成,为了节省程序的执行时间,发射单元可以一次发射多条指令,这就是指令的多发射并行。多发射处理器支持指令级并行,每个周期可以发射多条指令,一般为2-4条,这样可以使处理器每个时钟周期的指令数倍增,从而提高处理器的执行速度。多发射处理器的每个发射部件上同样可以进行指令流水,如下图。

指令多发射的方法有超标量(Superscalar)和超长指令字(Very Long Instruction Word,VLIW)两种,它们的不同之处在于并行发射指令的指定时间不一样。

超长指令字在编译阶段由编译器指定并行发射的指令,而超标量在执行阶段由处理器指定并行发射的指令,因此超标量的硬件复杂性更高,而超长指令字硬件复杂性较低。超标量通常会配合乱序执行来提高并行性,下面介绍乱序执行是如何提升超标量处理器性能的。

采用超标量技术的典型代表为X86处理器,采用超长指令字的典型代表为ARM和德州仪器TI。

超标量

如果程序中的所有指令都按照既定的顺序执行,那么一旦相邻多条指令不能并行执行,处理器的多发射部件就处于闲置状态,造成硬件资源的浪费。采用乱序执行可以很好的解决该问题,乱序执行就是程序不按照既定的指令顺序执行,在指令间不存在相关性的前提下通过调整指令的执行顺序以提升程序指令的并行性,是提高指令级并行的一种重要方式。

以下面指令段为例说明乱序发射:

add  R3, R2, R1   #指令1

mul  R1, R0, R4   #指令2

mul  R6, R5, R7   #指令3

如果按照顺序发射,指令2与指令1之间存在有R1引入的数据相关性,因此指令1与指令2不能并行。指令2和指令3同时使用乘法部件,此处假设该处理器上仅有一套乘法部件,因此指令2和指令3因为乘法部件资源数量的限制也不能并行。使用超标量结构是需要代价的,处理器内部将使用一定的资源用于将串行的指令序列转换成可以并行的指令序列,这会增加处理器的功耗和面积,而超长指令字则不需消耗过多的处理器资源。

乱序执行调整执行顺序后代码段为:

add  R3, R2, R1   #指令1

mul  R6, R5, R7   #指令3

mul  R1, R0, R4   #指令2

超长指令字

超长指令字处理器的每一条超长指令装有多条常规的指令,并于同一时刻被发射出去。超长指令字结构广泛应用于(Reduced Instruction Set Computer,RISC)精简指令集的处理器上,典型代表为(Digital Signal Process,DSP)数字信号处理器。以德州仪器TI的C6000系列数字信号处理器为例。图中处理器有8个功能部件,理论上每个时钟周期处理器可以同时并行执行8条指令,这8条指令被看成是一个包。

超长指令字处理器通常利用编译器指定并行的指令,也可以由优化人员在汇编中指定并行的指令,如下面的汇编代码所示。指令2、指令3、指令4前面的“‖”,表示这条指令和上条指令在同一个周期执行,如果没有“‖”,则表示这条指令在下一个周期执行。超长指令字的并行指令执行由编译器来完成,因此编译器的优化能力直接影响到程序在超长指令字处理器上的性能。为了在超长指令字机器上获得更好的程序性能,优化人员可以通过修改汇编代码,指定每个多发射的超长指令。

add R1, R2, R3   #指令1

‖ sub R1, R2, R6  #指令2

‖ mul R3, R4, R5  #指令3

‖ and R8, R6, R7  #指令4

以德州仪器TI的C6000处理器为例进行说明,C6000处理器包含如图7.7所示的四种类型功能部件,分别标记为S、L、D、M。S部件主要执行算术运算、数据打包解包以及比较运算等,L部件主要执行算术运算、打包解包运算等,D部件主要执行访存和逻辑运算,M部件主要执行乘法运算和点积运算,每类部件又对应两个通道,理想情况下每个时钟周期可以将8个部件同时利用起来,实现8条指令并行发射。

如下面的指令片段,充分利用了上述八个功能部件,可以有效的提升指令执行效率。在一个时钟周期内将硬件提供的各种功能部件使用起来,就可以充分发挥处理器的指令级并行优势。

ADD  .S1  A1, A2, A1

‖ ADD  .S2  A4, A5, A4

‖ MUL  .M1  B1, B2, B1

‖ MUL  .M2  B4, B5, B4

‖ SUB  .L1  A3, A6, A3

‖ SUB  .L2  B3, B6, A3

‖ LDDW .D1T1 *A7++, A9:A8

‖ LDDW .D2T2 *B7++, B9:B8

实际程序中受限于指令间的相关性以及指令的数量不足,往往不能充分发挥处理器提供的指令级并行优势。此时可以利用循环展开进行优化,发掘程序指令级并行性。以下面这段矩阵乘为例。该程序循环内的指令条数太少,不足以填充满C6000处理器的8个功能部件,难以充分发挥处理器提供的指令级并行的优势。

循环展开优化后,循环体内指令的条数足够多,编译器就可以在这些指令间进行调度,选择相对较好的并行指令发射组合,因此能够提升程序的性能。以德州仪器TI6678计算平台上矩阵乘汇编为例。每个连续带有“||”的语句的前面都有一条语句没有“||”的语句,这些语句为一组是并行发射执行的关系,所以共有三组并行发射的语句。由此可以看出调度器优化之后指令并行性更好,且优化后的指令没有过多的跳转语句,如BRANCHCC语句。除了编译器可做优化外,优化人员还可以在汇编层面进一步指定并行发射的指令组合。

原始代码:

指令调度优化后:


自编教材分享:第七章—指令级并行(二)的评论 (共 条)

分享到微博请遵守国家法律