如何强制 cpu 核心刷新 c 中的存储缓冲区?
How to force cpu core to flush store buffer in c?
我有一个有 2 个线程的应用程序,线程 A 与核心 1 的亲和力和线程 B 与核心 2 的亲和力,
核心 1 和核心 2 在同一个 x86 插槽中。
线程A做一个整数x的忙自旋,线程B在某些情况下会增加x,当线程B决定增加x时,它使x所在的缓存行无效,并且根据x86 MESI协议,它存储new x to store buffer before core2 receive invalidate ack, then after core2 receive invalidate ack, core2 flush store buffer .
我想知道,core2 是否在 core2 收到无效确认后立即刷新存储缓冲区?!我有没有机会强制 cpu 用 c 语言刷新存储缓冲区?!因为在我的例子中,核心 1 中的线程 A 旋转 x 应该尽早获得 x 新值。
你需要使用原子。
如果你真的想的话,你可以使用 atomic_thread_fence
(这个问题有点 XY 问题),但是让 x
原子化并使用 [=12= 可能会更好] 和 atomic_load
,或者 atomic_compare_exchange_weak
.
核心总是尝试尽快将其存储缓冲区提交给 L1d 缓存(并因此变得全局可见),以便为更多存储腾出空间。
您可以使用屏障(如 atomic_thread_fence(memory_order_seq_cst
)使线程 等待 其存储在执行任何更多加载或存储之前变得全局可见,但是通过阻塞这个核心来工作,而不是通过加速刷新存储缓冲区。
显然为了避免 C11 中的未定义行为,变量必须是 _Atomic
。如果只有一个作者,您可以使用 tmp = atomic_load_explicit(&x, memory_order_relaxed)
和 tmp+1
的 store_explicit 来避免更昂贵的 seq_cst 存储或原子 RMW。 acq / rel 排序也可以,只是避免默认 seq_cst,如果只有一个作者,则避免 atomic_fetch_add
RMW。
如果只有一个线程修改它,并且其他线程以只读方式访问它,则不需要整个 RMW 操作是原子的。
在另一个核心可以读取您写入的数据之前,它必须从将其写出的核心 L1d 中的修改状态转移到 L3 缓存,然后从那里到达 reader 的 L1d核心.
您可以加快 这 部分的速度,这发生在数据离开存储缓冲区之后。但是您可以做的事情并不多。您不想 clflush
/clflushopt
,这会写回 + 完全逐出缓存行,这样另一个核心就必须从 DRAM 中获取它,如果它没有尝试读取它的话在这个过程中的某个时刻(如果可能的话)。
冰湖有 clwb
which (hopefully) leaves the data cached as well as forcing write-back to DRAM. But again that forces data to actually go all the way to DRAM, not just a shared outer cache, so it costs DRAM bandwidth and is presumably slower than we'd like. (Skylake-Xeon has it, too, but 。我期待并希望 Ice Lake client/server has/will 有一个正确的实现。)
Tremont (successor to Goldmont Plus, atom/silvermont series) has _mm_cldemote
(cldemote
)。这与 SW 预取相反;将缓存行写入 L3 是一个可选的性能提示,但不会强制它进入 DRAM 或任何东西。
如果没有特殊说明,也许您可以写入 L2 和 L1d 高速缓存中同组别名的其他 8 个位置,强制进行冲突驱逐。这会在写入线程中花费额外的时间,但可以使数据更快地供其他想要读取它的线程使用。我没试过这个。
这也可能会驱逐其他行,从而消耗更多的 L3 流量 = 系统范围的共享资源,而不仅仅是在生产者线程中花费时间。你只会考虑延迟,而不是吞吐量,除非其他行是你想要写入和驱逐的行。
我有一个有 2 个线程的应用程序,线程 A 与核心 1 的亲和力和线程 B 与核心 2 的亲和力, 核心 1 和核心 2 在同一个 x86 插槽中。
线程A做一个整数x的忙自旋,线程B在某些情况下会增加x,当线程B决定增加x时,它使x所在的缓存行无效,并且根据x86 MESI协议,它存储new x to store buffer before core2 receive invalidate ack, then after core2 receive invalidate ack, core2 flush store buffer .
我想知道,core2 是否在 core2 收到无效确认后立即刷新存储缓冲区?!我有没有机会强制 cpu 用 c 语言刷新存储缓冲区?!因为在我的例子中,核心 1 中的线程 A 旋转 x 应该尽早获得 x 新值。
你需要使用原子。
如果你真的想的话,你可以使用 atomic_thread_fence
(这个问题有点 XY 问题),但是让 x
原子化并使用 [=12= 可能会更好] 和 atomic_load
,或者 atomic_compare_exchange_weak
.
核心总是尝试尽快将其存储缓冲区提交给 L1d 缓存(并因此变得全局可见),以便为更多存储腾出空间。
您可以使用屏障(如 atomic_thread_fence(memory_order_seq_cst
)使线程 等待 其存储在执行任何更多加载或存储之前变得全局可见,但是通过阻塞这个核心来工作,而不是通过加速刷新存储缓冲区。
显然为了避免 C11 中的未定义行为,变量必须是 _Atomic
。如果只有一个作者,您可以使用 tmp = atomic_load_explicit(&x, memory_order_relaxed)
和 tmp+1
的 store_explicit 来避免更昂贵的 seq_cst 存储或原子 RMW。 acq / rel 排序也可以,只是避免默认 seq_cst,如果只有一个作者,则避免 atomic_fetch_add
RMW。
如果只有一个线程修改它,并且其他线程以只读方式访问它,则不需要整个 RMW 操作是原子的。
在另一个核心可以读取您写入的数据之前,它必须从将其写出的核心 L1d 中的修改状态转移到 L3 缓存,然后从那里到达 reader 的 L1d核心.
您可以加快 这 部分的速度,这发生在数据离开存储缓冲区之后。但是您可以做的事情并不多。您不想 clflush
/clflushopt
,这会写回 + 完全逐出缓存行,这样另一个核心就必须从 DRAM 中获取它,如果它没有尝试读取它的话在这个过程中的某个时刻(如果可能的话)。
冰湖有 clwb
which (hopefully) leaves the data cached as well as forcing write-back to DRAM. But again that forces data to actually go all the way to DRAM, not just a shared outer cache, so it costs DRAM bandwidth and is presumably slower than we'd like. (Skylake-Xeon has it, too, but
Tremont (successor to Goldmont Plus, atom/silvermont series) has _mm_cldemote
(cldemote
)。这与 SW 预取相反;将缓存行写入 L3 是一个可选的性能提示,但不会强制它进入 DRAM 或任何东西。
如果没有特殊说明,也许您可以写入 L2 和 L1d 高速缓存中同组别名的其他 8 个位置,强制进行冲突驱逐。这会在写入线程中花费额外的时间,但可以使数据更快地供其他想要读取它的线程使用。我没试过这个。
这也可能会驱逐其他行,从而消耗更多的 L3 流量 = 系统范围的共享资源,而不仅仅是在生产者线程中花费时间。你只会考虑延迟,而不是吞吐量,除非其他行是你想要写入和驱逐的行。