存储指令是否会在高速缓存未命中时阻止后续指令?
Do store instructions block subsequent instructions on a cache miss?
假设我们有一个具有两个内核(C0 和 C1)的处理器和一个从地址 k
开始的高速缓存行,该地址最初由 C0 拥有。如果 C1 在第 k
行的 8 字节槽上发出一条存储指令,是否会影响在 C1 上执行的后续指令的吞吐量?
intel优化手册有如下一段话
When an instruction writes data to a memory location [...], the processor ensures that it has the line containing this memory location is in its L1d cache [...]. If the cache line is not there, it fetches from the next levels using a RFO request [...] RFO and storing the data happens after instruction retirement. Therefore, the store latency usually does not affect the store instruction itself
参考以下代码,
// core c0
foo();
line(k)->at(i)->store(kConstant, std::memory_order_release);
bar();
baz();
英特尔手册中的引述让我假设在上面的代码中,代码的执行看起来好像存储本质上是空操作,并且不会影响 foo()
和 bar()
的开始。相反,对于以下代码,
// core c0
foo();
bar(line(k)->at(i)->load(std::memory_order_acquire));
baz();
foo()
结束和 bar()
开始之间的延迟会受到负载的影响,因为以下代码将负载结果作为依赖项。
这个问题主要与英特尔处理器(Broadwell 系列或更新的处理器)如何处理上述情况有关。另外,特别是对于那些看起来像上面的 C++ 代码如何编译为这些处理器的汇编。
一般来说,对于后续代码不会很快读取的存储,存储不会直接延迟任何现代乱序处理器上的后续代码,包括英特尔。
例如:
foo()
*x = y;
bar()
如果 foo()
不修改 x
或 y
,并且 bar
不从 *x
加载,则商店是独立的,可能甚至在 foo()
完成之前(甚至在它开始之前)就开始执行,并且 bar()
可能在存储提交到缓存之前执行,并且 bar()
甚至可能在 foo()
时执行是运行宁等
虽然 直接 影响很小,但这并不意味着没有间接影响,实际上存储可能会支配执行时间。
如果存储在缓存中未命中,它可能会在满足缓存未命中时占用核外资源。它还通常防止后续存储耗尽,这可能是一个瓶颈:如果存储缓冲区填满,前端将完全阻塞并且新指令不再进入调度程序。
最后一切还是要看周边代码的细节,照例。如果该序列重复 运行,并且 foo()
和 bar()
很短,则与存储相关的未命中可能会占据 运行 时间。毕竟,缓冲不能掩盖无限开店的成本。在某些时候,您会受到商店内在吞吐量的限制。
假设我们有一个具有两个内核(C0 和 C1)的处理器和一个从地址 k
开始的高速缓存行,该地址最初由 C0 拥有。如果 C1 在第 k
行的 8 字节槽上发出一条存储指令,是否会影响在 C1 上执行的后续指令的吞吐量?
intel优化手册有如下一段话
When an instruction writes data to a memory location [...], the processor ensures that it has the line containing this memory location is in its L1d cache [...]. If the cache line is not there, it fetches from the next levels using a RFO request [...] RFO and storing the data happens after instruction retirement. Therefore, the store latency usually does not affect the store instruction itself
参考以下代码,
// core c0
foo();
line(k)->at(i)->store(kConstant, std::memory_order_release);
bar();
baz();
英特尔手册中的引述让我假设在上面的代码中,代码的执行看起来好像存储本质上是空操作,并且不会影响 foo()
和 bar()
的开始。相反,对于以下代码,
// core c0
foo();
bar(line(k)->at(i)->load(std::memory_order_acquire));
baz();
foo()
结束和 bar()
开始之间的延迟会受到负载的影响,因为以下代码将负载结果作为依赖项。
这个问题主要与英特尔处理器(Broadwell 系列或更新的处理器)如何处理上述情况有关。另外,特别是对于那些看起来像上面的 C++ 代码如何编译为这些处理器的汇编。
一般来说,对于后续代码不会很快读取的存储,存储不会直接延迟任何现代乱序处理器上的后续代码,包括英特尔。
例如:
foo()
*x = y;
bar()
如果 foo()
不修改 x
或 y
,并且 bar
不从 *x
加载,则商店是独立的,可能甚至在 foo()
完成之前(甚至在它开始之前)就开始执行,并且 bar()
可能在存储提交到缓存之前执行,并且 bar()
甚至可能在 foo()
时执行是运行宁等
虽然 直接 影响很小,但这并不意味着没有间接影响,实际上存储可能会支配执行时间。
如果存储在缓存中未命中,它可能会在满足缓存未命中时占用核外资源。它还通常防止后续存储耗尽,这可能是一个瓶颈:如果存储缓冲区填满,前端将完全阻塞并且新指令不再进入调度程序。
最后一切还是要看周边代码的细节,照例。如果该序列重复 运行,并且 foo()
和 bar()
很短,则与存储相关的未命中可能会占据 运行 时间。毕竟,缓冲不能掩盖无限开店的成本。在某些时候,您会受到商店内在吞吐量的限制。