高通吧 关注:168,166贴子:3,871,425
  • 26回复贴,共1

关于苹果的ROB设计

只看楼主收藏回复

有些朋友可能对目前苹果的ROB数据比较模糊,我总结一下目前数据有A站的630slot, dougall测的nop ~2300slot, 还有些写的是~330slot。那么这些数据到底哪一个是对的?其实要解答这个问题我们要先看苹果的ROB设计。苹果在Firestorm/Avalanche设计了用了一个7slot/rob row的设计,每个周期最少释放单位是一个row。但是所有会导致处理器flush pipeline的指令(比如分支指令(分支预测错误),store/load(load dependence预测错误,导致load后面的指令要重新执行 或者 访问地址出现异常))必须要占用每一行的最后一个slot。这些特殊指令导致了在真实运行的代码里往往达不到测试ROB size所得出的大小,举个例子:
add xn, xm
mov xn, xm
store [add], xn
这里虽然只有3条指令但是却需要占用7个rot slot也就是一行。这里store指令相当于起到了一个分隔符的作用。最坏的情况下如果每条指令都要占用一行(比如连续的load,那么rob大小会限制在320(firestorm)。
接下来我们谈谈History FIle,或者说PRRT(Physical register reclaim table),这名词都代表了一个东西。
我们知道在现代乱序执行处理器中,为了解决WAW, WAR,这类anti-dependence同时保持RAW这类real-dependence,都采用了寄存器重命名技术,在苹果的implementation中又一个逻辑寄存器到物理寄存器的mapping table,每当新指令到达rename stage的时候都会通过把自己source operand的逻辑寄存器通过查询mapping table替换成物理寄存器,然后为自己的destination operand申请一个新的物理寄存器,并且更新对应逻辑寄存器到物理寄存器的mapping。可是如果处理器在未来发生了异常、missprediction那么就需要刷新流水线回到异常发生时的状态。可是由于处理器是乱序执行的,mapping table的映射在每一个rename cycle都会被改变。那么如何回到异常发生时那一个周期mapping table的状态?很显然我们需要记录它的状态,这里苹果采用的是记录每一条指令对mapping table所做出的改变(保存在这条指令rename之前 mapping table对应的逻辑寄存器所映射的物理寄存器)。当发生异常时暂停最新的指令执行,从最新的指令所记录的上一个mapping开始往回走恢复到上一条指令的映射 并且释放当前指令的物理寄存器,一直到发生异常的指令。下面给个例子
Inst1: X1 <- P4, Old P1
Inst2: X2 <- P5, Old P2
Inst3: X3 <- P6, Old P3
Inst4: X1 <- P7, Old P4
这里Xn <- Pn代表了这些这条指令会对mapping table做出的改变,Old Pn代表了执行这条指令之前逻辑寄存器Xn所映射到的物理寄存器。
假设Inst1发生了异常我们需要从inst4开始回滚,具体操作是释放当前指令的物理寄存器并且把mapping table改变到执行这条指令之前此逻辑寄存器所map到的物理寄存器。这样一路回滚就可以把mapping table改回到之前的状态,从而继续从发生异常的位置开始执行。
那么知道完理论后,我们知道要实现回滚我们需要记录每条指令对mapping table做出改变之前的状态,History FIle就是用来做这个的,理论上我们是可以把它当成ROB的一个记录项,但是对于很多没有修改物理寄存器的指令来说这就是一种浪费,所以在苹果的实现里,他是单独的一个表(在苹果的专利里叫History Table)。m1这个表的大小是~630这也就对应了A站的数据。
具体测试方法是适用一条不需要占用物理寄存器但是会更改mapping的指令作为ROB的filler指令,比如zero-cycle mov, mov x5, 0。
我们前面说到ROB最少释放单位是7slot/1 row, 根据maynard Handley的数据每周期可以retire 56条nop指令也就是8row。


图源(https://github.com/name99-org/AArch64-Explore Volume 1)
而History Buffer由于涉及到改变Mapping table,释放的速度就会慢一些16slot/cycle(同时释放物理寄存器的数据也是16/cycle)


最后,我想说的是现代商用高性能处理器在不同的层面都做了大量的优化,很多具体实现的技巧课本是都不会说的,想要更深入的了解还是要多读paper,多看看Henry Wong等一系列研究处理器微架构的人的博客。引用Maynard Handley的一句话 “当你读完Hennesy & Patterson 的量化研究的时候你要意识到你仅仅完成了你路途中的10%, 不是90%”。
关于苹果处理器还有很多值得讲的部分比如如何处理指令间的依赖,然后处理OoO内存访问,但是碍于篇幅限制就先写到这里。有机会再继续探讨。


IP属地:广东1楼2023-09-07 18:19回复
    @孤影照惊鸿♋


    IP属地:广东来自iPhone客户端3楼2023-09-07 18:25
    回复
      Maynard Handley的文档
      https://github.com/name99-org/AArch64-Explore


      IP属地:广东5楼2023-09-07 18:27
      回复
        Henry Wong的blog
        https://blog.stuffedcow.net/2013/05/measuring-rob-capacity/


        IP属地:广东6楼2023-09-07 18:28
        回复
          以及dougall的m1测试代码这里链接发不了 代码在他的github gist上有


          IP属地:广东8楼2023-09-07 18:30
          回复
            🐮


            IP属地:福建来自iPhone客户端9楼2023-09-07 18:30
            回复
              虽然看不懂,但这个意思是不是说实践中这个rob最小是320,有时可以相当于630?


              IP属地:内蒙古10楼2023-09-07 18:31
              收起回复
                继续补充一点Firestorm

                ADD x0, x5, x5; FABS d0, d0; STR x5 [x2]
                这个指令序列下处理器可以执行930条这样的指令

                cmpx5, x5; str x5, [x2, #8]; b.ne.+0xF0; mov x5, 0
                这个指令序列下可以执行853条指令


                IP属地:广东11楼2023-09-07 18:34
                回复
                  哦 那我认为对于不同指令等效不同rob数的理解是没问题的,感谢。


                  IP属地:广东来自Android客户端12楼2023-09-07 18:45
                  回复
                    顶一顶技术贴


                    IP属地:山东来自Android客户端13楼2023-09-07 18:57
                    回复
                      现代cpu方案很成熟了。差的就是那点优化和设计,现代意义上的cpu,在奔四时代已经很完整了。而酷睿的设计思路至今存在于所有现代意义上的cpu中。


                      IP属地:安徽16楼2023-09-07 19:05
                      收起回复
                        这么看来苹果的乱序执行能力也吃优化?如果把关键指令打散的话能明显提高ROB的实际容量


                        IP属地:上海17楼2023-09-07 22:39
                        收起回复