最近的阐述和幽灵提醒我的时间我在Xbox 360 CPU中找到了一个相关的设计错误 - 一个新添加的指令,其仅存在危险。
回到2005年我是Xbox 360 CPU的家伙。我活着呼吸那芯片。我的墙上仍然有一个30厘米的CPU晶圆,以及CPU布局的四英尺海报。我花了很多时间了解CPU的管道如何工作,当我被要求调查一些不可能的崩溃时,我能够在设计错误的情况下如何成为他们的原因。但首先,一些背景......
Xbox 360 CPU是IBM制造的三核PowerPC芯片。三个核心位于三个单独的象限中,第四象限包含1-MB L2缓存 - 您可以在右侧和CPU晶圆上看到不同的组件。每个核心具有32 kB指令缓存和32 kB数据缓存。
琐事:核心0更接近L2缓存,并具有可测量的L2延迟。
Xbox 360 CPU的一切都具有高延迟,内存延迟特别糟糕。而且,对于三核CPU,1 MB L2缓存(所有可能适合的缓存)非常小。因此,节省L2缓存中的空间以最大限度地减少缓存未命中很重要。
CPU缓存因空间和时间位置而提高性能。空间局部意味着,如果您已经使用了一个字节的数据,那么您可能很快就会使用其他附近的数据字节。时间位置意味着如果您使用了一些内存,那么您可能会在不久的将来再次使用它。
但有时候暂时的地方实际上并没有发生。如果要处理大量数据,则每帧一次,那么它可能是微不足道的,即它将在您再次需要时从L2缓存中消失。您仍然希望L1缓存中的数据仍然可以从空间局部受益,但是在L2缓存中消耗有价值的空间只是意味着它会阻碍其他数据,可能会减慢其他两个核心。
通常这是不可避免的。我们PowerPC CPU的内存一致性机制要求L1缓存中的所有数据也在L2缓存中。用于内存一致性的MESI协议要求当一个核心写入缓存行时,使用相同的缓存行的副本的任何其他核心需要丢弃它 - 并且L2缓存负责跟踪L1缓存的缓存地址。
但是,CPU是用于视频游戏控制台,并且表现胜过所有的性能所以添加了一个新的指令 - XDCBT。普通PowerPC DCBT指令是典型的预取指令。 XDCBT指令是一个扩展的预取指令,将从存储器直接获取到L1 D-Cache,跳过L2。这意味着内存一致性不再保证,但嘿,我们是视频游戏程序员,我们知道我们在做什么,它会很好。
我编写了一个广泛使用的Xbox 360内存复制例程,可选择使用XDCBT。预取源数据对于性能至关重要,通常它将使用DCBT,但在预取_EX标志中通过,并且它将使用XDCBT进行预取。这不是很好的想法。预取基本上是:
使用此函数的游戏开发人员报告奇怪的崩溃 - 堆损坏崩溃,但内存转储中的堆结构看起来正常。在盯着崩溃的垃圾箱之后,我意识到了我所犯的错误。
用XDCBT预取的内存有毒。如果它是由另一个核心写成的,然后从L1刷新之前,那么两个核心有不同的内存视图,并且没有保证他们的观点将收敛。 Xbox 360缓存行为128字节,我的复制例程预取右转到源存储器的末尾,这意味着XDCBT应用于后一个部分是相邻数据结构的一部分的一些高速缓存行。通常,这是堆元数据 - 至少是我们看到崩溃的地方。不连贯的核心锯陈旧数据(尽管仔细使用锁),并崩溃,但崩溃转储写出了RAM的实际内容,以便我们看不到发生的事情。
因此,使用XDCBT的唯一安全方法是非常小心,即使在缓冲区结束之外也要预取。我修复了我的记忆复制例程,避免预取太远,但在等待修复的时候,游戏开发人员停止传递预取邮件,崩溃崩溃了。
到目前为止如此正常,对吧?公鸡游戏开发商用火,飞到太阳太靠近阳光,嫁给他们的母亲,一场游戏机几乎错过了圣诞节。
但是,我们及时抓住了它,我们逃脱了,我们都设置了发货和控制台,回家快乐。
症状相同。除了游戏不再使用XDCBT指令。我可以缩短代码并看到这一点。我们有一个严重的问题。
我用古代调试技术用空白的心灵盯着我的屏幕,让CPU管道填补我的潜意识,我突然意识到了这个问题。快速电子邮件给IBM确认我怀疑我以前从未想过的微妙内部CPU细节。它是崩溃和幽灵背后的罪魁祸首。
Xbox 360 CPU是一个有序CPU。真的很简单,依靠其高频率(尽管10 o4的高度希望,尽管10 o4)进行性能。但它确实有一个分支预测因素 - 它的长管道使必要。这是一个公开共享的CPU管道图我所做的(我的循环准确版本是NDA,但在这里看起来),显示所有管道:
您可以看到分支预测器,并且您可以看到管道长度(图上宽) - 足够长的时间,以便在秩序处理中获得速度的错误指令。
因此,分支预测器使预测,预测指令被提取,解码和执行 - 但未退出,直到已知预测是正确的。听起来有点熟?我有的实现 - 当时对我来说是新的 - 它是推测性地执行预取的意义。延迟很长,因此很快就要尽快在公共汽车上获得预取交易,一旦启动预取,就无法取消它。所以授课的执行XDCBT与真正的XDCBT相同! (推测执行的负载指令只是预取,FWIW)。
这就是问题 - 分支预测因子有时会导致XDCBT指令被推测地执行,并且与真正执行它们的情况也一样糟糕。我的一个同事(谢谢Tracy!)建议验证这个聪明的测试 - 用断点替换游戏中的每一个XDCBT。这实现了两件事:
断点未击中,从而证明游戏未执行XDCBT指令。
我知道这将是结果,但它仍然是惊人的。这些年后,即使阅读了关于崩溃之后,它仍然是简单的酷,看看未被执行的指令造成崩溃的实心证明。
分支预测器实现明确说明这条指令太危险,无法在任何游戏的代码段中的任何位置 - 控制当可能投机地执行的指令太难时。理论上,间接分支的分支预测器可以理解地预测任何地址,因此没有“安全的地方”来放置XDCBT指令。并且,如果推测性地执行,它将愉快地执行指定寄存器发生在随机包含的任何内存中的扩展预取。有可能降低风险,但不能消除它,它不值得。虽然Xbox 360架构讨论继续提及我怀疑任何游戏都会发出的指令。
我在求职期间提到了这次曾经 - “描述了你必须调查的最艰难的虫子” - 面试官的反应是“是的,我们在阿尔法处理器上袭击了类似的东西”。改变的事情越多......
如何预设从未拍摄的分支?简单的。分支预测器不会为可执行文件中的每个分支保持完美的历史 - 这将是不切实际的。相反,简单的分支预测器通常会将一堆地址位挤出,也许是一些分支历史比特,并且索引到两位条目的数组中。因此,分支预测结果受其他不相关的分支的影响,导致有时是虚假的预测。但没关系,因为它是“只是一种预测”,它不需要是正确的。
此条目在调试和标记的分支预测器,熔点,PowerPC,推测执行,Xbox 360中发布。