繁体   English   中英

现代x86实现能否从多个以前的存储中进行存储?

[英]Can modern x86 implementations store-forward from more than one prior store?

如果负载与两个较早的存储区重叠(并且该负载未完全包含在最旧的存储区中),现代的Intel或AMD x86实现是否可以从两个存储区转发来满足负载?

例如,考虑以下顺序:

mov [rdx + 0], eax
mov [rdx + 2], eax
mov ax, [rdx + 1]

最终的2字节加载从紧接的前一个存储中获取其第二个字节,但在此之前从存储中获取第一个字节。 可以将此存储转发给存储,还是需要等到两个先前的存储都提交到L1时才能进行?

请注意,通过此处的存储转发 ,我将包括可以满足仍在存储缓冲区中的存储读取的任何机制,而不是等待它们提交到L1的机制,即使这是比“从A转发”的最佳情况慢的路径。单店”案例。

没有。

至少不是在Haswell,Broadwell或Skylake处理器上。 在其他Intel处理器上,限制是相似的(Sandy Bridge,Ivy Bridge)或更严格的限制(Nehalem,Westmere,Pentium Pro / II / II / 4)。 在AMD上,适用类似的限制。

来自Agner Fog出色的优化手册

Haswell / Broadwell

英特尔和AMD CPU的微体系结构

§10.12商店转运摊位

在某些条件下,处理器可以将内存写入转发给来自同一地址的后续读取。 在以下情况下,商店转发有效:

  • 写入64位或更少后,随后进行大小和地址相同的读取,无论对齐方式如何。
  • 写入128或256位后,再进行相同大小和相同地址的读取,完全对齐。
  • 当写入64位或更少的位时,随后进行较小大小的读取,该大小将完全包含在写入地址范围内,而与对齐方式无关。
  • 当任何大小的对齐写入之后,是两个一半的读取两次或四个季度的四个读取等,其自然对齐在写入地址范围内。
  • 当对齐的128位或256位写入之后是64位或更少的读取,且不会越过8字节边界。

如果存储块越过64字节的高速缓存行边界,则会发生2个时钟的延迟。 如果所有数据都具有自然对齐方式,则可以避免这种情况。

在以下情况下,商店转发失败:

  • 当任何大小的写入后跟着较大的读取
  • 当任何大小的写入后跟部分重叠的读取
  • 当写入128位之后是较小的读取时,越过两个64位半部分之间的边界
  • 当写入256位之后是读取128位时,越过两个128位半部之间的边界
  • 当写入256位之后是读取64位以下的字符时,越过四个64位四分之一之间的任何边界

失败的商店转发比成功的商店转发花费10个时钟周期。 在写入未对齐至少16位的128或256位之后,代价要高得多-大约50个时钟周期。

重点已添加

天空湖

英特尔和AMD CPU的微体系结构

第11.12条商店转运摊位

在某些情况下,Skylake处理器可以将内存写入转发给来自同一地址的后续读取。 存储转发比以前的处理器快一个时钟周期。 对于32或64位的操作数,在最佳情况下,写存储器后再从同一地址进行读取需要4个时钟周期,而对于其他操作数大小,则需要5个时钟周期。

当128或256位的操作数未对齐时,存储转发将增加多达3个时钟周期的损失。

当任何大小的操作数越过高速缓存行边界(即可被64字节整除的地址)时,存储转发通常会额外花费4-5个时钟周期。

写入后再从相同地址进行较小的读取几乎不会造成任何损失。

当读偏移量但完全包含在写所覆盖的地址范围内时,写64位或更少的位,然后进行较小的读,将产生1-3个时钟的损失。

对齐写入128或256位,然后读取两个半部或四个四分之一等中的一个或两个,等等,几乎没有损失。 不适合两半或四分之一的部分读取可能会额外花费11个时钟周期。

大于写入的读取 ,或覆盖已写入和未写入字节的读取,大约需要11个时钟周期。

重点已添加

一般来说:

Agner Fog的文档指出,跨微体系结构的一个共同点是,如果对齐写入并且读取的值是写入值的一半四分之一 ,则存储转发的可能性更大。

一个测试

具有以下紧密循环的测试:

mov [rsp-16], eax
mov [rsp-12], ebx
mov ecx, [rsp-15]

显示ld_blocks.store_forward PMU计数器确实确实增加了。 该事件记录如下:

ld_blocks.store_forward [此事件计算加载操作获得阻止存储转发的真正的存储区阻止代码的次数。 这包括以下情况:-先前存储与负载冲突(重叠不完全)

  • 由于u-arch限制,无法进行商店转发

  • 先前的锁定RMW操作不会转发

  • 存储设置了无转发位(不可缓存/页面拆分/屏蔽的存储)

  • 使用所有阻塞存储(主要是围栏和端口I / O)

这表明当只读仅部分重叠最近的较早存储时(即使考虑了较早的存储也完全包含了该存储),存储转发确实会失败。

有序的Atom可能能够执行此存储转发而完全不会停止。

Agner Fog没有特别针对Atom提到这种情况,但是与所有其他CPU不同,它可以以1c的延迟将存储从存储转发到更大或更不同的负载。 Agner发现的唯一例外是在高速缓存行边界上,其中Atom太可怕了(即使不涉及存储转发,CL拆分加载或存储也要付出16个周期的惩罚)。


可以将此存储转发给存储,还是需要等到两个先前的存储都提交到L1时才能进行?

这里有一个术语问题。 如@IWill的答案中所列出的,许多人会把“是否可以将此存储转发给存储”解释为,是否可以以与满足快速路径存储转发的所有要求一样低的延迟发生。 (如果所有加载的数据都来自最近的存储,以与任何加载重叠,并且满足其他相对/绝对对齐规则)。

起初我以为您错过了第三种可能性,即较慢但仍(几乎?)固定延迟转发而不等待提交给L1D的可能性,例如,在某些情况下采用了一种刮除整个存储缓冲区(可能是L1D的负载)的机制。 Agner Fog和Intel的优化手册称“商店转发失败”。

但是现在我看到这个措词是故意的,您确实想问第三个选项是否存在。

您可能需要将其中一些内容修改为您的问题。 总之,英特尔x86 CPU的三个可能的选择是:

  1. 英特尔/阿格纳(Intel / Agner)对存储转发成功的定义,其中所有数据仅来自最近一家具有低延迟( 几乎)固定延迟的商店。
  2. 扫描整个存储缓冲区并组装正确的字节(根据程序顺序)需要额外(但有限)的延迟,并且(如果有必要或始终是?)从L1D加载以提供最近未存储的任何字节的数据。

    这是我们不确定存在的选项

    它还必须等待尚未准备好输入的存储数据块中的所有数据,因为它必须遵守程序顺序。 可能会发布一些有关具有未知存储地址的推测执行的信息(例如,猜测它们没有重叠),但是我忘记了。

  3. 等待所有重叠的存储提交到L1D,然后从L1D加载。

    在某些情况下,某些真正的x86 CPU可能会退一步,但它们可能始终使用选项2,而不会引入StoreLoad障碍。 (请记住,x86存储必须按程序顺序进行提交,并且加载必须按程序顺序进行。这将有效地耗尽存储缓冲区到这一点,例如mfence ,尽管以后对其他地址的加载仍可以推测性地进行存储转发或仅采用来自L1D的数据。)


中间选项的证据:

x86中提出的锁定方案是否可以对一个狭窄的商店重新排序,使其具有更大的负载并完全包含它? 如果存储转发失败需要刷新到L1D,它将正常工作。 由于没有mfence ,它不能在实际的硬件上mfence ,因此有力的证据表明,真正的x86 CPU正在将存储缓冲区中的数据与L1D中的数据合并。 因此,选项2存在并在这种情况下使用。

另请参阅Linus Torvalds对x86确实允许这种重新排序的解释 ,以回应提出与该SO问题相同的锁定思想的其他人。

我尚未测试存储转发失败/停顿处罚是否可变,但是如果不是最好的话,则强烈暗示当最佳情况转发不起作用时,它会退回到检查整个存储缓冲区。

希望有人能回答在x86上存储到装载转发失败的代价是什么? ,正是这样问。 我会解决的。

Agner Fog只提到了一个单一的数字来表示存储转发的罚款,并且没有说如果高速缓存未命中的存储比未转发的存储要早的话会更大。 (这将导致很大的延迟,因为由于x86的顺序内存强,存储必须按顺序提交到L1D。)他也没有说任何关于数据来自1个存储+ L1D与来自1个存储的不同情况。两个或两个以上商店的一部分,因此我猜想它在这种情况下也适用。


我怀疑“失败”的存储转发非常普遍,以至于晶体管值得处理的速度要比刷新存储队列和从L1D重新加载更快。

例如, gcc并没有专门尝试避免存储转发停顿 ,它的某些惯用法会导致它们(例如__m128i v = _mm_set_epi64x(a, b);在32位代码中已经存储/重载到堆栈中了)大多数情况下,大多数CPU上的策略错误,因此会报告该错误)。 效果不好,但结果通常不会带来灾难性的AFAIK。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM