gpt4 book ai didi

multithreading - “full memory barrier”的反面是什么?

转载 作者:行者123 更新时间:2023-12-04 06:40:30 26 4
gpt4 key购买 nike

我有时会在有关内存排序的教程中看到“完全内存屏障”一词,我认为这意味着:

如果我们有以下指示:

instruction 1
full_memory_barrier
instruction 2

然后,不允许将 instruction 1重新排序为低于 full_memory_barrier,并且不允许将 instruction 2重新排序为高于 full_memory_barrier

但是,完全内存屏障的反义词是什么呢?我的意思是说有像“半内存屏障”之类的东西只能阻止CPU在一个方向上对指令重新排序吗?

如果存在这样的内存障碍,我看不到它的意义,我的意思是如果我们有以下说明:
instruction 1
memory_barrier_below_to_above
instruction 2

假设 memory_barrier_below_to_above是一个内存屏障,可以防止 instruction 2重新排序为上面的 memory_barrier_below_to_above,因此将不允许以下操作:
instruction 2
instruction 1
memory_barrier_below_to_above

但是将允许以下操作(这使这种类型的内存屏障毫无意义):
memory_barrier_below_to_above
instruction 2
instruction 1

最佳答案

http://preshing.com/20120710/memory-barriers-are-like-source-control-operations/解释了不同种类的障碍,例如LoadLoad或StoreStore。 StoreStore障碍仅阻止商店跨障碍重新排序,但负载仍会无序执行。

在实际的CPU上,任何包含StoreLoad的屏障也会阻塞其他所有事物,因此被称为“完全屏障”。 StoreLoad是最昂贵的一种,因为它意味着先清空存储缓冲区,然后才能从L1d缓存中读取以后的加载。

障碍示例:

           strong               weak
x86 mfence none needed unless you're using NT stores
ARM dmb sy isb, dmb st, dmb ish, etc.
POWER hwsync lwsync, isync, ...

ARM具有“内部”域和“外部可共享域”。我真的不知道这意味着什么,不必处理它,但是 this page记录了可用的不同形式的数据内存屏障。 dmb st只等待较早的存储完成,因此我认为这只是一个StoreStore障碍,因此对于C++ 11发行存储来说太弱了,C++ 11发行存储也需要对较早的加载进行排序以防止LoadStore重新排序。另请参见 C/C++11 mappings to processors:请注意,可以在每个商店周围使用完全屏障,或者在加载之前和商店之前使用屏障来实现seq-cst。不过,使负载便宜通常是最好的。

ARM ISB刷新指令缓存。 (ARM没有相干的i缓存,因此在将代码写入内存之后,需要一个ISB才能可靠地跳转到那里并将这些字节作为指令执行。)

POWER有很多可用的障碍,包括上面链接的Jeff Preshing文章中提到的轻重量(非全障碍)和重同步(全障碍)。

一个单向障碍是您从发行商店或获得负载的中获得的。关键部分末尾的发布存储(例如,解锁自旋锁)必须确保关键部分内部的加载/存储不会稍后出现,但是不必将后面的加载延迟到 lock=0变为全局可见。

Jeff Preshing也有关于此的文章: Acquire and Release semantics

“完全”与“部分”屏障术语通常不用于发布存储或获取负载的单向重新排序限制。实际的发布围栏(在C++ 11中为 std::atomic_thread_fence(std::memory_order_release))确实阻止双向存储的重新排序,这与特定对象上的发布存储不同。

这种微妙的区别在过去(甚至在专家之间!)也引起了困惑。 Jeff Preshing还有另一篇出色的文章对此进行了解释: Acquire and Release Fences Don't Work the Way You'd Expect

没错,与商店或 cargo 无关的单向障碍不是很有用;这就是为什么这种事不存在的原因。 :P可以在一个方向上重新排序无穷远的距离,并使所有操作相互重新排序。

atomic_thread_fence(memory_order_release)到底是做什么的?

C11( n1570 Section 7.17.4 Fences)仅当在原子存储(松弛的或其他方式)对负载访问的同一对象使用释放栅栏时,才通过与获取负载或获取围栏创建同步关系来定义它。 (C++ 11具有基本相同的定义,但是在注释中与@EOF的讨论提出了C11版本。)

这个关于净效果的定义,而不是实现效果的机制,并不能直接告诉我们它允许或不允许做什么。例如,第3小节说

3) A release fence A synchronizes with an atomic operation B that performs an acquire operation on an atomic object M if there exists an atomic operation X such that A is sequenced before X, X modifies M, and B reads the value written by X or a value written by any side effect in the hypothetical release sequence X would head if it were a release operation



因此,在编写线程中,它是在谈论这样的代码:
stuff           // including any non-atomic loads/stores

atomic_thread_fence(mo_release) // A
M=X // X
// threads that see load(M, acquire) == X also see stuff

syncs-with表示从 M=X中看到值的线程(直接或间接通过释放序列)也看到了所有 stuff,并在没有Data Race UB的情况下读取了非原子变量。

这可以让我们说出允许/禁止的事情:

这是原子存储的两向障碍。它们无法在任何方向交叉,因此屏障在此线程的内存顺序中的位置受原子存储前后的限制。任何较早的存储区都可以是 stuffM的一部分,任何较后的存储区都可以是与获取负载(或负载+获取栅栏)同步的 M

这是原子负载的单向屏障:较早的屏障需要保留在屏障之前,但较晚的可以保留在屏障之上。 M=X只能是商店(或RMW的商店部分)。

这是非原子负载/存储的单向屏障:非原子存储可以是 stuff的一部分,但不能是 X,因为它们不是原子的。可以允许稍后在此线程中进行的加载/存储在 M=X之前出现在其他线程中。 (如果在障碍之前和之后修改了非原子变量,则即使在与该障碍进行同步之后,也没有任何东西可以安全地读取它,除非读者还有一种方法可以阻止该线程继续运行并创建Data Race UB因此,编译器可以并且应该将 foo=1; fence(release); foo=2;重新排序为 foo=2; fence(release);,从而消除了已死的 foo=1存储区。但是将 foo=1下沉到障碍之后,仅在技术上是合法的,如果没有UB,什么都说不清。

作为一个实现细节,C11发行围栏可能比(例如,用于更多种编译时重新排序的2向屏障)强,但并不弱。在某些体系结构(如ARM)上,唯一足够强大的选择可能是完全障碍的asm指令。对于编译时重新排序的限制,编译器可能只是为了保持实现的简单性而不允许这些1向重新排序。

通常,这种组合的2向/1向特性仅对compile-time reordering 很重要。 CPU不会在原子存储与非原子存储之间进行区分。非原子的总是与宽松原子的asm指令相同(对于适合单个寄存器的对象)。

使内核等待直到全局可见的较早操作的CPU屏障指令通常是2路屏障。它们是根据在所有内核共享的内存的一致 View 中全局可见的操作来指定的,而不是C/C++ 11创建同步关系的样式。 (请注意,操作可能对其他线程全局可见,然后才对所有线程全局可见: Will two atomic writes to different locations in different threads always be seen in the same order by other threads?
但是,只要在物理核心内避免重新排序的障碍,就可以恢复顺序一致性。)

C++ 11发行版围栏需要LoadStore + StoreStore障碍,但不需要LoadLoad。一个可以让您仅获得那2个但并非全部3个“便宜”障碍的CPU,将使整个障碍指令中的负载在一个方向上重新排序,同时在两个方向上阻止存储。

弱排序的SPARC实际上就是这样,并使用LoadStore等术语(这是Jeff Preshing在其文章中使用的术语)。 http://blog.forecode.com/2010/01/29/barriers-to-understanding-memory-barriers/显示了它们的用法。 (最近的SPARC使用TSO(总存储顺序)内存模型。我认为这就像x86,其中硬件给人以程序顺序发生内存操作错觉的感觉,除了StoreLoad重新排序。)

关于multithreading - “full memory barrier”的反面是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51013872/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com