uOps 微架构 简单指令 复杂指令 CISC RISC 超流水线 超标量 超线程
【uOps哲学三问】我来自哪里?——带你梳理x86微架构 - 知乎 https://zhuanlan.zhihu.com/p/511035787
【uOps哲学三问】我来自哪里?——带你梳理x86微架构
前言
在上篇文章解答完“我是谁?”这个问题后,接下来试着回答“我来自哪里?”这个问题,这一切都要从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 from | MITE | DSB | MS | LSD |
步骤 | Addr -> Bytes -> Inst -> uops | Sent uop from uop Cache | Send uops flow from MS ROM | Re-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相对前端来说较为复杂,有更多新的概念,所以可能会分成好几篇文章进行讲解。如果大家对前端还有什么问题的话,也欢迎提出来一起探讨学习。
参考
- ^2021 Intel Architecture Day Presentation https://www.intel.com/content/www/us/en/newsroom/resources/press-kit-architecture-day-2021.html
1、
Micro-operation,micro可以希腊字母μ代替,然后operation可以缩写为op,连一起即μop,由于μ不太好打出来,所以大多数人用型像的u代替了μ,故我有了uop这个名字。然后在我的名字后面加上s,用uops来统称所有的uop,即Micro-operations。
uop can be a way to spell μop or Micro-operation in computing, using a "u" to represent the Greek letter mu.
【uOps哲学三问】我是谁?——带你梳理x86微架构
前言
本系列将会尝试替uOps回答经典的哲学三问:我是谁?我从哪里来?我要到哪里去?借讲解uOps的今生来世以梳理x86微架构,目的是帮助大家能看懂网上的CPU微架构图,如下图的Skylake微架构[1]。读完本系列应该能对图中每个模块具体的功能以及他们如何和其他模块互相配合协同工作有个大概的了解,至少知道uOps在微架构中是如何流动的,即大概的执行过程,但不会深入到每个模块的原理和细节,不然光是Branch Predictor这个小模块就需要长篇大论一番,而且很多模块的技术细节由于机密原因我们也无法获取到相关资料。
Skylake Microarchitecture
为什么要写这一系列文章呢?其实也有点受公司内部一个名为Life of a µOp的PPT启发,该PPT通过追寻某几条uops的一生,从而贯穿整个Haswell的微架构,以让相关软件开发人员对微架构有个大概的认识。由于该PPT基于的架构比较老,而且有些模块没有涉及,所以就萌生写一篇从uops的视角出发,以大致介绍整个微架构的文章。
写前也先在互联网搜索了一番,发现现在无论是国内还是国外,对微架构研究的硬核测评媒体越来越少了。每次新CPU发布,测评报告更多的篇幅放在跑分上面,要不然就是超个频再跑个分,关于微架构的新增和改进方面多数是一笔带过。对于比较硬核的发烧友来说其实大篇幅的测评看下来只了解到了几个苍白无力的分数,至于这些分数背后提升的根本原因——微架构的改进所带来的影响,却少有人细细分析。
或许是因为互联网信息大爆炸让大家越发浮躁?大家根本无暇坐下来深入了解CPU上科技的进步,只是想知道新一代跑分多少,是不是吊打对方还是说继续挤牙膏?不过对于我这种喜欢刨根问底的人来说,仅仅得知一个分数是不满足的,更希望得知这次制程工艺所带来的性能提升有多少,微架构上的改进带来的提升又有多少,并且这些改变分别有什么优势和负面影响。既然这方面的资料较为匮乏,那么何不也出一份力,让更多人看到相关的内容,或许也能引起相关的发问,从而使得相关的内容日渐丰富。
CPU微架构资料
在查找相关微架构资料的过程中,也发现了关于新产品的微架构资料寥寥无几的这个问题,更多只能从官方的发布会和官方文档中获取零星的相关信息,而民间自发整理分析微架构的资料渐渐减少。在Real World Technologies网站中,最后一篇关于Intel微架构的介绍是Haswell,然后在WikiChip能找到有具体微架构图的最后一个架构是Skylake,都是很老的产品了。关于新产品的微架构分析,更多只能从Intel的官方发布会和官方文档中去了解了,大家如果对最新的Intel CPU架构感兴趣的话,可以去看看Intel Architecture Day 2021。
如果想要详细了解Intel的CPU架构的话,这里还推荐Intel的官方文档,可以在Intel官方网站中找到Intel® 64 and IA-32 Architectures Optimization Reference Manual,重点可以看看第二章关于Intel各代CPU微架构的介绍,虽然有些系列和某些模块不是很详尽甚至没有介绍,但是如果能完全看下来,差不多就对Intel微架构各个模块的功能以及微架构的发展和演变有个大概的认识和了解。
我是谁?
废话不多说,先来回答第一个哲学问题:“我是谁?”为了让大家彻彻底底了解uOps到底是谁,本文会先介绍一些相关的背景知识,已经熟悉的朋友可以略过不看,如果大家还有不清楚的地方,在网上找下相关的介绍资料研读一番,应该就可以弄懂了。
uOps
大多数人把我称作uop,为什么我叫这个名字呢?其实我的全称叫做Micro-operation,micro可以希腊字母μ代替,然后operation可以缩写为op,连一起即μop,由于μ不太好打出来,所以大多数人用型像的u代替了μ,故我有了uop这个名字。然后在我的名字后面加上s,用uops来统称所有的uop,即Micro-operations。
uop can be a way to spell μop or Micro-operation in computing, using a "u" to represent the Greek letter mu.
网上还有关于我的Wikipedia,简要一句话概括,就是像x86这种CISC处理器,为了能在内部更加高效灵活地处理接收到的x86指令,会将这些长短不一复杂不一的指令转换成我——uop,以便更容易被调度和优化,提升CPU性能。
CISC vs. RISC
关于CISC (Complex instruction set computer)和RISC (Reduced instruction set computer)网上对比介绍的资料很多,这里就不赘述了。简单一句话概括,他们的区别就是一个是复杂指令,简单了编译器和汇编用户,可是却苦了处理这些指令的CPU硬件;另一个是简单指令,苦了编译器,但是硬件处理起来就比较轻松了。而且由于指令的执行时间差不多,实现流水线难度小很多,天花板也高了很多。但是目前的x86 CPU内部使用了uop,来利用RISC架构的一部分优势,以追求更好的性能,所以可以说目前的x86架构,虽然外部看来是CISC,内部却是一颗RISC的心,还多亏有了我——uop[狗头]
为什么说使用了uop就能提升整体的执行效率呢?x86的指令可以理解成是粗颗粒度的,而对于uop,可以理解成是细颗粒的微小指令。对于CPU处理工厂而言,处理加工各种大小各异,种类繁多的x86,意味着要涉及多种处理装置以应付如此多种类的指令,并且由于存在异常复杂的指令,所以某些处理装置还要设计得十分庞大,这样下来,意味着CPU工厂会变得很大,而且这么多装置只有少部分在开工,十分浪费资源。如果将这些粗颗粒的先分解成各个细颗粒的任务就不一样了,由于进行了拆解,原先种类繁多的各种指令最终都能被拆解成简单的几种任务,然后这些简单的任务只需要设计几台小型的装置就可以进行处理,对于比较常见的任务大不了就多放几台机器好了。最终的结果就是CPU工厂变得更紧凑,机器的使用率变得更高,并且得益于被拆分成各种简单的任务,这些任务还可以并行进行处理,更短的时间内完成相应的复杂操作。
Pipeline vs. Super-pipelined vs. Superscalar vs. Hyper-Threading(SMT)
然后再介绍下流水线,超流水线,超标量和超线程的概念。目前Intel和AMD的CPU中都使用了这些技术,并且这些技术对性能提升的贡献也不小,所以还是有必要熟悉下他们。首先流水线这个概念想必大家都不陌生,正是流水线将我们一个个工人变成了一颗颗螺丝钉,变得易于替代。CPU中的流水线使得各个模块没有喘息的机会,执行完各自相应的职责后,立马可以拿到后续需要处理的任务,不需要等待刚刚处理的任务被真正执行完。带来的好处自然是使得各个模块能一直处于工作状态,充分压榨硬件资源,坏处自然也是有的,一直工作就得消耗电力,同时也得对外散发热量,当然这些负面影响在追求压榨光最后一滴性能的人们面前算不得什么。
Pipeline既然有流水线,那就有超流水线,所谓超级,就是变得更能卷,比如原先A组被要求2分钟了完成A任务,为了提升效率,A组被拆分了A1和A2两组,A任务也被相应拆分成A1和A2两项任务,执行时间变成原来的一半。那么在前一分钟,A1被执行完后,A1组就可以继续执行新任务的A1部分,不需要等到A被完全执行完后才能拿到新的任务,同样的时间内,能够完成的任务量乘以了划分组数的倍数。简单来说,就是尽量让各模块不要闲下来,比如整个模块在工作,但是如果可以通过拆分的方式,找出空闲的那一拨人,然后不断给他们提供相应的任务,直到不能再进一步划分为止,这就是所谓的超流水线。通过进一步细分流水线中的每个周期,以缩短每个周期的时间,提升频率,在同样的时间内完成更多的任务。
Pipeline vs. Super-pipelined vs. Superscalar除了超流水线,还有一个带超的相关技术就是超标量,所谓的超标量,是和标量处理器相对比的,标量处理器一次只能处理一个数据,即SISD,但超标量一次可以并行处理多个数据。比如刚刚划分成A1,A2两个组后,工厂组还是不满意执行效率,那么就多添置N倍A组相应的资源,那么同样的时间可以处理的任务将继续乘以N,对应就是CPU内部有多个执行单元。对于某个线程的任务,想要应用超标量技术的前提是能够乱序执行,试想如果必须顺序执行所有的任务,那么必然没有两个及以上可以并行处理的任务,即便前后两个任务是同一个任务,也必须等前一个任务执行完才能执行下一个,只要同时执行就没有了顺序之分,即为乱序。
既然提到了超标量,这里就把另一个比较容易混淆的超线程概念一起介绍了,超线程Intel这项技术称作Hyper-Threading,AMD称作Simultaneous Multithreading,其实都是同一个意思。超即多的意思,意思是CPU上可以同时执行多个线程的任务,目前较为常见的双线程。那么为何要引入两个线程的任务呢?必然是因为有资源被闲置,CPU的利用率不够高,那么我就多加入额外一点点资源,以让CPU可以同时处理两个线程的任务,尽量不要让硬件资源被闲置,说到底还是为了压榨硬件资源。和超标量不同的是,超标量是通过加入额外的硬件的资源以使得同样的时间内可以处理多个一样的任务,而超线程是额外多引入了一个线程,以免这些额外添加的资源没被利用上而闲置。
如下图所示,RAM放着各式各样等待被处理的线程任务,由于CPU使用了超线程技术,可以同时处理黄色和红色这两个线程的任务,两个线程共享某些硬件资源,谁有需求就用,不分你我,目的就是不要让这些资源闲置浪费。然后还因为使用了超标量的技术,一个时间周期内,不仅仅可以在前端取指译码多个指令,还可以在后端的执行单元同时执行多个多个任务。当然乱序执行也是必不可少的,不然就不可能同时在后端执行单元并行执行这么多项同一个线程的任务了,不过最后还是得乖乖按顺序排队离开CPU。即怎么进来的就得怎么出去,哪怕后来的比先到的提前执行完,还是得排在先到的后面。
Hyper-Threading(SMT)
Architecture Overview
在开始深入介绍微架构前,还是需要对整个计算机架构和CPU微架构有个大概的认识的,关于现代CPU架构的介绍,这里推荐Intel的Architecture All Access中的前两个视频,即Modern CPU Architecture部分,看完大概会对目前的电脑架构和CPU微架构有个大概的认识,但是还不足以完全看懂本文贴出的第一张Skylake的架构图和uops在其中执行的流程,不过这也是本系列文章所要深入介绍的。
这里推荐Intel的科普视频仅仅是因为做得比较简单易懂,用词也比较准确,并没有任何好处费,Intel打钱,哈哈。如果介意广告植入的话可以跳过不看,后续遇到什么不懂的网站一查应该也可以找到很多资料。不过如果完全能看懂第一张Skylake的架构图,知道上面每个模块的功能的话,就没有必要看本系列的文章了,也更没有必要浪费时间看这两个小白科普视频了:)
下一篇文章将会试着替uop解答“我来自哪里?”这个问题,帮助大家梳理微架构的Front End部分,即取值和译码的流程。回答完“我来自哪里?”后,后续文章会试着回答“我要到哪里去?”这个问题,以梳理Back End部分的内容,即执行,访存和回写阶段的过程,由于后端涉及到了乱序执行,多种执行单元和复杂的执行机制,所以可能会分成多篇文章来解答最后一个问题。
由于这些文章是本人利用工作之余的空余时间,出于个人兴趣搜集整理相关资料以完成编写的,并不是本职工作,所以文章质量和更新速度上面可能差强人意,望见谅。不过有什么不完善或者错误的地方欢迎评论指出,或者有相关的问题也欢迎留言,互相交流学习。
参考
- ^Skylake Microarchitecture https://en.wikichip.org/w/images/7/7e/skylake_block_diagram.svg