如果负载与两个早期存储重叠(并且负载未完全包含在最早的存储中),现代Intel或AMD x86实现是否可以从两个存储转发以满足负载?
例如,请考虑以下顺序:
mov [rdx + 0], eax
mov [rdx + 2], eax
mov ax, [rdx + 1]
最后的2字节加载从前一个存储区获取第二个字节,但是它之前的存储区的第一个字节。这个负载可以存储转发,还是需要等到两个先前的存储都提交到L1?
请注意,通过 store-forwarding ,我可以包括任何能够满足仍然存储缓冲区中存储的读取的机制,而不是等待它们提交到L1,即使它是一个比最好的情况更慢的路径"来自单个商店的转发"情况下。
答案 0 :(得分:7)
有序Atom可以在不停顿的情况下进行存储转发。
Agner Fog并未专门针对Atom提及此案例,但与所有其他CPU不同,它可以存储从存储到更宽或不同对齐的负载的1c延迟。 Agner发现的唯一例外是缓存线边界,其中Atom非常糟糕(对于CL拆分加载或存储,即使不涉及存储转发,也会有16周期的惩罚)。
这个加载可以存储转发,还是需要等到两个先前存储都提交到L1?
这里有一个术语问题。许多人会解释"这种负载是否可以存储转发"正如在@ IWill的回答中所列出的那样,询问它是否能够以低延迟发生,就像快速路径存储转发满足所有要求一样。 (所有加载的数据都来自最近的商店,以重叠任何负载,以及其他相对/绝对 符合对齐规则。)
我一开始以为你错过了第三种可能性,即慢速但仍然(几乎?)固定的延迟转发而不等待提交到L1D,例如在Agner Fog和英特尔的优化手册调用"存储转发失败"
的情况下,使用一种机制来刮擦整个存储缓冲区(以及可能来自L1D的负载)。但现在我看到这个措辞是有意的,你真的想问第三个选项是否存在。
您可能想要将其中一些内容编辑到您的问题中。总之,英特尔x86 CPU的三种可能选择是:
扫描整个存储缓冲区的额外(但有限)延迟并组装正确的字节(根据程序顺序),并且(如果必要或始终?)从L1D加载以提供任何字节的数据#39; t最近存储。
这是我们不确定的选项。
它还必须等待来自尚未准备好输入的商店数据uop的所有数据,因为它必须遵守程序顺序。可能会发布一些关于投机执行的信息,包括未知的商店地址(例如猜测它们不重叠),但我忘了。
等待所有重叠的商店提交到L1D,然后从L1D加载。
在某些情况下,某些真正的x86 CPU可能会退回到此状态,但它们可能始终使用选项2而不引入StoreLoad屏障。 (请记住,x86存储必须按程序顺序提交,并且加载必须按程序顺序进行。这将有效地将存储缓冲区排到这一点,如mfence
,尽管稍后加载到其他地址仍然可以推测性地存储 - 转发或从L1D获取数据。)
如果存储转发失败需要刷新到L1D,Can x86 reorder a narrow store with a wider load that fully contains it?中提出的锁定方案将起作用。由于它在没有mfence
的情况下不能在真实硬件上工作,因此有力的证据表明真正的x86 CPU正在将来自存储缓冲区的数据与来自L1D的数据合并。因此选项2存在并在这种情况下使用。
另见Linus Torvalds' explanation that x86 really does allow this kind of reordering,以回应提出与SO问题相同锁定思想的其他人。
我还没有测试存储转发失败/失速惩罚是否可变,但如果没有那么强烈意味着当最佳情况转发不起作用时它会回退到检查整个存储缓冲区。
希望有人会回答What are the costs of failed store-to-load forwarding on x86?,这正是要求的。如果我接触它,我会的。
Agner Fog只提到一个号码用于存储转发处罚,并且如果缓存缺失商店在未能转发的商店之前飞行,则不会说它更大。 (这会导致很大的延迟,因为由于x86的强排序内存模型,商店必须按顺序提交L1D。)他也没有说明数据来自1的不同情况商店+ L1D与两个或更多商店的部分相比,所以我猜这也适用于这种情况。
我怀疑"失败"存储转发很常见,以至于值得晶体管处理它的速度要快于刷新存储队列并从L1D重新加载。
例如,gcc doesn't specifically try to avoid store-forwarding stalls及其一些习惯用法导致它们(例如{32}代码中的__m128i v = _mm_set_epi64x(a, b);
存储/重新加载到堆栈,对于大多数情况,这在大多数CPU上已经是错误的策略因此,错误报告)。这不好,但结果通常不是灾难性的,AFAIK。