Multithreading “a”的反面是什么;“完整内存屏障”;?
我有时会在关于内存排序的教程中看到“完整内存屏障”一词,我认为它的意思如下: 如果我们有以下说明:Multithreading “a”的反面是什么;“完整内存屏障”;?,multithreading,assembly,memory-barriers,Multithreading,Assembly,Memory Barriers,我有时会在关于内存排序的教程中看到“完整内存屏障”一词,我认为它的意思如下: 如果我们有以下说明: instruction 1 full_memory_barrier instruction 2 instruction 1 memory_barrier_below_to_above instruction 2 然后,指令1不允许重新排序到低于满内存屏障,指令2不允许重新排序到高于满内存屏障 但是什么是完全内存屏障的反面,我的意思是,是否有类似于“半内存屏障”的东西,只阻止CPU在一个方向上
instruction 1
full_memory_barrier
instruction 2
instruction 1
memory_barrier_below_to_above
instruction 2
然后,指令1
不允许重新排序到低于满内存屏障
,指令2
不允许重新排序到高于满内存屏障
但是什么是完全内存屏障的反面,我的意思是,是否有类似于“半内存屏障”的东西,只阻止CPU在一个方向上重新排列指令 如果存在这样的记忆障碍,我看不出它的意义,我的意思是如果我们有以下说明:
instruction 1
full_memory_barrier
instruction 2
instruction 1
memory_barrier_below_to_above
instruction 2
假设下面的内存\u屏障\u到上面的是一个内存屏障,它阻止指令2
被重新排序到上面的下面的内存\u屏障\u到上面的,因此不允许出现以下情况:
instruction 2
instruction 1
memory_barrier_below_to_above
但以下情况是允许的(这使得这种类型的内存屏障毫无意义):
解释不同类型的障碍,如Load或Store。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有“内部”和“外部可共享域”。我真的不知道这意味着什么,也没有必要处理它,但记录了可用的不同形式的数据存储屏障dmb st
只等待较早的存储完成,所以我认为这只是一个StoreStore障碍,因此对于C++11发行版存储来说太弱了,它还需要根据LoadStore的重新排序来订购较早的加载。另请参见:请注意,可以在每个仓库周围设置完整的屏障,或在装载之前以及仓库之前设置屏障,以实现seq cst。不过,让货物便宜通常是最好的
ARM ISB刷新指令缓存。(ARM没有连贯的i-cache,所以在将代码写入内存后,您需要一个ISB,然后才能可靠地跳转到内存并将这些字节作为指令执行。)
POWER有很多可用的屏障,包括Jeff Preshing在上面链接的文章中提到的轻型(非全屏障)和重型同步(全屏障)
单向屏障是您从发布存储或获取负载中获得的。临界段末端的释放存储(例如解锁自旋锁)必须确保临界段内的加载/存储不会在以后出现,但不必延迟以后的加载,直到lock=0
变得全局可见
Jeff Preshing也有一篇关于这方面的文章:
“完整”与“部分”屏障术语通常不用于发布存储或获取加载的单向重新排序限制。与特定对象上的发布存储不同,实际的发布隔离(在C++11中,std::atomic_thread_fence(std::memory_order_release)
)会阻止存储在两个方向上的重新排序
这种微妙的区别在过去曾引起过混乱(甚至在专家中也是如此!)。杰夫·普雷辛(Jeff Preshing)还有一篇优秀的文章对此进行了解释:
你是对的,一个没有绑在商店或货物上的单向屏障不会很有用;这就是为什么这样的东西不存在的原因P它可以在一个方向上对无限距离进行重新排序,并将所有操作留给彼此重新排序
原子线程围栏(内存\u顺序\u释放)
具体做什么?
C11()仅根据创建与获取加载或获取围栏的同步关系来定义它,当释放围栏在加载访问的同一对象的原子存储(放松或其他)之前使用时。(C++11的定义基本相同,但在注释中与@EOF的讨论提出了C11版本。)
这个净效应的定义,而不是实现它的机制,并没有直接告诉我们它允许或不允许什么。例如,第3小节说
3) 释放栅栏A与执行获取的原子操作B同步
如果存在原子操作X,则原子对象M上的操作
在X之前排序,X修改M,B读取X写入的值或写入的值
假想的释放序列中的任何副作用,如果是释放,X都会领先
操作
因此,在编写线程中,它讨论的代码如下:
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
与同步意味着线程可以看到M=X
(直接或间接通过发布序列)中的值,也可以看到所有内容
,并读取非原子变量,而不需要数据
这让我们谈谈什么是/不允许的:
这是原子存储的双向屏障。它们在两个方向上都不能穿过它,因此该障碍在该线程内存顺序中的位置受前后原子存储的限制。对于某些M
,任何较早的存储都可以是stuff
的一部分,任何较晚的存储都可以是采集加载(或加载+采集围栏)与之同步的M
这是原子负载的单向屏障:早期的需要停留在屏障之前,但后期的可以移动到屏障之上M=X
只能是门店(或RMW的门店部分)
这是非原子加载/存储的单向屏障:非原子存储可以是东西的一部分,但不能是X
,因为它们不是原子的。允许此线程中的后续加载/存储在M=X之前显示给其他线程是可以的<