实验内容一:利用WinDLX模拟器运行以下两段程序。
1)程序段1的执行周期数是多少?分析程序中出现的暂停,都是由什么原因导致的?出现了哪些相关,导致这些相关的原因是什么?各种相关暂停的比例是多少?建议结合clock cycle diagram进行分析,计算周期时请指明设置的各运算单元的周期数。
答:在WinDLX中执行程序段1,这里要声明的是文件需要以S文件的形式保存,TXT文件winDLX好像读不进去。
导入之后,程序段顺利运行,可以通过statistics查看执行周期数,由下图可知,执行周期数为130个周期。
分析程序中出现的暂停:
程序中出现的第一个暂停是数据相关,因为MEM段才获得了f1,所以第二个指令要用f1就得等一个周期。
程序中的第二个暂停是结构相关,因为除法运算单元没有流水化,这就导致了在前一个除法运算指令完成之前,不能够再有第二个除法指令来执行。这是硬件导致的暂停。
程序中的第三个暂停是数据相关,因为指令要用的数据没有计算完成,所以在EX阶段之前暂停等待之前的两个指令计算完成。
程序中的第四个暂停是数据相关,也是需要用的操作数f3还没有计算完成,所以进EX段之前需要等待。
程序中的第五个暂停和第二个暂停一样,是结构相关,因为除法单元没有进行流水化,所以在前一个除法指令计算完成退出EX段之前,后面的除法指令都只能等待。
程序的第六个暂停和第五个一致,也是结构相关。
程序的第七个暂停是数据相关,因为乘法指令用到的操作数是前一个指令的除法运算的结果,所以要等除法运算完成前送之后才可以进EX段
程序的第八个暂停也是数据相关,虽然是除法指令,但是之前的除法指令因为数据相关的原因,已经计算完了,而用到的操作数f6的乘法计算需要四个周期,还没有计算完成,所以需要等待
出现的相关分析:
在程序中出现了两种相关,首先是结构相关,出现原因为除法单元没有实现流水化,刚好程序中有多处连续使用除法指令,导致出现结构相关。
其次是数据相关,且多数都是RAW相关的数据相关,后面指令读取数据要在前面指令计算好数据之后,才可以通过前送给到后面指令的EX段。
各种相关暂停的比例:
可以通过statistics查看暂停的比例:
数据结构导致的暂停有42个周期,占全部周期的32.31%
结构相关导致的暂停有54个周期,占全部周期的41.54%
也可以通过推荐的clock cycle diagram来查看暂停的比例:
经过对于diagram的手动统计,也可以获得和上述一致的暂停比例:
数据结构导致的暂停有五次,分别暂停了1,18,1,18,4个周期,总计是42个周期,占程序总共使用的130个周期中的32.31%
结构相关导致的暂停总共有三次,均暂停了18个周期,总计是54个周期,占程序总共使用的130个周期中的41.54%
这里给我印象比较深的还是除法单元部分,因为之前在课上学习的时候除法单元应该是占用15个周期,但是这里一个个周期去数的时候,发现DLX中的除法运算使用了19个周期,这也导致了连用除法指令导致的结构相关暂停或者要后续指令要用除法运算结果的数据相关暂停为18个周期。
接下来还有浮点加法运算单元,在DLX中,浮点加法运算需要两个周期的时间,这部分就是数据相关的五次暂停中,暂停了1个周期的部分。
还有乘法运算单元,在DLX中,乘法运算需要五个周期的时间,这部分就是数据相关暂停中,暂停了四个周期的部分的暂停原因。
2)对比程序段1,程序段2的周期数是多少?与程序段1相比,程序段2有何不同?(可用如下思路:例如:程序1采用XX方法,减少了程序2中的XX相关导致的暂停周期数,或者增加了XX相关,XX暂停周期数。)
答:和程序段一一样操作,通过TXT文件另存为成S文件后,导入虚拟机,使用winDLX执行,运行完毕后,可以通过statistics查看到程序段2的周期数为124个周期。
和程序段1相比,程序段2的不同之处在于程序段2有意识的将没有数据相关的除法运算放在一起连续执行,把数据都算好了,再执行需要数据的乘法运算和浮点加法运算,最后再把用到乘法运算和浮点加法运算的除法指令执行,在保证最后结果正确一致的前提下,通过顺序的调整减少了数据相关的发生次数和暂停周期,因为都是尽可能把用到的都算好再执行的,但是这样的调整就导致有一个连续四个的除法指令和一个连续两个的除法指令,这会带来大量的结构相关和暂停。这里直接取程序段2的statistics就可以看的出来:
3)上述两段程序中是否存在结构相关?Why would a designer sometimes allow structural hazards?
答:很明显上述两个程序段都存在结构相关的问题,因为除法单元没有流水化,所以一次只能算一个除法指令,只有前面的计算完成后,才可以执行后面的。
允许结构相关的原因:其实解决这个结构相关的方法也很明确,就是参考整数单元,浮点和整数乘法器,浮点加法器这些功能单元,将浮点和整数除法器流水化,可以多个指令一起进入EX段,但是问题在于除法操作要用到的周期太多了,像本次实验中观察到的除法操作EX计算部分使用了19个周期,这就意味着如果要流水化,这19个周期就要全部分开,这就意味着大量的站间寄存器和数据转移的操作,这里的开销是很大的,又考虑到除法操作在整体指令集中占比不是特别高,并且结构相关本身只影响性能,不影响程序的运行结果。所以不进行除法单元的流水化,保留结构相关是性价比更高的选择。
实验内容二:将以下程序段修改为可利用WinDLX模拟器运行的程序。假设R3的初始值为R2+40
1)观察分析使用定向技术与不使用定向技术的情况下,该程序段的执行周期数。画出在使用定向技术的情况下,一个循环的流水线时空图,并根据一个循环的时空图推算出整个程序的周期数。对比是否与模拟器中的clock cycle diagram以及程序中统计出的周期数一致。
答:首先,参考上一个实验中提供的代码结构,将给出的程序段放进main程序中,直接运行:
不出意外的,winDLX在执行过程中报错,因为我还没有对R3进行初始化,所以这次只是简单尝试一下:
在LOOP之前加上了对于R3的初始化之后:
再次执行,程序顺利运行:
这时的winDLX程序是使用了定向技术的,这点可以从configuration中查看,或者在执行完程序之后,打开clock cycle diagram,看到箭头也可以得出使用定向技术的结论:
在使用了定向技术的前提下,我们可以通过statistics来查看整个程序段执行的周期数:
可以看到,在使用了定向技术的前提下,程序总共执行了95个周期。
接下来,关闭定向技术:
将文件load进来,执行程序,程序顺利执行,从clock cycle diagram中可以看到没有使用定向技术,每次都是通过暂停流水线的方式解决的数据相关,所以同样的位置,和上一个图相比多了不少的R-stall:
同样通过statistics来查看整个程序段执行的周期数:
由图可知,在没有使用定向技术的情况下,程序段执行要使用155个周期。
由给定的程序段,自行绘制使用定向技术下的流水线时空图:
其中一次循环需要12个周期。因为流水线中使用的是预测转移失败的策略,所以两个循环之间共同的部分有3个周期,程序总共执行10次循环部分,最后有一个trap 0,前面有一个R3的初始化定义,所以总共是1+12+9*9+1=95个周期,和statistics中说明的周期数一致。
2)一次循环中的暂停周期数是多少,引起这些暂停的原因是什么?一次循环中有哪些相关,引起这些相关的原因是什么?
答:一次循环中的暂停周期数为2个周期,可以通关观察流水线时空图得知,因为是整形流水线,6个指令,没有暂停应该是5+1*5=10个周期,通过计算也可以得知一次循环有12-10=2个周期的暂停。
引起第一个暂停的原因是:第四个周期是数据相关,前一个指令是load指令,在MEM段才获得R1,所以后面的指令需要等一个周期,在EX通过前送才可以获得。
引起第二个暂停的原因是:第九个周期也是数据相关,在将转移目标计算和转移条件测试挪到ID中,在IF写PC的改进之后,转移指令在ID段就需要知道R4的数值,但是前一条指令在EX段才完成计算,所以需要等一个周期,R4才能够通过前送给到转移指令的ID段。
在一次循环中有数据相关和控制相关,引起数据相关的原因是指令在流水先中重叠执行,可能会导致读写操作数顺序的改变,数值结果不同于在非流水线情况下执行的。引起控制相关的原因是转移指令导致的PC数值的改变。
3)为了减少控制相关带来的暂停,我们学习了多种方法,该模拟器中使用的是哪种方法?该方法的思路是什么?在什么情况下可得到改进?
答:在模拟器中使用的显然是预测分支转移失败的方式,因为我们可以看到,在十次循环中,每次执行条件转移指令之后,后面都会先读trap 0指令,发现转移成功之后再读入其他指令:
这种方法的思路是:流水线继续照常流动,如果分支转移成功,将分支指令后的指令转换为空操作(cancel掉),并从分支目标处开始取指令执行,否则照常执行,同时,取消的指令不会对数据造成影响,因为被取消的指令是必不可能走到WB段的
这样的方法在指令不转移的时候会改进性能,在指令转移的时候也至少不会降低性能。因为获得转移条件和转移地址本身就需要一个周期。
实验内容三:将上述程序段3修改为可利用WinMIPS64模拟器运行的程序。在使用定向技术的情况下,对指令进行重排,采用delayed branch来减少控制相关带来的损失。进行分析:延迟槽中调度的是哪条指令?为什么选择该指令?调度后的程序与调度前的程序周期数相差多少?改进是如何体现的?
答:首先,在课程平台中下载WinMIPS64模拟器,和之前的WINDLX不同,这款软件可以直接在电脑中解压使用,不需要使用虚拟机。
解压完成之后得到的软件界面如下:
其中各个界面的功能和命名和winDLX类似,也可以直接按照winDLX来理解这些界面的功能。
虽然网络和平台上没有找到关于WinMIPS64指令的具体介绍和说明,但是通过参考使用winMIPS64的测试文件test.s中的代码,可以总结出一些不同于之前winDLX的代码段的不同点:首先就是指令,除开load-store之外的计算指令前面都加了一个D,另外就是立即值不需要前面加#号,加了#号的话会按0处理。最后就是结束的指令修改为end即可,基于这些,修改后的代码如下:
代码导入到模拟器中:
这里文件可以导入进去,但是会报错:
查看代码,发现在store语句处被标红:
通过具体查询MIPS中的load-store指令,我发现了问题:
在MIPS中,store指令中赋值的寄存器位置和DLX中的相反,所以需要调整SW语句,修改后代码如下图:
再次将文件输入进WinMIPS64中,这次就没有报错了,程序可以顺利执行:
可以看到在初始情况(即没有定向技术的前提下)执行周期也是155个周期,在打开了定向技术之后,执行总共使用了95个周期,两者均和之前使用winDLX的结果一致。
接下来开始通过重排指令,调整delay branch,减少控制相关带来的损失。
目前的程序中使用的减少控制相关带来损失的方式是预测转移失败指令,这点可以看实验内容二的说明。而延迟槽的设定和这里的预测转移失败指令也有一定的相似之处,都是要利用转移语句需要一个周期的时间计算是否要转移和转移指令的前提,利用他们计算的这一个周期去执行一个其他的指令,这样一来就算这个指令没有用也不会拉低性能,用上了性能自然会提高。不过区别在于延迟槽的指令时一定会执行完的,而预测失败的在确认转移成功后就会cancel掉执行的指令。
在确定了原理之后,就可以选择将哪一条指令放置在延迟槽的位置了。在程序段三中,主题的指令只有六条,还有一条是转移指令,而余下的五条指令之间有着明确的先后关系:取指,计算,存入内存,计算,所以其实只有第一条取数值的指令可以移动到延迟槽而不影响结果。
而如果要将第一条指令放到延迟槽的话,就需要对循环进行一下调整,要将之前第一条指令拿出循环,不然的话就是两次连续的load操作,虽然从编码的人的角度来看,这是两次一样的操作,前一次load好,直接前送就可以了,不用管第二个,但是在编译的过程中,为了避免RAW问题,虚拟器还是要等前面的指令完成,那样的话设置延迟槽就完全没有意义了。另外还要记得最前面留一个load指令,不然第一次循环没有R1的。
所以,经过上述考量,我最后修改的程序段如下图所示:
在WinMIPS64中,打开延迟槽功能:
执行修改过后的,开启了前送和延迟槽功能的代码,得到的结果如下:
可以看到执行周期为87个周期,相比于之前的95个周期,性能提高了8个周期,之所以是8个周期,是因为第一个循环的时候延迟槽的load用不上,总共减了九个周期,而之前的预测转移失败方法在最后确实失败的时候节省了一个周期,而延迟槽最后一个周期因为taken是没有节省的。所以相比于之前的性能提高了八个周期。
而在确定了性能的提升之后,还需要确认的一个点就是结果的正确性,毕竟是对于程序段进行了修改的:
可以看到结果和之前没有使用延迟槽的时候一致,说明指令的重排和延迟槽的添加没有影响最后的结果。
改进的体现:
下图为改进之前的流水线时空图:
下图为改进后的流水线时空图:
可以看到,load指令的执行与转移指令相比,修改之前的load指令在转移指令的EX段开始取指,修改之后的可以再ID段就进行取指操作,所以性能得到了提升和改善。