相关:x86上失败的存储到加载转发的成本是多少?对于多个SF停顿未被同时处理的细节有更多说明,但在SF停顿正在进行时可以发生成功的SF。
有序Atom可能能够在不停顿的情况下进行此存储转发。
Agner Fog没有特别提到Atom的这种情况,但与所有其他CPU不同,它可以在从存储到更宽或不同对齐的加载时具有1个时钟周期的存储转发延迟。唯一的例外是在缓存行边界处,Atom表现非常糟糕(即使不涉及存储转发,对于CL-split加载或存储,也会有16个周期的惩罚)。
这个负载能否被存储-转发,还是需要等待前面的两个存储提交到L1?这里存在一个术语问题。许多人会将“这个负载能否被存储-转发”解释为是否可以像满足快速路径存储转发的所有要求一样低延迟地进行,如@IWill的答案中列出的那样(其中所有加载的数据都来自与任何负载重叠的最近存储器,并且其他相对/绝对对齐规则得到满足)。起初我以为你漏掉了第三种可能性,即较慢但仍然(几乎)固定延迟的转发,而无需等待提交到L1D,例如使用刮取整个存储缓冲区(和可能从L1D加载)的机制,在Agner Fog和Intel的优化手册中称为“存储转发失败”。但现在我看到这种措辞是有意的,你确实想问第三个选项是否存在。你可能想把其中一些编辑到你的问题中。总之,Intel x86 CPU 的三个可能选项为:
Intel/Agner将存储转发成功定义为,所有数据仅来自一个最近的存储,并具有低且(几乎)固定的延迟。
额外的(但有限的)延迟用于扫描整个存储缓冲区并按程序顺序组装正确的字节,并(如果必要或总是?)从L1D加载以提供未被最近存储的任何字节的数据。
我们不确定是否存在这种选项。
它还必须等待所有存储数据uop的数据,因为它必须遵守程序顺序。可能会发布一些关于具有未知存储地址的推测执行的信息(例如,猜测它们不重叠),但我忘记了。
等待所有重叠的存储提交到L1D,然后从L1D加载。
一些真正的x86 CPU在某些情况下可能会退回到此选项,但它们可能始终使用选项2而不引入StoreLoad屏障。(请记住,x86存储必须按程序顺序提交,加载也必须按程序顺序进行。这将有效地将存储缓冲区耗尽到此点,像mfence
一样,尽管稍后对其他地址的加载仍然可以推测地存储转发或只从L1D中获取数据。)
中间选项的证据:
Can x86 reorder a narrow store with a wider load that fully contains it?提出的锁定方案可以在需要将失败的store-forwarding清除到L1D的情况下工作。但由于在实际硬件上没有mfence
就无法工作,这强烈表明实际的x86 CPU正在将存储缓冲区的数据与L1D中的数据合并。因此,在这种情况下存在并使用第二种选择。
请参见Linus Torvalds' explanation that x86 really does allow this kind of reordering,他回应了另一个人提出的与该SO问题相同的锁定想法。
我尚未测试store-forwarding失败/停顿惩罚是否可变,但如果不可变,那么它很可能会退回到在最佳情况下的转发不起作用时检查整个存储缓冲区。
希望有人能回答What are the costs of failed store-to-load forwarding on x86?,它正是询问这个问题的。如果我有时间,我会回答。
Agner Fog只提到一种存储转发惩罚的数字,并没有说如果缓存未命中存储在无法转发之前进行,这个数字会更大。 (这会导致很大的延迟,因为由于x86的强有序内存模型,存储必须按顺序提交到L1D。)他也没有提到数据来自1个存储器+ L1D和来自两个或多个存储器的部分情况是不同的,所以我猜在这种情况下它也有效。
我怀疑“失败”的存储转发很常见,值得用晶体管更快地处理它,而不仅仅是清空存储队列并从L1D重新加载。
例如,
gcc没有特别避免存储转发停顿的尝试, 它的一些惯用语会导致这种情况(例如,在32位代码中
__m128i v = _mm_set_epi64x(a, b);
将存储/重新加载到堆栈中,这在大多数CPU上对于大多数情况来说已经是错误的策略,因此出现了该错误报告)。虽然这不是好事,但据我所知,结果通常不会灾难性。
add cx, 127
(66 opcode modrm imm8
)是可以的,add cx, 128
(66 opcode modrm imm16
)不行。 还要注意,最近的英特尔CPU不会在mov-immediate
上LCP停顿,只会在其他ALU指令上停顿。(而且,LCP停顿只会影响解码,而不会影响uop缓存)。 - Peter Cordes