曾经吓到Intel,AMD的K8架构回顾
20年前,AMD的K8架构与英特尔的最佳架构展开了正面交锋。但不同于今天的“Zen vs somesomelake”的情况,K8 vs Netburst的情况特别有趣,因为两个核心采用了截然不同的方法来实现高性能。
在本文中,我们将使用来自AMD Athlon FX-62的数据来研究K8的架构。该芯片在AMD自己的90nm处理器上实现了两个K8核心,运行速度为2.8 GHz。它在2006年与英特尔基于Netburst的芯片正面交锋,直到同年晚些时候Merom的推出。我们还将使用来自AMD Athlon 64 6000+的一些数据,它在AMD的65nm制程中使用了相同的K8核。
概述
K8的架构与K7 Athlon的架构基本相同,但增加了64位支持,并进行了一些调整。核心仍然是围绕执行x86指令构建的,这些指令将数学运算和内存访问结合在一起,而无需将它们解码成多个微操作。整数执行端被分为三个很大程度上独立的管道,通过转发网络连接。
英特尔的Netburst也是3宽的,但在其他方面都很庞大,而且具有先进的微架构功能。
前端:分支预测
K8的流水线相对较短,能够取消从错误路径获取的指令,因此它比英特尔的Netburst更少受到错误预测分支的影响。虽然分支预测对于实现高性能仍然很重要,但AMD不需要像英特尔那样优先考虑分支预测器。
从AMD发布的结构图和优化手册来看,K7和K8都使用了具有全局历史的两级预测器,其中以前的分支结果和分支地址都用于索引到一个2位计数器的历史表中。但是,K8通过将历史表的大小从4096个条目增加到16384个条目来降低混淆的可能性。AMD的优化手册说,历史表的索引使用4位分支地址,以及最后8个分支的结果(取或不取)。这只产生了12位,这很有趣,因为你需要14位才能从16384个条目中选择一个。Hans de Vries认为“4位分支地址”对应于16字节的指令缓存行,这意味着这两个未解释的位将用于在该行中选择分支。
总的来说,AMD的Hot Chips幻灯片声称其分支预测精度比K7提高了5-10%。我们并没有得到特别清晰的结果,但K8的模式识别能力明显落后于Netburst,后者使用16位全球历史数据索引到其预测器中。
为了加速分支处理,K8有一个具有2048个条目的单层BTB。BTB的尺寸对于Athlon时代的CPU来说还算不错,但也没什么值得特别介绍的。它不能处理连续的分支。如果采用分支,则前端必须在等待目标来自 BTB 的同时停止一个周期。这使得循环展开对于AMD的架构非常重要。现代架构通常能够处理至少少量的分支而不会造成损失,这意味着像循环展开这样的编译器优化并不那么重要。
同时,Netburst具有令人难以置信的零气泡分支跟踪能力,在最好的情况下与Zen 3相当。它还具有多级BTB,可以跟踪两倍于K8的分支目标,同时时钟品频率更高。Netburst的BTB设计有点像现代设计,除了当一切不按计划进行时的大规模惩罚。
AMD通过附加在指令缓存线路上的分支选择器,将BTB与L1指令缓存耦合在一起。一方面,该方案简化了K8的前端,因为指令缓存获取还提供了分支预测信息。但是,这意味着在指令缓存未命中时没有可用的分支预测信息。由于分支大约每10条指令发生一次,因此如果存在指令缓存未命中,则很难准确地提前预取。Netburst可能使用解耦的BTB设计,只要不超过BTB容量,分支预测器就可以充当非常准确的预取器。
总的来说,对于 2000 年代中期的设计,K8 有一个不错的分支预测器。但Netburst的预测器属于另一类。英特尔有一个更现代和复杂的分支预测器设置,并配备了更多的存储空间来引导。
前端:指令取出
K8 使用存储 x86 指令字节的指令缓存,但是将一些解码阶段移到了指令缓存之前。当指令被填入缓存时,预解码阶段进行初步解码。它确定指令边界、操作码字节位置和指令类型,并将预解码数据与指令字节一起存储。主要的解码器要做的工作更少,因为解码工作已经部分完成。
这种预解码方案是一种折衷方案,它使用额外的存储来简化主解码器,同时保持传统指令缓存的缓存容量优势。相比之下,Netburst的跟踪缓存在最大化获取带宽的方向上做出了极端的权衡,以牺牲缓存容量为代价。
AMD的指令缓存容量优势实际上比上面的图所显示的要大得多,因为单个内存位置可以缓存在英特尔的跟踪缓存中的多个位置。我们还测试了4字节的NOP。在整数应用程序中(以7-Zip为例),平均执行的指令长度实际上略低于4字节。更小的指令会增加AMD的缓存容量,但对英特尔的跟踪缓存没有帮助。英特尔声称他们的跟踪缓存的命中率类似于8 KB指令缓存。
现代芯片可以使用宽解码器和长距离预取来保持L2的高指令带宽。不幸的是,K8的耦合BTB阻止了内核在L1i未命中后准确地确定未来的获取地址。这可能就是为什么AMD只支持两个未完成的指令缓存未命中(使用两个指令MAB,或未命中地址缓冲区)。K8 从指令端需要更多的内存级并行(MLP)来隐藏 L2 延迟,但是如果内核不能准确地预取,就会浪费更多的 MLP。因此,当指令占用空间适合 L2 而不是 L1i 时,AMD 最终落后于 Netburst。
如果面临长指令,K8还会受到L1i带宽的限制。为了证明这一点,下面是带有8字节NOP的测试结果,以每个周期的字节数表示。AMD不能接近每周期3条指令(每周期24字节),因为指令缓存每周期只能提供16字节。Netburst的微操作缓存绕过了这个限制。
公平地说,这一指标在21世纪初并不那么重要。K8不需要处理长AVX或AVX-512指令,这些指令由于额外的前缀而更长。Athlon 时代的“长”指令通常是具有大立即数或位移的指令。Netburst 也不是最擅长处理这些问题的。正如 Agner 的说明手册所详述的,具有大立即数或位移的指令有时会在跟踪缓存中占用两个条目。
重命名
当微操作被分配到后端时,现代 CPU 可以发挥很多技巧。例如,可以取消注册到注册副本。但这是 2006 年,所以我们得到了最重要的基础知识。常见的归零习惯用法被识别并且依赖关系被打破,但仅此而已。
执行引擎
在我们关于Netburst的文章中,我们谈到了它是如何放弃P6的ROB+RRF方案,而采用PRF方案的。K8采用了两者的混合。整数端行为类似于P6,尽管K8的RRF有足够的端口来避免寄存器读带宽瓶颈。浮点和向量执行端使用PRF, ROB持有指向单独向量寄存器文件中的条目的指针。实际上,这种区别并不重要。K8有120个FP寄存器,在减去保持体系结构状态所需的条目之后,这足以覆盖整个ROB。
这120个FP寄存器比K7的88个条目FP寄存器文件增加了很多,但是这是必要的,因为x86-64带来了更多的架构寄存器。具体来说,现在有16个架构上的SSE寄存器,而32位x86上只有8个。因此,K8需要更多的条目来保存架构状态。否则,新内核最终将能够跟踪比其前代更少的正在进行的向量操作。
除了更大的寄存器文件之外,K8还向每个整数调度队列添加了两个条目,使整个整数调度容量从K7的18增加到24。这是一个可喜的变化,但总体来说,K8与K7相比变化不大。最终,它远远落后于Netburst的重新排序能力——至少在纸面上是这样。
理论上,英特尔在吸收缓存和内存延迟方面会做得更好。但英特尔也不能在失效前取消虚假操作,因此重新排序的能力将浪费在跟踪从错误路径获取的指令上,即使在错误预测被发现后。如果伪路径上的指令的延迟比错误预测的延迟要长,英特尔将因此失去重新排序的能力。最后,为了提高SMT的产量,英特尔可能选择了比单线程更优的重新排序能力。
整数执行
在整数方面,AMD采取了一种蛮力的方法,几乎所有东西都创建三个。ROB由24条线组成,每条线有3个微操作,可以看作是一个3通道的设计。整数执行引擎基本上是由转发网络连接的三个通用管道,每个管道都能够处理大多数x86指令。其中包括将数学运算与内存访问相结合的指令,因此每个管道都有一个小的调度队列,为ALU和AGU提供数据。与英特尔的P6不同,load-op和load-op-store指令并没有被分解成单独的微操作。相反,该指令被保存为一个“宏操作”,调度程序根据需要将其多次分派到它的AGU和ALU管道。
因为执行引擎不会在管道之间移动指令,所以每个ALU都必须处理绝大多数整数运算。所有ALU必须同时接受负载和存储。一个值得注意的例外是整数乘法,乘法器占用更多的模具面积,所以只有第一个管道得到一个乘法器,重命名者将所有乘操作放入该管道中。
AMD的方法在几个方面简化了设计。大多数ALU指令可以进入任何管道,因此重命名者可以以轮循方式分配操作,而不必担心一个管道获得太多工作,而其他管道则处于空闲状态。它甚至可以在“通道”上保存从解码到退出的指令。我还可以想象这样一种设置:在必要时,第二个和第三个重命名器槽切换到第一个重命名器槽,这意味着每个重命名器槽只需要跟踪其通道中的资源(而不是整个OOO引擎)。此外,调度程序和ALU/AGU块可以设计一次并使用三次。
还有一些真正的性能优势。在整个管道中保持load-op和load-op-store指令为单一的“宏操作”可以更有效地使用重新排序的资源。这些指令只会占用一个重排序缓冲区和调度器条目,但在英特尔P6架构的早期版本中会占用两个或更多。
但是这种方案意味着整数执行资源被过度复制。由于内存指令吞吐量受到双端口数据缓存的限制,这三个AGU永远不能被充分利用。每个ALU都比它们处理常见指令混合所需的更强大。所有三个ALU都可以处理非执行跳转和移位指令。即使是不常见的整数指令,如CWD(通过符号扩展将字转换为双字)、BSWAP(用于在大端和小端之间转换)和SAHF(设置标志),也可以在K8上以每个周期3次执行。要知道这种执行资源的分配是多么荒谬,Golden Cove 只有 1 或 2 个能够处理这些指令的端口。
浮点和向量执行
像以前的Athlon核心一样,K8的FPU是作为协处理器实现的,它在很大程度上独立于整数端。FPU有自己的重命名器、调度器和寄存器。浮点指令和矢量指令仍然需要一个ROB条目,但是将它们的结果写入FPU的专用寄存器文件而不是ROB。K8还保留了Athlon的超大型36条目FP调度程序。Netburst和K8都有一半的指令是等待执行的FP操作——这个比例比现代CPU要高得多。
整数执行单位往往很小,但浮点和向量单位则不然。AMD负担不起将所有与FP/向量相关的东西做成三个,所以K8的FPU得到了三个专门的管道,由一个36入口的统一调度器提供。整数端的三个相等管道设置所允许的简化不适用于FPU端。此外,高效地执行遗留的x87代码需要一些技巧来打破对堆栈顶部寄存器的依赖,因此FPU获得了自己的重命名器。
执行单元是64位宽的(如果使用x87,则为80位宽),因此128位SSE向量操作被分解为两个微操作。不过,向量和浮点吞吐量比Netburst更好,Netburst也使用64位执行单元,但只有一个管道可以处理FP/向量操作。矢量整数操作在K8上可以使用两个管道,而在Netburst上也只能使用一个管道。
AMD也有更好的浮点执行延迟。K8可以以4个周期延迟处理FP加法和乘法,而Netburst分别需要5和7个周期。
内存执行(加载/存储单元)
当任何流水线CPU处理一个加载指令时,它必须决定数据应该来自缓存还是一个动态存储指令。这是通过将加载的地址与飞行中的早期存储的地址进行比较来实现的。
但是彻底的比较是昂贵的,CPU设计人员将尝试实现一个涵盖最简单、最常见情况的快速路径。然后,更困难的使用回退路径,可能涉及在执行加载之前等待存储完成,然后从缓存中读取数据。
K8的内存排序检查机制是非常基本的。存储到加载转发需要精确的地址匹配和8字节对齐。Netburst可以处理地址匹配的所有情况,除非其中一个访问跨越了64B缓存线边界。失败的存储转发情况通常需要10个周期,但是如果跨越8字节的边界,损失将增加到15个周期。即使是非重叠访问,如果跨越8B边界,吞吐量也会下降。如果存储转发成功,在Netburst和K8上的延迟大约是5个周期。
不过,最大的不同是Netburst的巨额罚款。失败的存储转发成本为51个周期,而K8上为10-15个周期。与Netburst的23-100个周期相比,错位访问在K8上需要额外的一两个周期,这取决于错位访问是加载还是存储。
K8的内存排序缓冲区也值得一提,因为它是一个非常独特的设计。现在大多数CPU都有独立的加载队列和存储队列。K8有两个统一的队列。第一个有12个条目,保存着等待检查L1D缓存的内存操作。在第一个队列中的操作探测了缓存之后,它们被移动到第二个32条目队列,在那里等待退出。
就重新排序能力而言,K8可以跟踪多达32条飞行中的内存指令,而不需要单独限制负载或存储。这种灵活性让AMD在加载/存储单元中使用更少的队列条目。但是Netburst的加载队列和存储队列都大于32个条目,因此AMD在这方面无疑处于劣势(除非Netburst有一批条目被虚假的操作阻塞)。
缓存和内存子系统
为了加快数据访问速度,每个K8核都有一个64 KB的L1数据缓存和1 MB的L2数据缓存。L2缓存是牺牲性高速缓存,不包含L1D内容。由于L1和L2都是回写式缓存,它们具有ECC保护功能。
延迟
由于L1D具有令人印象深刻的三个周期延迟,Athlon 64 FX-62与奔腾至尊版965的L1D延迟相当,尽管运行在较低的时钟频率下。
在L2,情况更有趣。K8的L2较小,但延迟比英特尔低得多。理论上,AMD的延迟甚至更低。Hot Chips介绍K8的幻灯片显示L2延迟为11个周期,但我们测量到了12.5个周期。我想知道这是否只适用于L2缓存较小的变体,因为K8的L2缓存大小可配置为1 MB。
当缓存丢失时,AMD的集成内存控制器允许Athlon以令人印象深刻的低延迟访问DRAM。60纳秒的原始内存延迟即使以现代标准衡量也是相当不错的。Netburst必须通过主板芯片组来命中DRAM,并遭受超过94ns的延迟。英特尔的延迟劣势可能促使他们增加了L2缓存的大小,因为他们的内存访问比AMD的要昂贵得多。
客户端应用程序通常使用4K页面,因为较大的页面大小往往会浪费内存,并面临碎片问题。但是跟踪更小的页面意味着更少的TLB覆盖,因此需要更高的地址转换代价。
由于有更大的64条目DTLB, 英特尔在256 KB以下有一个小优势。AMD的K8只有32条目的L1 DTLB,所以当测试大小超过128 KB时,地址转换损失使其有效的L2延迟接近英特尔。但是在较大的测试规模中,K8可以使用它的512条目L2 TLB。相比之下,Netburst什么都没有。没有L2 TLB,所以英特尔吃了20多个周期的页面历惩罚。在DRAM中,页遍历惩罚更严重,因为分页结构本身可能溢出缓存。在使用1GB阵列进行测试时,K8的内存访问延迟几乎是Netburst的一半。
带宽
缓存带宽不是K8的强项。Netburst拥有更宽的数据通路和更高的时钟,这给了它巨大的优势。通过SSE读取,K8无法在内存层次结构的任何级别上匹配Netburst的带宽。Agner的指令表表明,K8使用64位标量读取实际上可以获得更高的缓存带宽。128位SSE读取(MOVDQA和MOVDQU)被分解成两个64位的微操作,只能在FSTORE管道上执行,导致每两个周期一次128位读取的吞吐量。同时,受双端口数据缓存的限制,标量64位负载可以使用三个AGU执行每个周期。这就造成了一种尴尬的局面,需要大量带宽的矢量代码最终访问的缓存带宽可能比标量代码更少,而标量代码通常更关心延迟而不是带宽。
但是一旦我们进入内存并加载两个核心,K8就会做得更好。AMD集成的内存控制器和更快的内存配置意味着当两个核心都加载时,内存带宽可以扩展。与此同时,奔腾极致版965在所有线程活动的情况下实际上会损失DRAM带宽,这可能是因为共享前端总线上的争用。
Athlon FX-62 使用 DDR2-800 4-4-4-12,而 Pentium EE 965 使用 DDR2-533 3-3-3-9
写入带宽
K8使用回写L1数据缓存,因此写入L1D的数据一直保存在那里,直到它被删除。通过Netburst的透写L1D,写入L1D的数据会立即写入L2,这意味着写入带宽受到L2带宽的限制。我在Netburst的文章中提到过,我不认为透写缓存的好处可以抵消它的缺点。
由于Netburst的L1D带宽在写入方面是MIA,当测试大小适合L1D时,AMD有很大的写入带宽优势。英特尔在L2上有一个小的写入带宽领先优势,但在DRAM上又失利了。
相干延迟
需要跨核心数据移动的竞争锁在Netburst上的开销更大,因为所有的离核通信都要通过一个外部的北桥。K8可以通过其集成的北桥处理核心到核心的通信,因此具有更低的延迟。
K8 与 Netburst 细分
最后的话
AMD的K8架构相对于之前基于k7的Athlon架构有了微小的改进。整数调度队列得到了两个额外的条目,TLB大小增加了一倍,FP注册文件得到了额外的条目,以更好地处理SSE负载。但仅此而已。重新排序容量、核心宽度、分支预测资源和执行单元布局都保持不变。K8给人的感觉就像是英特尔tick-tock模型中的一个“tick”和一个“tock”。
与Netburst相比,K8的重新排序能力较低,而且没有华而不实的微架构特性。内核的某些部分看起来非常原始,比如需要64位对齐才能工作的存储转发机制。理论上,它应该没有机会对抗英特尔的怪兽。但是规范可能会误导人,酷炫的架构技术并不能替代正确的基础。这就是K8保持竞争力的原因。这是一种宽容的架构,具有轻微的惩罚和低延迟。通过保守地迭代经过验证的设计,并继续在基础上执行良好,AMD实现了更高的IPC,并保持对英特尔更先进和创新的Netburst设计的竞争力。
AMD没有追求大幅度的架构变化,而是把工程精力集中在将x86指令集扩展到64位,并实现系统级别的变化。这两个领域的工作都取得了成效。x86-64随后主导了消费者和服务器市场,与“等效的x86-32实现”相比,只增加了2-3%的模具尺寸。更重要的是,x86-64是32位x86的非常直接的扩展,它最小化了指令编码差异。这使得它很容易与现有的32位代码保持兼容性和高性能。相比之下,ARM决定在32位和64位之间大幅改变指令编码(以及更多),这增加了解码器的复杂性,意味着即使晶体管预算增加,他们也无法无限期地维持32位的兼容性。
K8的新集成内存控制器提供了更短的延迟和更高的带宽。并且 AMD 的 HyperTransport 启用了高带宽跨插槽链接,这是多插槽服务器的一个重要优势。在整个 2000 年代中期,这种精心分配的工程努力得到了回报,并使 AMD 能够与一家更大的公司正面交锋。
但最初的Athlon核心是在1999年推出的。到了2006年,这个架构已经有点过时了。当英特尔在那年年底推出Merom时,K8要想保持竞争力,需要的不仅仅是一个小改动。
★ 点击文末【阅读原文】,可查看本文原文链接!
*免责声明:本文由作者原创。文章内容系作者个人观点,半导体行业观察转载仅为了传达一种不同的观点,不代表半导体行业观察对该观点赞同或支持,如果有任何异议,欢迎联系半导体行业观察。
今天是《半导体行业观察》为您分享的第3143内容,欢迎关注。
推荐阅读
半导体行业观察
『半导体第一垂直媒体』
实时 专业 原创 深度
识别二维码,回复下方关键词,阅读更多
晶圆|集成电路|设备|汽车芯片|存储|台积电|AI|封装
回复 投稿,看《如何成为“半导体行业观察”的一员 》
回复 搜索,还能轻松找到其他你感兴趣的文章!
微信扫码关注该文公众号作者