【知乎】 如何看待苹果拿 M2 处理器同 4 年前的 8 代酷睿做对比?
如何看待苹果拿 M2 处理器同 4 年前的 8 代酷睿做对比?

JamesAslan
喜欢画画和摄影的硅工码农(滑稽)
牙膏?Maybe
2020年,M1携firestorm如烈火燎原般席卷了消费级市场,技惊四座,不免令人期待续作M2能够到达何种高度。然而现实十分骨感,从SPEC06和17来看,M2的提升幅度不尽如人意:


那么Apple真的没有做什么改动吗?科技以换名为本?那倒也不是,在不为人知的角落,Apple silicon还是在默默地进化着,以满足更多corner case负载的需求。
什么?你只关心SPEC06/17,Geekbench,Cinebench?那以上都当我没说过。
非分离式前端最后的守护者
在我浅薄的认知中,这个世界上的主流厂商中似乎只有三家没有使用decoupled frontend(分离式前端):Apple、Sifive、Tenstorrent。在这里我不想去揣测和分析原因,这不是本文的重点,而是想说明Apple M2做了什么。
什么是分离式前端?
在传统的非分离式前端中,取指PC被同时送往IFU(Instruction Fetch Unit,ICache等)和BPU(Branch Prediction Unit)。BPU负责进行分支预测,IFU负责获取指令内容,它们同时产生结果并被送往后续的流水级。这一设计咋看很美好,但是随着负载指令footprint的扩大,ICache的miss越发频繁,它的问题越发凸显:一旦ICache发生miss,流水线就会发生阻塞;由于IFU无法及时产生结果,BPU也要随之停止工作并等待IFU。我们回看BPU要解决的三大问题(我的一家之言,见笑):
Is there a branch?
Whether to take?
Where to jump?
在BTB的帮助下,其实这三个问题都不需要指令的具体内容。也就是说,在BTB的帮助下,IFU能否取得指令内容并不影响BPU的正常工作。那么ICache miss时停止BPU的工作就十分亏了。分离式前端的核心思想正是将分支预测与取指解耦合。

BPU将预测的取指PC送入FTQ(Fetch Target Queue)中,FTQ再将预测信息送往IFU进行取指。由于FTQ的队列属性,BPU从此能够独立地工作。这还引入额外的好处,即FDIP(Fetch directed instruction prefetch)的潜力。当BPU填入FTQ的速度大于IFU消耗FTQ的速度时,FTQ内就会产生堆积的信息,我们就可以利用这部分信息进行指令预取,这能够大幅提升大指令footprint的负载的性能表现。但是苹果似乎并没有跟随这股潮流,至少不完全。
什么是BTB?
在之前的内容里我们多次提到了BTB,它是前端的核心组件之一。

它主要负责解决三大问题中的两个:Is there a branch?Where to jump?。它存储了所有曾经taken过的分支指令的信息。其表项如上图所示,为译码后可得的分支类型、跳转目标地址等。通过pc查询时,如果匹配,我们就将该指令视为跳转指令,并对其进行分支预测。跳转目标地址主要由BTB提供,如果读取出的分支类型为间接跳转、call、return时则由其他预测器接管。由此我们可见,只有BTB的容量足够大,才能较好得识别指令流中的所有分支指令。但是大容量往往意味着高延迟,如何兼顾等效延迟和容量呢?没错,多级结构,现如今的处理器往往都配备了2-3级的BTB,但是新的问题又出现了。
前端空泡
多级BTB中的低层级BTB往往容量较小,高层级BTB容量较大。较小的BTB由于容量限制时常会发生miss,一旦没有在BTB中命中,指令就不会被视为分支指令,指令流就会顺序向下流淌。倘若这条指令后续被证实为taken的分支指令,整个前端就会发生回滚到该分支的跳转目标地址处重新开始取指。如下图所示:

一旦一条taken分支指令不能在最快的L1BTB(低层级BTB)中命中,就会产生前端空泡(bubble),这是前端浪费的取指机会(周期数)。不过,尽管在L2BTB中命中会产生前端空泡,其带来的代价还是远远少于分支预测错误,因此多级BTB是有效的。我们不妨将产生0个空泡的BTB成为L0BTB,依次类推:
Apple M1Sifive P870Tenstorrent AscalonAMD Zen4Intel RaptorcoveL0BTB102410241024?2048?1536128L1BTB----6144L2BTB---768012288
该表从中间位置分为了两个阵营,左侧的非分离式前端阵营,和右侧的分离式前端阵营。一个显而易见的趋势便是右侧阵营的BTB都十分巨大,这就不得不提非分离式前端的一个好处了。之前我们提过
其表项如上图所示,为译码后可得的分支类型、跳转目标地址等
BTB内存储的信息本质是经过译码处理后得到的,那么倘若非分离式前端取得指令后进行预译码,是不是也可以获得BTB信息呢?答案是可以的。非分离式前端就是通过预译码将ICache当作了它的末级BTB。我们观察Apple M1的BTB测试图像:

从中我们可以得到两组信息:
M1拥有一个1024项的L0BTB,它是一种Region BTB且十分质朴,每个Region居然留足了16个slot。
M1的ICache充当ICache时会导致2个前端空泡,即其取指延迟是3 cycle。事实上,基本所有采用非分离式前端的设计,都会将它们的ICache延迟控制在3 cycle,以确保预译码后充当BTB的ICache不会造成太多的前端空泡。
对于指令footprint较小的程序,这样的设计有几个优点:
避免对独立的大BTB的需求。
减少了前端的流水级数。
但是这样的机制显然不是万能的,缺点也十分明显:
1.BTB的性能和ICache容量强绑定,一旦ICache容量不足以容纳指令footprint,BTB也会随之躺平。我们不妨进行一个不严谨的简单计算,以末级BTB为例:即便M1拥有192KB的独一档的ICache,其作为BTB能够覆盖的指令footprint也为192KB。以平均每10条指令有一条跳转指令计算,7680项的独立L2BTB能够覆盖300KB+的指令footprint。
2.进行FDIP需要额外的冗余设计。
因此我们发现,当一款微架构要同时顾及服务器市场时,主流厂商似乎都倒向了分离式前端+大BTB的设计。
M2做了什么?
M1在1024项的L0BTB和192KB的ICache(准L2BTB)间空缺了L1BTB,而M2做的正是填补了这一空白。


可见,M2增加了一组3072项的L1BTB,那么反映到实际应用上又会如何呢?我们考察指令footprint极大的Verilator:

可见M2取得了远超频率提升的进步,即便是面对Raptorcove也不落下风(当然超过4-thread就是另一个故事里)。这里不禁畅想,如果Avalanche这样的一颗核心被用于服务器,该是怎样的一幅画面呢?确实也有不少人想要这么做,可惜要么被收购,重回了手机的老本行;要么陷于官司的泥潭,还没什么公开信息,啧啧。

就这样吧,期待即将到来的一大波新架构能够带来更多的有趣的设计。
发布于 2023-10-21 14:05