uops 我来自哪里

在上篇文章解答完“我是谁?”这个问题后,接下来试着回答“我来自哪里?”这个问题,这一切都要从CPU的Front End说起。前端主要实现取指和译码,即Fetch和Decode这两大重要的步骤,为后端提供需要被处理执行的命令,并且这一切都是在井然有序中完成的。

可以把Front End想象成Core内负责接收上级命令,然后对内发号施令的中层领导。Front End​从外部接收到x86指令,然后把这些指令翻译成内部更好消化处理的uops,并且由于前端还可以在拿到足够信息做出下一步决策之前,提前拿到完整的执行步骤,所以前端还可以提前按照自己的经验判断,先执行可能将要实施的计划,以揣摩出上层领导的想法,让领导满意。为了提升自己传达指令的效率,前端还把曾翻译过的内容保存了下来(DSB),并且招聘了多个翻译专家(Multiple Decoder),目的都是更快地获取领导想法,更及时地传达相应的命令,以让后端更快地运作起来。

本文目的​是借回答“uop来自哪里?”这个问题来帮大家梳理x86的Front End部分,看完本章大概会对下图的Skylake Front End每个模块的功能以及在取指和译码的流程中扮演的角色有个大概的认识。虽然不会深入到每个模块内部的底层原理和细节,但是至少后续看一些CPU发布会时,对于一些架构的改进和演变,再也不会如同听天书一般。所以在本文最后也会拿目前Intel最新的Alder Lake架构上的改进做简短的介绍和分析,以检验本文内容是否对大家有所帮助,顺便也让大家了解Intel最新架构上Front End的改进。

Skylake Front End


我来自哪里?

在我诞生后不久,就被送往了MUX,然后在这里被选中后,紧接着就被送往了IDQ(Instruction Decode Queue),在IDQ中,我遇到了许多uop同类,一打听,原来还有来自不同地方的uop,但我们最终都在IDQ相遇,等待被送往后端前线,真真正正去干一番事业。通过整理,发现和我一样先经过MUX再来到LDQ的,除了有和我一样来自MITE (Macro Instruction Translation Engine)的,还有来自DSB (Decoded Stream Buffer)和MS (Micro Sequencer)的小伙伴。然后还发现有没有经过MUX就直接出现IDQ的,这些小伙伴竟然直接出现在IDQ内部,据说是LSD (Loop Stream Detector)这个模块搞的鬼。

我还通过向MUX和IDQ打听了解到,最早出现的是我们来自MITE的uop,据说一切之初,都是源自MITE中的x86 Instruction, 所以想要探究uop来自哪里,还得先探究这些macro instruction来自哪里,然后是怎么转变成我们这些uops的。经过一番打听,发现这些Instructions源自一个个的Bytes,而这些Bytes是通过Instruction Address找到的。即在最初的最初,Core拿到的只有Address,然后这些Address经过处理,变成了一个个Bytes,经过检索对照之后,才变成了x86指令,然后这些x86指令再一次经过译码,最终才变成了我们这一个个uop。

LIP (Linear Instruction Pointer)

原来,最初这些address出现在LIP中,LIP会告知IFU (Instruction Fetch Unit)这个周期需要fetch哪个线性地址,IFU就拿着从LIP得到的信息去执行后续的操作,下个周期LIP会更新新的线性地址再给到IFU去执行取指操作。关于LIP下个周期要更新的地址,完全要听BPU (Branch Prediction Unit)的指挥,BPU的作用是预测指令分支的方向,故给到LIP的命令就只有两种,要么是不需要跳转,继续向前执行,要么就是提供需要跳转的地址,让LIP更新成相应的地址。

对于不需要跳转的情形,BPU会发出NT (Not Taken)的信息,LIP便只需要把目前的地址再添加32即可,即加上0x20,相对于的就是32B/Cycle,至于为什么是这个步进,只能说步伐跨太大了后续其余模块也跟不上啊。但是不同的微架构这个值有可能不一样,而且随着后续的技术发展,这个值也有可能变化。对于需要跳转的情形,那就简单很多了,无脑地把BPU提供的地址更新到LIP即可。

IFU (Instruction Fetch Unit)

IFU从LIP拿到线性地址后,要实现的目的就是拿到这个线性地址所对应的Bytes(其实也能直接通过查找DSB获取对应的数据,由于后面会详细介绍DSB,这里先不提),要获取对应的Bytes前,IFU还需要先把线性地址(虚拟地址)转换成物理地址(这里只考虑常见的保护模式下的情形,感兴趣的朋友可以去了解下x86实模式和保护模式),然后才能拿着物理地址从Cache或Memory中取得相应的Bytes。

所以相应的,IFU需要和ITLB (Instruction Translation Lookaside Buffer)和IC (Instruction Cache)打交道,通过访问ITLB,把拿到的地址转换成真实的物理地址,然后再拿着地址去找L1 Instruction Cache,如果Miss就会去后几级的Cache或Memory中查找,最终拿到对应数据。至于TLB地址转换的过程,和如何通过物理地址拿到对应数据的过程这里就不展开了,具体细节网上有很多相关的详细资料。

MITE (Macro Instruction Translation Engine)

IFU成功取得对应的32Bytes数据后,就开始了MITE的处理流程,即下图中红框的部分。最先拿到这些数据的是ILG (Instruction Length decode and Generate)模块,对应的是图中的Pre Decode,上一节提到x86指令是CISC指令集,所以还需要ILG来处理这些杂糅在一起的不定长指令数据块。ILG的处理能力是16B每个周期,然后x86指令的长度有1B至15B,如果遇到某条指令跨越了16B的边界,ILG会记录下来,然后和后续收到的16B拼接起来。

ILG会将处理得到的指令发往IQ (Instruction Queue)以进一步处理,从图中可以看到Skylake每个周期可以从ILG向IQ发送6个MOP(Macro Operation),这个宽度不是固定的,老一些的处理器如Haswell每个周期只能发送5个MOP到IQ,后续新架构还有可能增加MOP的宽度。然后IQ在Skylake中的大小是50个entry,开启超线程的话是每个线程分得25个entry,可以把IQ理解成一个指令Buffer用于缓冲。IQ再把MOP发送到ID (Instruction Decode),这里的宽度是5 MOP/cycle,老架构Haswell中是4 MOP/cycle,也在不断变化。

由于IQ到ID的宽度是5MOP,所以对应的Decode是一个五路的译码器,负责把IQ中的x86指令译码成uop,Decode之后终于可以看到uop的身影了。观察图中可以发现,这五个译码器中,有一个是复杂译码器,其余四个是简单译码器,他们的区别就是复杂译码器可以处理所有的x86指令,哪怕是复杂的指令,这种复杂的指令往往要翻译成多个uops来执行,简单译码器就只能处理那些简单的只会翻译成一个uop的x86指令。然后译码器会把翻译得到的uops按照顺序,最多一个周期往IDQ发送5个uops,至此,就完成了整个MITE的处理流程,将从IFU拿到指令数据转换成uops。

ID除了往IDQ中发送uops,其实还会往一个叫做BAC (Branch Address Calculator)的模块发送branch的相关信息,BAC经过处理再把一些有用信息给到BPU,然后BPU才有足够的信息做出相关的判断,但是这一流程在图中没有画出。因为只有在decode之后才能获取到相关的branch信息,在此之前,我们是不知道该地址或该数据块中是否存在分支的。由于这里牵涉到分支预测的内容,就不展开细说了。

细心观察下图,在IQ中我们发现还有Macro Fusion的绿色框框,似乎与IDQ中的Micro Fusion遥相呼应,这里简要介绍下这两个Fusion的模块。顾名思义,fusion就是融合,可以把多个融合成一个,Macro Fusion就是把多条Macro Instructions融合成一条Macro Instruction,以减轻后端译码的带宽压力。然后IDQ中的Micro Fusion是将多条uops融合成一个复杂的uop,虽然还是得执行多次,但是只需要占用一个entry,由于IDQ发往后端之后,是需要占用ROB一个entry的,所以也是有其意义的。

Macro-Operation Fusion (MOP Fusion) - WikiChip

Skylake Front End MITE


DSB (Decoded Stream Buffer)

在ID往IDQ发送uops的时候,同时也会将这些被译码的uops记录在DSB中,这样后期如果还需要取指译码相关地址,那么就可以直接从DSB中取得对应地址的uops,不需要重新执行IFU和MITE的流程,节省了能耗的同时还提升了性能,一举两得。

那么在前面阶段,是怎样得知要使用DSB中的数据呢?还记得前面刚刚提到过的LIP吗?LIP会从BPU拿到相关信息,是否继续取下一个32Bytes还是使用BPU提供的地址,其实同时进行的还有DSB查找的操作。DSB查找的Tag使用的是LIP地址,如果该LIP在DSB中hit了,表明该地址经过了取值和译码的流程,已经有相关结果保存在DSB中,那么就可以跳过上述的IFU和MITE流程,直接从DSB中向IDQ发送相关的uops。

MS (Micro Sequencer)

在ID的左边,我们可以发现还有一个MS ROM,从上图可以得知,MS也能往IDQ发送uops。我们再看下ID中的复杂译码器,图中显示的是1~4 uops,大家可能会有疑问,作为CISC的x86指令,不应该有些老复杂的指令需要被翻译成N多个uops吗?难道最多4个uops就可以翻译完吗?其实对于这些特别复杂的指令,就需要MS ROM的援助了,这些巨复杂的指令的前4个uops由MITE提供,然后后续的uops序列就会由MS提供,由于MS ROM是有一个ROM的,存放像CPUID这种老长的x86指令根本不在话下。

有些朋友可能会问,如果像CPUID这类复杂指令之前已经经过MITE和MS,还会把这么一长串的uops记录在DSB中吗?这显然是没必要的,DSB也只需要保存MITE提供的前4个uops就足够了,后续的uops序列还让MS提供即可。MS ROM每个周期可以往IDQ发送4个uops,当所对应的uops全部发送完后,就会把发送uops的权利交还到DSB或者MITE。

至此,LDQ前一个MUX模块的三个uops来源我们都已经一一梳理过了,那么他们为什么先要经过一个MUX选择器才可以到达IDQ呢?MUX的作用是使得IDQ每个周期只能接收来自DSB,MS ROM或MITE其中一个通道的uops,避免一个周期接收到来自其中两个甚至三个渠道的uops,以方便排序管理,这就是MUX在这其中发挥的作用。
 

LSD (Loop Stream Detector)

在IDQ中,我们发现还有一个左边绿色的方框没有提到,即LSD,从名字可以了解到,这是一个用于识别循环uops流的模块。由于在IDQ中存放在许多uops,在这堆uops中发现一些uops循环模式也是很正常的,一旦LSD认定了某个uops loop stream,就可以直接从IDQ中反复发送LSD识别出来的循环uops序列,直至分支预测错误为止,即循环结束。

在Haswell中,LSD总共可以识别出长达56 uops的循环序列,如果开启了超线程,那么每个线程可以识别出长度为28 uops的循环序列,当然,在新的每一代架构中,LSD的能力也在不断提升,以求识别出更长的uops循环序列。需要注意的是LSD所识别的uops序列需要全部都来自于DSB,即已经被BPU识别出来过的循环跳转,表示这段uops循环序列之前已出现过,而不是第一次出现在IDQ中。

SE (Stack Engine / Stack Pointer Tracker)

目前,图中的所有模块几乎都涉及到了,除了右下角的SE模块还未被提及,网上关于SE的介绍资料比较匮乏,在Intel的优化手册中也没有找到这个关键字,比较接近的模块就是Stack Pointer Tracker了,通过浏览下面的回答,几乎可以确认SE就是Intel优化手册中提到的Stack Pointer Tracker。由于这个模块与uops的诞生关系不大,有兴趣的朋友可以看看下面的简要介绍,权当科普,没兴趣的朋友也大可以直接跳过,这仅是一个针对堆栈相关指令的优化处理模块。

What is the stack engine in the Sandybridge microarchitecture?

要理解这个模块的功能,首先要理解x86指令中的PUSH, POP, CALL, LEAVE, RET这一类指令,这些指令用于更新堆栈指针寄存器(RSP),维护栈内的参数,并且都是无需其他指令干预隐式完成的。

The Intel 64 and IA-32 architectures have several commonly used instructions for parameter passing and procedure entry and exit: PUSH, POP, CALL, LEAVE and RET. 
These instructions implicitly update the stack pointer register (RSP), maintaining a combined control and parameter stack without software intervention. 
These instructions are typically implemented by several micro-ops in previous microarchitectures.

在老式架构中,这类指令会被译码成多个uops操作,于是Intel引入了Stack Pointer Tracker,使得Core在Front End处理阶段便可以隐式更新堆栈的相关信息,这样不仅仅可以节省前端的带宽,还会节省后端执行的带宽,与此同时,还可以更好地乱序执行和节省一部分能源,可谓好处多多。

The Stack Pointer Tracker moves all these implicit RSP updates to logic contained in the decoders themselves. The feature provides the following benefits:
• Improves decode bandwidth, as PUSH, POP and RET are single micro-op instructions in Intel Core microarchitecture.
• Conserves execution bandwidth as the RSP updates do not compete for execution resources.
• Improves parallelism in the out of order execution engine as the implicit serial dependencies between micro-ops are removed.
• Improves power efficiency as the RSP updates are carried out on small, dedicated hardware.

总结

经过上面的梳理介绍,现在已经有足够的信息来回答“我来自哪里?”这个问题。我们可以整理出uops可能会来自于MITE, DSB, MS和LSD这四个地方,然后这四个地方uops具体的诞生方式也各有不同。作为最复杂的MITE,先要IFU的协助获得相应的数据,才能进行相关译码操作,自然是最费时间和能源的。所以架构师引入了DSB和LSD来避免频繁地利用MITE获取uops,在DSB和LSD在工作的时候,就可以完全关断MITE和IFU相关模块的电源,以到达省电的目的。对于MS,这是x86作为CISC指令集所特有的一个模块,由于需要一个ROM来存放这些复杂指令的执行步骤,自然就可以让这个模块直接生成相应的uops序列。

uops fromMITEDSBMSLSD
步骤Addr -> Bytes -> Inst -> uopsSent uop from uop CacheSend uops flow from MS ROMRe-dispatch uop loop from IDQ
能耗
速度最快

Alder Lake Front-End Improvement

通过上述对Front End各个模块的逐一讲解,相信大家对CPU 微架构中的前端部分各个模块所扮演的角色有了一定的理解,并且知道他们是如何参与到取值和译码这一过程中来的。下面我们来看下2021 Intel Architecture Day Presentation[1]中提到的Alder Lake Performance Core微架构中Front-End部分的改进,看下我们是否都能理解其中提到的微架构相关改进,以及其背后的含义。

2021 Intel Architecture Day Presentation

首先我们可以发现Decode有1~6六个序号,然后uop Cache有1~8个序号,这里表明了在最新架构中Decoder变成了6个,一个周期可以同时往IDQ发送6个uops,然后uop Cache的宽度也有所提升,每个周期最多可以往IDQ发送8个uops。

再来看看PPT中提到的改进部分,Smarter表示BPU变得更加聪明,提高了分支预测的准确率;Large Code这块提到的是TLB和BTB容量的提升,由于本文中没有涉及到这块,所以目前不展开细说;Wider这部分的提升表明现在可以每个周期Decode 32 Bytes了,再也不是原先的16 Bytes/Cycle,然后译码器从4个变成了6个(4个应该是Haswell这种老架构了,在Skylake就已经是5个了),然后uop Cache每周期可以发送的uops数量从6个提升到了8个(其实这两点可以直接从图中得出);uop Cache的改进就是直接把容量提升成4K uops entries,然后还提升了命中率,目的是为了提升uops直接从DSB中获取的比重,从而避免经过IFU和MITE的流程,以节省能源和时间;IDQ的也扩大了,目前每个线程可以存放72个entries,如果关闭超线程,还可以直接翻倍变成144个entries。

看来通过梳理Front End的执行流程和各个模块的功能,目前已经可以看懂CPU发布会中关于微架构Front End改进的相关介绍了,再也不是听天书的感觉了。当然,本文的讲解都比较肤浅,如果想要深入理解各个模块的细节,还需要各位花上一定的功夫去钻研,其实只要把其中一个小模块吃透了,并且能够融会贯通,就大有可为。


以上就是对uop“我来自哪里?”这个问题的简要回答,下一篇文章将会试着回答“我要到哪里去?”这个问题来继续介绍后端乱序执行的部分。由于Back End相对前端来说较为复杂,有更多新的概念,所以可能会分成好几篇文章进行讲解。如果大家对前端还有什么问题的话,也欢迎提出来一起探讨学习。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值