我有一个具有2个线程的应用程序,线程A与内核1的亲缘关系和线程B与内核2的亲和力, 核心1和核心2位于同一x86插槽中。
线程A忙于整数x的旋转,线程B在某些情况下会增加x,当线程B决定增加x时,它将使x所在的缓存行无效,并且根据x86 MESI协议,它将存储新的x在core2收到无效ack之前存储缓冲区,然后在core2收到无效ack之后,core2刷新存储缓冲区。
我想知道,在core2收到无效ack之后,core2是否立即刷新存储缓冲区?我是否有可能迫使cpu用C语言执行刷新存储缓冲区?因为在我的情况下,core1旋转x的线程A应该尽早获得x新值。
答案 0 :(得分:3)
您需要使用原子。
如果您确实愿意,可以使用atomic_thread_fence
(这个问题有点像XY问题),但是最好使x
原子化并使用atomic_store
和atomic_load
,或者类似atomic_compare_exchange_weak
之类的东西。
答案 1 :(得分:3)
内核始终尝试将其存储缓冲区尽快提交给L1d缓存(并因此成为全局可见的),以便为更多存储腾出空间。
您可以使用屏障(例如atomic_thread_fence(memory_order_seq_cst
)使线程 wait 使其线程在所有存储或存储之前变得全局可见,但这可以通过阻塞此核心来实现,而不是通过加快刷新存储缓冲区的速度。
很显然,为了避免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操作,而不需要其他线程以只读方式访问它,则不需要整个RMW操作都是原子操作。
在另一个内核可以读取您写入的数据之前,它必须先从将其写到L3高速缓存的内核的L1d中的“已修改”状态开始,再从那里到达读取器内核的L1d。
您也许可以加快这部分的进度,这是在数据离开存储缓冲区之后发生的。但是您没有什么可以做的有用的事情。您不想使用clflush
,它会完全回写+逐出高速缓存行,因此,如果另一个内核在此过程中未尝试读取它,则必须从DRAM中获取它。 (甚至有可能)。
Ice Lake将具有clwb
,它将保留缓存的数据并强制写回DRAM。但这又迫使数据实际上进入了DRAM。
Tremont (successor to Goldmont Plus,原子/银峰系列)具有_mm_cldemote
(cldemote
)。就像SW预取相反。这是一个可选的性能提示,用于将高速缓存行写到L3,但不会强制将其移至DRAM或其他任何内容。
除此之外,也许还有一些技巧,例如写入其他8个在L2和L1d缓存中具有相同集合的位置的别名,从而强制驱逐冲突。这会在写线程中花费额外的时间,但可能会使数据更快地提供给其他想要读取它的线程。