当前位置: 首页 >  关注  >  正文

【转】Tremont:双份解码,双倍快乐?
2023-01-29 22:45:40 哔哩哔哩

Tremont:双份解码,双倍快乐?

XZiar

中央处理器 (CPU)话题下的优秀答主


(资料图)

149 人赞同了该文章

目录

收起

常见的解释

双份解码,双倍快乐?

DSB:明明是我先

你这瓜保免费不?

展望

拖延症拖到Gracemont都发布了,不能再拖了!

阅读本文前,建议先看上一篇文章:

XZiar:分支预测,uOP,乱序执行266 赞同 · 12 评论文章

Tremont是Intel在19年下半年公布的新一代atom微架构,上一代则是Goldmont/Goldmont Plus。

实际产品在架构图披露后很久才发布[1]:首先是专供基站设备的Snow Ridge,2020Q1发布,不对外发售。然后是2020Q2的初代大小核Lakefield,用在少数几款高端平板上。消费级产品线Jasper Lake、嵌入式产品线Elkhart Lake直到2021Q1才发布。

很遗憾,以上设备我一款都没有。但这并不妨碍我从纸面上去做一些分析。

这次的关注点在于Tremont微架构中的最大亮点:双解码簇。这一设计在它的后继者Gracemont中也得到了保留。

https://en.wikichip.org/wiki/intel/microarchitectures/tremonten.wikichip.org/wiki/intel/microarchitectures/tremont

常见的解释

两个解码簇,很容易让人联想到一正一反,也就是对应着分支预测里的跳转/不跳转。

于是,很常见的一种解释就出现了:

两个解码簇分别用于解码一个分支的两个目标,等到结果计算完成后再把不正确的分支得到的内容丢弃,从而“降低分支预测的开销”。

可是这个说法正确吗?

如果均等地照顾分支的两个目标,那分支预测(指Taken/Non-Taken)又还有什么存在的意义呢?(不过就算能覆盖两个目标,对于“跳转”的情况,目标地址依旧需要做预测

显然从图上可以看到独立于解码的预测单元,而且这也是Tremont宣传的提升之一——“大核酷睿级别的分支预测”。

其次,现代处理器的分支预测准确率已经很高了,哪怕是很原始的、大学课程都可能教到的Two-Level Adaptive Branch Predictor,根据论文结果准确率也通常在9x%。

CPU 的分支預測器是怎樣工作的?443 赞同 · 18 评论回答

1995年发布的Pentium Pro就已经采用了这种设计,而现在更先进的TAGE及其派生预测器的准确率就更高了。根据Intel的一篇论文[2],TAGE-SC-L 8KB的预测器(CBP2016的冠军)在specint2017中的平均准确率能有95.2%。

如果你有95%的把握能预测对一个分支,那为什么还要持续消耗资源去为那5%做解码甚至执行呢?尤其是在atom这种看重功耗与面积的设计上。

最重要的是,分支是可以无限套娃的

你为两个分支都做了解码,那接下来每个分支可能还会再遇到两个分支,工作量翻倍。而实际上代码只会沿着一条特定的路线走下去,越往后,全都解码的浪费就越多,维护这一整套分支的开销也越大

那么,这个双解码簇的设计究竟是为了什么?

双份解码,双倍快乐?

其实对于这块的分析,网上资料是有的(虽然不是官方的,但wikichip能拿到的信息还是比较靠谱):

Intel Unveils the Tremont Microarchitecture: Going After ST Performancefuse.wikichip.org/news/2851/intel-unveils-the-tremont-microarchitecture-going-after-st-performance/2/

有兴趣的可以直接点进去看原文,我在这用我的语言更详细地表述一下流程:

如上一篇文章所说,“分支预测通常发生得很早,因为只需要PC,所以甚至可以放在取指解码之前”。事实上第一阶段的分支预测正是“最早发生”的:仅根据当前PC去预测下一周期的PC

紧接着,根据当前PC,CPU会去ICache和DSB里做查找,前者数据拿到后要做解码,后者直接输送uop。如果ICache miss还要通过ITLB去往下一级缓存索要指令。

当uop生成后,第二阶段地分支预测还会对分支做检查——比如BPU觉得下一周期不会跳转但uop里发现了无条件跳转指令,或者BPU觉得下一周期会跳转但uop里一点跳转相关的指令都没有。当然更复杂的情况还要等后端处理完前面的指令,给出反馈才能知道有没有做错。

而双解码簇的工作机制是:当第一阶段BPU预测会发生跳转时,把新的PC移交给另一个簇(或者是比较空闲的簇)去做处理

可能很多人会觉得奇怪:BPU预测的是下一周期PC,那也得等到下一周期才能让第二个簇开始工作啊。明明可以继续把工作交给第一个簇,整这么多花样干嘛?

这其中的关键就是一个簇里只有三个解码单元,而继续往一个簇里增加解码单元是有代价的

刚好这两天Intel的Architecture Day,Golden Cove就把解码提升到了6宽,来看看anandtech对此说了什么[3]:

For Golden Cove, Intel has decided to push forward with these changes, and a compromise that had to be made is that the design now adds an additional stageto the mispredict penalty of the microarchitecture, so the best-case would go up from 16 cycles to 17 cycles.

流水线级数增加了。

这在很大程度上还要怪x86指令集的变长特性,这里可以插播一下x86的指令编码格式[4]:

[Legacy Prefixes]+[Prefix],[REX],opcode/VEX opcode+[ModR/M]+[SIB]+[Displacement]+[Immediate]

可见x86的变长体现在方方面面,上面带括号的都是“可选”或者“依赖于其他部分”的,比如SIB、Disp、imm是否存在是通过REX和ModR/M共同决定的,但ModR/M又依赖于opcode……此外前缀更是复杂,可以套娃,可能被忽略又可能导致“非法指令”……

诸多反汇编工具是什么原理?69 赞同 · 9 评论回答

指令前缀0x66到底是干什么用的?46 关注 · 4 回答问题

x86解码的第一步就是ILDInstruction Length Decoder去确认指令长度与边界,看了前面的描述就知道这一部分多么让人头大了。想要得到一个长度信息,并不是判断一个字节就够的,至少需要把前三个部分都识别出来,还要对后两部分的内容做一定判断——这已经相当于解码了半截指令了。而处理第二条指令又依赖于第一条指令的长度……

我在一个回答下提到过:

x86/x86-64架构是否有可能通过某些方式提高指令解码效率?17 赞同 · 5 评论回答

现有的解码器应该不是单纯线性扫描的,可能是每个字节都去当作指令开头去尝试预解码,然后根据结果(比如预解码能知道从此处开始的指令有多长)去一步步剔除不可能的选项。线性扫的话先后依赖太高了,感觉CPU这种不缺面积缺时间的东西还是会并行做。

如果去线性扫描,那延迟(耗时)会随解码宽度线性增加;如果并行扫描意味着复杂度(以及能耗,因为做了更多无用功)会随着解码宽度线性甚至非线性增加,总之都有代价

而如果是双解码簇的话,两个簇工作时相互没有依赖,每个簇只要保持三解码的规模,但理想情况下却和六解码有相似的输出带宽

(从某种角度看,这也算得上是之前大家遐想的“逆超线程”了,即一个线程的指令流被两个模块处理……)

(P.S,我的私心是,双解码配合上SMT或许也会很搭?原本解码单元要时分复用,现在刚好能各占一个簇甚至负载均衡。那样也算是推土机双模块共享解码的逆向版设计了……)

这张图是silvermont的流水线图(虽然是比较旧的架构),可以看到取指+解码(IF+ID)占据了6个周期,算是半壁江山了。而看起来更关键的Execute其实只一个周期(实际上很多指令是要多个周期才能完成并进入Retire阶段,但流水线的设计使得大量EU每周期都能输入一个操作)。

看起来很夸张,这难道就是x86被ARM打趴下的原因吗?

其实不是的,ARM现在的流水线也是10+stage,IF+ID也是相似的夸张,比如A76(拿Neoverse N1的图[5]做例子),Fetch+Decode也有 4+2=6周期:

上一篇文章讲到过了:

换言之,乱序执行提高的是期望性能,并不能提升最差性能,所以更依赖于分支预测的准确性分支预测失误时所表现出更大的性能损失,主要来自于更多被投机执行的操作。从这个角度说,乱序深度越深,同样的分支预测失误率也会导致更明显的性能下降。

也就是说,降低流水线级数能减轻分支预测失误时带来的惩罚。那么有没有什么办法能降低流水线里的半壁江山,Fetch&Decode部分的级数呢

你好,有的:uop cache,也就是Intel的DSBDecoded Stream Buffer)。

DSB:明明是我先

DSB是在SandyBridge引入的[6],前身是NetBurst中的Trace Cache[7]。

如果分支预测为跳转,需要从BTB拿到目标地址,然后修改PC去重新取指。如果预测错误,还要回退到PC到错误的节点,抛弃一堆已解码或正在解码的指令。

Trace Cache的思路是,既然分支预测这么重要,不如更进一步,不止是做预测,而是把预测后的指令集合起来存入Cache。并且存入Cache的指令是经过解码后的uop,这样也可以省去重复解码的开销。

但是跟踪指令流并动态更新Cache并不是很容易的事情,尤其是当指令流跨越多个分支时,如前面所说,可能性与复杂度是指数上升的。这部分对能耗的需求或许是初代Core抛弃Trace Cache的原因。

到了SNB,简化版的Trace Cache被改名成了DSB,从更常见的说法uop Cache也可以看出,这次的Cache只负责省略解码阶段,不负责分支预测了。

我在其他回答下也说过DSB的优势:

如何判断CPU发射宽度?77 赞同 · 14 评论回答

传统的解码要耗费不算少精力,还有各种限制(比如16B的fetch,x86变长的判断,complex/simple组合),而解码完的指令会存入DSB,日后再使用的话不但省去解码开销(MITE会直接休眠省电),还能减少流水线深度。

比如SandyBridge的pipeline一般被标为14~19,这个差异主要就来自前端使用的是MITE还是DSB。

除去更短的流水线级数,DSB另一个优势是:输送uop的带宽也更高。比如skylake的DSB能做到6uop/cycle,比MITE的5uop要高。同时,6uop可以来自最多32B的原始指令,这相当于解除了16B Fetch的限制(Intel自己提到,SIMD指令编码通常较长,使得16B里放不进4条指令,从而导致FE Bound[8])。

而且Intel的Complex+Simple对指令排布有一定的偏好与要求,比如遇上两条解码到2uop的指令,就得分两个周期都由complex去解码,而DSB自然没这个问题。

AMD在zen开始也加入了uop cache,和Intel这边功能类似。不过细心的朋友可能会发现,AMD的decoder是对称的,每个都能解码出1~2个uop(或者叫MOP,Macro Operation)[9],所以4宽解码能产生4-8个MOP,看起来不比uop cache的8MOP要差啊?

其实这是因为AMD对MOP/uop的划分不一样。前文说过AMD和atom相似,尽量把每条x86指令都解码到一条MOP/uop,但并不意味着两者对op的定义是一致的。

AMD这边把直接解码的指令叫做fast path instruction或者direct path instruction,而其余指令则要通过MSROM解码,这和intel差不多。但在fastpath内还划分了single和double——这不是SP和DP单双精度浮点的意思,而是指一条指令对应了几个MOP[10]。

K8时代,128bit的SSE指令集开始流行,但K8的ALU还是64bit,所以不少指令就被解码成操作两个64bit lane的double MOP。K10更新到了128bit ALU,这类指令就几乎变成single了。而到了农用器械时代,256bit的AVX又撞上了128bit的ALU,double MOP重出江湖,一直保留到zen。然后zen2更新256bit ALU,又一次抹去了它们的需要。

所以ALU宽度足够时,解码是发挥不出8MOP的带宽的,uop cache的优势依旧显著。而且由于对uop的定义更广,8uop的带宽更高,这方面AMD其实应该说是优于Intel的。不过AMD的ARR部分没有8uop那么宽,所以其实并不能完全体现出来而已。

除了x86,ARM也在A77中跟风加入了MOP Cache[11]。

实际上ARM也做了类似的MOP->uop的转换[12],哪怕ARM已经是RISC了。

比如对于访存指令,RISC基本是load-store架构,访存和计算指令分得很开,不会出现R-M-W这样的情况。但访存地址是允许带有一些计算嵌入在指令里的,比如str x6, [sp, #0x60],就隐含了add addr, sp, 0x60这样的计算。ARM和x86殊途同归,把store指令拆成了STA和STD,前者做地址计算,后者用于输送数据

不过这部分是在ARR环节做的,所以ARM加入的是MOP Cache。但这里的MOP依旧可能做macro-fusion优化[13],也包括比较与条件跳转(B.cond)的融合[12]。

借助MOP cache,A77可以向后端提供更高的输出带宽,也可以减少一级流水线。

你这瓜保免费不?

既然uop cache既然又省电又省周期,x86和ARM都抢着用,那为什么在atom小核却没了踪影?

前面提到uop cache的第一大优势,就是能提供更高的带宽——但并不永远都能做到这一点。

比如haswell的DSB,分配以6uop为一组每block最多三组、每条只有32bit存储空间,要求对齐到指令的32B边界,只支持跳转到组首的uop等……大部分限制都是从SandyBridge一直延续至今,Skylake的限制甚至似乎增加了(64B对齐?)。所以实际使用起来并不总是美好,相比之下AMD的限制似乎更少(Intel总是有各种奇形怪状的限制,我也是理解不了)。

而一旦uop cache不能使用,那“节省流水线级数”的优点自然也就排不上号了。更麻烦的是,从uop cache切换回MITE,难道是毫无开销的吗?

假设IF+ID=6stage,用DSB可能只要2stage。但这只影响了分支预测错误时的开销,当分支预测没问题,流水线没有空泡的情况下,级数并不产生性能影响(跟考场发试卷一样,每次都往后传一张试卷,无论有多少排,最后都是末排每次收获一张试卷)。

但前面提到,用DSB时MITE会关闭,即IF+ID那几级里可能已经空了,这时候再切换回MITE,就要等上6个周期才能喂出第一份uop——填充流水线也有开销的

而按照三家的宣传说法,uop cache的hit rate在80%左右(热点区域通常更高),但这比率可赶不上分支预测正确率啊……

反观双解码簇的设计,除了避免了切换模式的开销,也有自己的独特优势:易于扩展(scalable)

想再提升解码宽度,再加一个簇就好了,设计复杂度比堆正统的9宽解码小太多。只要分支预测靠谱,多个簇就能被利用起来,甚至于跳转频繁了反而有利

与此相对的,如果代码的跳转少,反而可能导致一个簇闲置,于是手册上还建议此时插入无条件跳转以改善性能[14],简直丧心病狂……

uop cache还有另一个好处:缩短流水线周期。这个双解码簇倒是难以做到。不过atom也有优化措施——predecode cache。

atom这边不但减少了单个簇的解码宽度,还缓存了指令长度信息到PDCache:只有在指令第一次被解码时才需要靠ILD检测长度,和uop cache类似,这部分数据会被缓存以加速下一次的解码。这也就是Gracemont宣传时提到的OD-ILD[15]:

当然根据agner的文档[16],atom系列一直是有在cache里预先标记指令边界的。AMD这边从K8开始也有PDCache,记录了指令长度、opcode、MOP类型、是否为跳转等信息,而且似乎是从L2载入L1时处理的。不过从zen开始AMD也抛弃了PDCache转向了uop cache(都怪Intel把AMD带偏了,狗头)。

前面提到由于变长编码,ILD的任务很严峻,所以小核为了省点,ILD规模也比大核要小一些的。

此外“簇闲置”的问题在下一代Gracemont上也得到了部分解决:GRT会将无跳转的basic block做主动拆分,在两个簇间做负载均衡

It also includes hardware-driven load balancing, which takes long chains of sequential instructions and automatically inserts toggle points to ensure parallelism.[17]

展望

总的来说,Tremont的cluster-based decoder是颇有创意的设计,其背后的目的还是为了在性能与开销间做一个平衡

atom的首要关注还是面积与功耗,于是在加宽的前端的路线上产生了犹豫。双解码簇的设计虽然不算完美,但还是比较好的完成了预期需求,甚至给未来堆料留下了无限遐想的空间

对比Tremont和前代Goldmont Plus,区别不止是前端双解码,后端也加宽了。对比最近几代大小核,小核的后端规模甚至不落下风(虽然SIMD宽度显然差不少)。当然大小核在访存部分也有不少差异,但这个就不在今天讨论范围内了。

GMTGMT+TNTGRTSNCGLCFetch16B16B16B * 216B * 216B32BDecode333 * 23 * 24(5) / 6 (DSB)6 / 8 (DSB)Allocate Rename / Retire3445 / 85 / 5+6 / 6+EU Port7810171012

不是羡慕苹果的M1,8解码16端口的超宽架构吗?或许atom正在朝这个方向努力着。

参考

^Tremont Release Day https://en.wikipedia.org/wiki/Tremont_(microarchitecture)#List_of_Tremont_processors

^Branch Prediction Is Not A Solved Problem https://arxiv.org/pdf/1906.08170.pdf

^anandtech-GLC https://www.anandtech.com/show/16881/a-deep-dive-into-intels-alder-lake-microarchitectures/3

^X86 Instruction Encoding https://wiki.osdev.org/X86-64_Instruction_Encoding

^Neoverse N1 https://fuse.wikichip.org/news/2075/arm-launches-new-neoverse-n1-and-e1-server-cores/2/

^SNB DSB https://en.wikichip.org/wiki/intel/microarchitectures/sandy_bridge_(client)#New_.C2.B5OP_cache_.26_x86_tax

^Trace Cache https://en.wikipedia.org/wiki/Trace_cache

^Intel® 64 and IA-32 Architectures Optimization Reference Manual E.2.2.2

^zen 8MOP https://en.wikichip.org/wiki/amd/microarchitectures/zen#Individual_Core

^AMD MOPs https://en.wikichip.org/wiki/amd/microarchitectures/zen#Decode

^anandtech A77 https://www.anandtech.com/show/14384/arm-announces-cortexa77-cpu-ip/2

^abArm® Cortex®-A78 Core Software Optimization Guide

^macro fusion https://en.wikichip.org/wiki/macro-operation_fusion

^Intel® 64 and IA-32 Architectures Optimization Reference Manual 4.1.2

^Gracemont wikichip https://fuse.wikichip.org/news/6102/intels-gracemont-small-core-eclipses-last-gen-big-core-performance

^agner microarchitecture https://www.agner.org/optimize/microarchitecture.pdf

^Intel Architecture Day 2021 https://www.intel.com/content/www/us/en/newsroom/resources/press-kit-architecture-day-2021.html

编辑于 2022-09-11 01:39

中央处理器 (CPU)

英特尔 (Intel)

x86

赞同 14912 条评论

12 条评论

David Huang

插入无条件跳转这个倒也可以理解,相当于帮CPU预先算好了指令边界的位置……

2021-08-22

4

XZiar

作者

但是要占用BTB资源和浪费一点点L1空间,而且这种做法对其他架构没帮助。还是PDCache+运行时负载均衡更合理。就是不知道会不会和大核的uop cache一样,跳转到不对齐地址就歇菜

2021-08-23

一个大大大水货

感觉你们搞体系结构研究的阅读量好大...

01-05

时夏初秋

一组3个decoder是因为16B的fetch在面对avx之类的指令时不足以容纳4条还是因为分支指令真的很多呢?分支指令一般也就20%左右吧?或许做到4decoder一组才合适,但也许真的是因为4decoder复杂度会高很多吧。

2022-04-12

XZiar

作者

原因很多,大头肯定是复杂度吧。文中那句主要是想说fetch宽度不够的话解码堆上去也还是会瓶颈的

2022-04-12

1

丢猫

就是说可能某个周期内两组解码器可以同时发射uops是么? 还是说发射始终是交替的但是因为惩罚降低了,带宽还是和6解码接近?

2022-01-28

ssagg

“两个解码簇,很容易让人联想到一正一反,也就是对应着分支预测里的跳转/不跳转”——直接用于任何跳转不行吗?非得只用于分支的跳转?

2021-10-19

ssagg

XZiar

那这种直观是错的,会有遇到分支的跳转,但没必要只处理分支的跳转

2021-10-19

XZiar

作者

解码之前也不知道是条件跳转还是无条件跳转啊,分支预测都要做跳或不跳的判断的。我这句话的意思是,很多人直观认为两个簇分别处理跳转和不跳转两种结果

2021-10-19

游客

无条件跳转等于手动/默认帮ICC扩大竞争优势?这么搞谁还愿意跟ICC比优化力度啊

2021-08-23

幸福渠水到俺村

开源编译器的后端都是各个厂商自己做的。干嘛要跟icc比优化力度,这不是左右互博?

2021-09-02

1

李伯通

感觉小核近期不会搞SMT吧

2021-08-23

热门推荐