如何通过按顺序提交load-> store重新排序?

问题描述 投票:6回答:1

ARM允许重新排序加载后续存储,以便以下伪代码:

// CPU 0 | // CPU 1 temp0 = x; | temp1 = y; y = 1; | x = 1;

可以导致temp0 == temp1 == 1(并且,这在实践中也是可观察到的)。我无法理解这是怎么发生的;似乎有序提交会阻止它(这是我的理解,它存在于几乎所有的OOO处理器中)。我的理由是“在提交之前,负载必须具有其值,它在存储之前提交,并且在提交之前,存储的值不会对其他处理器可见。”

我猜我的一个假设肯定是错的,并且必须遵循下列之一:

  • 指令不需要按顺序一直提交。稍后的存储可以安全地提交并在之前的加载之前变得可见,只要在存储提交核心时可以保证先前的加载(以及所有中间指令)不会触发异常,并且加载的地址是保证与商店不同。
  • 负载可以在其值已知之前提交。我不知道如何实现这一点。
  • 商店在提交之前可以显示。也许某个内存缓冲区允许将存储转发到另一个线程的加载,即使负载先前已加入?
  • 还有别的吗?

有许多假设的微体系结构特征可以解释这种行为,但我最好的是那些实际存在于现代弱有序CPU中的那些。

arm cpu-architecture memory-barriers
1个回答
6
投票

您的假设所有内容对我来说都是正确的,除了您可以构建一个uarch,其中只需检查负载上的权限(TLB)以确保它肯定会发生,负载可以从OoO核心退出。可能有OoO exec CPU执行此操作(更新:显然有)。

我认为x86 CPU需要负载来实际让数据在退出之前到达,但是他们强大的内存模型无论如何都不允许LoadStore重新排序。所以ARM肯定会有所不同。

你是对的,在退休之前商店不能被任何其他核心看到。那种方式就是疯狂。即使在SMT core(一个物理内核上的多个逻辑线程)上,它也会将两个逻辑线程上的推测链接在一起,如果任何一个检测到错误推测,则要求它们都回滚。这会破坏SMT的目的,让一个逻辑线程利用其他人的档位。

(相关:对同一核心上的其他逻辑线程可见的已退役但尚未提交(到L1d)的存储是一些真正的PowerPC实现如何使线程在全局商店订单上不一致.Will two atomic writes to different locations in different threads always be seen in the same order by other threads?


具有按顺序执行的CPU可以启动加载(检查TLB并写入加载缓冲区条目),并且只有在指令尝试在结果准备好之前使用结果时才会停止。然后,包括商店在内的后续指令可以正常运行。这对于有序流水线中的非可怕性能基本上是必需的;停止每次高速缓存未命中(或甚至只是L1d延迟)将是不可接受的。内存并行性甚至在有序CPU上也是如此;它们可以有多个加载缓冲区来跟踪多个未完成的缓存未命中。像Cortex-A53这样高性能的有序ARM内核仍然广泛应用于现代智能手机中。

因此,如果加载在高速缓存中未命中但存储命中(并且在早期高速缓存未命中加载之前提交到L1d获取其数据),则可以获得LoadStore重新排序。 (Jeff Preshing intro to memory reording将此示例用于LoadStore,但根本没有进入uarch细节。)

在检查TLB和/或任何内存区域内容后,加载不会出错。该部分必须在退休之前完成,或者在它到达有序管道的末尾之前完成。就像坐在商店缓冲区等待提交的退休商店一样,在某个时刻肯定会发生一个停在负载缓冲区中的负载。

所以有序流水线的顺序是:

  • lw r0, [r1] TLB命中,但在L1d缓存中未命中。加载执行单元将地址(r1)写入加载缓冲区。试图读取r0的任何后续指令都会停止,但我们确信负载没有错误。 由于r0与等待该缓冲区准备就绪相关联,所以lw指令本身可以离开管道(退出),以后的指令也可以。
  • 任何数量的其他指令都没有读取r0。这将阻止有序管道。
  • sw r2, [r3]存储执行单元将地址+数据写入存储缓冲区/队列。然后该指令可以退休。 探测加载缓冲区会发现此存储不与挂起的加载重叠,因此它可以提交到L1d。 (如果它已经重叠,你无法提交它,直到MESI RFO完成,并且快速重启会将输入的数据转发到加载缓冲区。因此,如果不对每个商店进行探测,处理这种情况可能不会太复杂,但是,我们只看一下我们可以获得LoadStore重新排序的单独缓存行情况) 致力于L1d =变得全球可见。当早期加载仍在等待缓存行到达时,可能会发生这种情况。

对于OoO CPU,您需要一些方法将负载完成重新绑定到OoO核心,以获取等待加载结果的指令。我想这是可能的,但这意味着寄存器的架构/报废值可能不存储在核心的任何地方。由错误推测引起的流水线冲洗和其他回滚将不得不依赖于传入负载与物理和架构寄存器之间的关联。 (不要在管道回滚上刷新存储缓冲区已经是CPU必须要做的事情了。停留但尚未提交的存储缓冲区存储无法回滚。)

对于具有小OoO窗口的搜索来说,这可能是一个很好的设计理念,这个窗口太小而无法隐藏缓存未命中。


我们在OoO ARM上有LoadStore重新排序的实验证据:https://www.cl.cam.ac.uk/~pes20/ppc-supplemental/test7.pdf的7.1节显示Tegra 2上的“load buffering”的非零计数,它基于无序的Cortex-A9 uarch。我没有查看所有其他内容,但我确实重写了答案,建议这也是无序CPU的可能机制。但我不确定是否是这种情况。

© www.soinside.com 2019 - 2024. All rights reserved.