Zesto论文笔记:Zesto: A Cycle-Level Simulator for Highly Detailed Microarchitecture Exploration

来源:互联网 发布:华为手机网络助手在哪? 编辑:程序博客网 时间:2024/05/29 19:22
Zesto官网
http://zesto.cc.gatech.edu/


Related Work
SimpleScalar Family:
     很流行,是一个模拟器工具集而不是模拟器。
     Sim-outorder是一个样例,告诉别人如何使用这个工具集建立一个简单的时钟周期的模拟器。但是Sim-outorder太简单的,会使学生认为真正的处理器就是这样工作的,并且使用的逻辑架构是二十年前的。
     MASE是SimpleScalar2001年发布的,是一个比较详细的low level模拟,但是不支持x86指令集。

PTLsim:
     用了很多代码优化,性能很高,但是导致代码很难修改,并且有些地方还是不如Zesto详细。

PinTools:
     也是一个工具集,而不是一个模拟器。提供了强大的多样的计算机架构工具。但是没有提供一个对错误路径指令(如分支预测失败)的真实模拟。

其他的模拟器如SESC,GEMS,M5等,目前为止还不支持x86指令集。这些都是很好的模拟器,但是他们的硬件模型和对流水线的设定都不符合我们对高度细节的x86微架构模型的需求。

Overall  Organization

     由两部分组成,一部分是oracle "execute at fetch"功能模拟器,另一部分是详细的时序模拟器。
     
     
     因为对微架构的模拟很详细,所以速度很慢,大概为10's KIPS。也正是因为速度很慢,所以没有真实的"execute at execute"模拟。但是代码的的结构能很容易得扩展为execute at execute。

     Oracle功能模拟是以取指模块为基础的,如果取指模块对路径错误预测了,oracle也会顺着这个错误的路径执行下去,但是oracle保存了所有"in flight"的指令,所以当时序模拟发现了错误预测的时候,oracle能轻松的回滚。

     内存操作指令不会真的去修改模拟的内存,而是有一个辅助的内存数据结构用来保存所有"in flight"的store指令,而load指令要在这些store指令中寻找相关的内存数据,将这些内存数据和模拟的内存中的数据拼接。

     
     Oracle先将x86指令拆分成uops,然后执行每条独立的uop,这种方法能使指令在进去流水线之前就已经获取的所有的寄存器信息、内存值以及分支预测信息。
     流水线主要分为五个模块,取指、译码。分配、执行和提交。取指对宏指令进行操作,译码从宏指令到微指令,之后一直到提交之前都是微指令,提交要保证原子性所以要也需要访问宏指令。
     不同模块使用各自的数据结构,导致模拟器的代码比较冗余,并且开发者必须对数据是从哪个模块来的非常清楚。

Oracle
     功能模拟
     不支持64-bit code和SIMD extension instructions。
     

Zesto Microarchitectural Model
     五个主要的模块包含在"core"里,其他剩余的结构包含在"uncore"模块里。
     
fetch

     取指模块每次取一个cache line的指令,先告诉oracle要取这个cache line的指令,然后oracle会将这个cache line中的指令执行一遍,然后取指模块一次性取到整个cache line的指令,而大部分学术用途的模拟器都是每个指令向缓存请求一次。
     当遇到跨cache line 的指令时,oracle是在得到第一个cache line就执行了这个指令,但取指模块必须对请求第二个cache line,并且不能在一个cycle中发生,所以所有被分隔开的指令都必须在指令队列中等到剩余部分,然后才能进入下一次模块。
     大部分模拟器中,ITLB和IL1是两个独立的 缓存结构,对于取指的周期计算只是简单的取两个结构访问的max,但是实际上必须要在ITLB计算出地址之后IL1才能进行查找和判断,在这方面,Zesto
模拟得更加精确,并且还精确地模拟了miss的时候的等待周期。
     然后是对指令的pre decode,pre decode只是单纯地对指令进行切分。因为实际上oracle已经做了这部分工作了,所以不需要真的再去切分,但是需要根据硬件来模拟这个步骤对时钟和带宽的限制。
     最后是分支预测部分。包括了方向预测,BTBs,返回地址堆栈等组件。用户可以配置任意数量的预测组件,然后指定一个选择器将各个预测期结合起来。每一个单独的组件都可以是支持的算法中的任意一个,并且添加新的算法非常简单,并且meta-predictor可以使用各种不同的技术, classical tournamentbased selection, multi-hybrid selection, or fusion
techniques。
     

Decode
     每个周期可以有多个宏指令进入decode模块,主要还是模拟decode过程的周期。
     Zesto的模拟是"non-compressing"的,每个模块在每一个周期处理的指令会全部传输给下一个模块,这样就不用在每个模块判断需要释放多少,往后传输多少。
     decode是根据生成的uop数量来限制,并且支持uop融合,但是不支持宏指令融合。

     当一条宏指令对应的uops超过4条或者有复杂情况时,需要使用microop sequencer产生对应的uops序列。当碰到一个复杂的指令时,首先等待专门用来处理复杂指令的0号decoder,然后0号decoder与MS通信,得到对应的uops。
     对于循环指令,Zesto会加入额外的uops来实现循环,比如在执行其他的uop之前加入一条跳转指令, 判断循环是否结束,在每个循环的最后也要加上uop来更新对应的寄存器信息。Zesto把每一次循环看成独立的宏指令结构,但是在记录和统计的时候还是看成一条单独的指令。


Allocation
     分配阶段主要为uop分配硬件资源。每一个物理寄存器都对应着ROB中的一个entry,load和store需要等待load queue和store queue的entry。所有uop都需要一个ROB entry,所有non-NOP都需要RS entry。如果任何需要的资源不可用,都会进入等待状态。融合的uop只占一个RS和ROB的entry。
     分配阶段同时为uop分配执行单元。uop将被分配到有对应的执行单元的端口上,才用的是负载均衡的策略为其分配端口。


Execute

     采用的是每次选取最老的k条uop发射给执行单元,Zesto模拟了同一个端口的多条uop之间的竞争关系,某个端口下的uop不能放到其他端口去执行,许多模拟器没有考虑到这个,会导致过度乐观的指令调度。
     然后对uop进行读操作数,如果所有的操作数都读到了,就进行指令执行并且释放RS entry,对于融合指令,执行最后一条uop的时候释放RS entry。如果有操作数没有读到就需要重置对应的RS entry,重新进行调度。
     执行结束后,向依赖于该uop执行结果的uop发出广播,并且修改ROB中状态。
     每个执行端口只有一个bypass总线,因为一般每个端口一个cycle只执行一条uop,当有多cycle的uop时,一个端口可能会有多个uop同时执行完成,但是只有一条uop能做执行结束的这些处理并移交给提交,其他的要等下一个cycle才能处理。

Load
     load指令处理为单个load uop,在allocation阶段将load指令插入到LDQ中。当load指令执行的时候,先计算虚拟地址,写入到LDQ中。
     DTLB和DL与ITLB和IL的策略一致,要先在DTLB中命中物理地址才能去DL中找对应的数据。如果DL1miss则去DL2中找,并将指令放到MSHR中,等待被唤醒。Zesto没有详细地模拟多层cache,但是模拟了唤醒的策略,有限容量的MSHR,指令之间对总线的竞争和缓存端口的竞争。
     LDQ在去缓存中寻找数据的同时,会在STQ中查找这条LDQ之前的对这条load操作有影响的store操作,并且直接从STQ中读取数据,从STQ中查询被设计为和DL1命中一样的周期。但是Zesto只支持当只有一条store对当前的load有影响时的拼接,当有多条store有影响时,load操作必须等这些相关的store全部回写到DL中才能执行。
     假如我的load操作在STQ中全部命中了,就不再需要从缓存中取来的数据了,但是缓存还是在执行这个load的操作,占用着总线带宽和MSHR entry,甚至当缓存返回这条load操作的结果时,这条load操作早就被提交或者flush了。在真实的处理器中对这种情况有很复杂的向cache中追捕原始load指令的触发机制。(但是模拟的时候可以很简单的处理)


Store
     store的实现和load类似。不同的是store拆分为STA和STD,同一条store指令对应的两条STA和STD是完全独立发射的,但是在STQ中占有同一个entry。Zesto在模拟STD的时候,是先对STD进行读操作数,然后放入到对应的STQ中,有些模拟器是先将让指令占了STQ中的entry,然后再进行读操作数,这样和硬件不一致,因为硬件上STQ和bypass一般没有连线。
     当一条store指令的STA和STD的出结果之后,要去LDQ中判断有没有地址重叠的之前的load没有还没有执行的。特别的,地址判重非常复杂,Zesto是遍历每一个Byte去判重的,这也大大降低了了Zesto效率。


Commit
     提交的时候要保证宏指令的原子性,所以要等所有对应宏指令的uop都到齐了之后才能提交。
     Store指令要等commit信息返回之后才能往内存中写。commit之后的store指令也需要一个buffer,Zesto中这个buffer和STQ是同一个结构,每次接收到一条commit信号,STQ将对应的entry置入buffer中即可,load指令在找store的时候也要从这个buffer中找。但是这种方法是增加STQ的压力。     

Uncore
     除了主要的pipeline,Zesto还实现了这些组件:
          unified L2 cache
          L2 hardware prefetchers
          memory controller
          DRAM



   
Benchmark
     将模拟器配置为 Intel Core 2 (Merom),在模拟器上和本地硬件上跑同样的指令,只采用计时的方式测量,每个程序跑5次计算平均时间。




Zesto Multi-Core Support
     只是简单地模拟多个程序并行,而不是真正模拟多线程。模拟的时候是直接实例化n个core,然后共用一个uncore(包括L2)。多个core之间相互独立,只有在竞争L2时会有性能的影响。每个core跑一个程序,每个core有自己的oracle,为地址加标记来表明是属于哪个core的来防止错误的hit。


conclusion
     优点:对流水线的模拟非常精确,对x86的特性模拟得非常详细
     缺点:速度非常慢,对多核模拟支持差。
               并且不支持64-bitcode和SIMD extension instructions,已经停止维护,代码也下架了。
     Zesto主要用于对x86架构的处理器进行详细的模拟和研究,以及在教学上使用。






     
     
1 0
原创粉丝点击