我正在阅读Paul E. McKenney的记忆障碍 http://www.rdrop.com/users/paulmck/scalability/paper/whymb.2010.07.23a.pdf 所有内容都会详细解释,当我发现一切都很清楚时我遇到一句话,它使一切都变得愚蠢,让我觉得我什么都不懂.让我举个例子
void foo(void)
{
a = 1; #1
b = 1; #2
}
void bar(void)
{
while (b == 0) continue; #3
assert(a == 1); #4
}
Run Code Online (Sandbox Code Playgroud)
假设这两个函数在不同的处理器上运行.现在可能发生的是第二个处理器在存储到b#2之后可以看到存储到#1,因为第一个处理器队列存储到"a"并继续存储b指令.好的,没关系,我们在#1和#2之间的行中添加一个写入栅栏,但是这段代码仍然可能失败,因为第二个处理器可能会对invalidate消息进行排队,所以我们再添加一个内存栅栏(这次读取栅栏) #4和#4之间的界限.
void foo(void)
{
a = 1; #1
write_memory_barrier();
b = 1; #2
}
void bar(void)
{
while (b == 0) continue; #3
read_memory_barrier();
assert(a == 1); #4
}
Run Code Online (Sandbox Code Playgroud)
这强制第二个处理器处理所有排队的消息(使a无效)并通过向#4上的第一个处理器发送读MESI消息再次读取它.好.接下来文章说
因此,许多CPU架构提供较弱的内存屏障指令,这些指令仅执行这两者中的一个或另一个.粗略地说,"读取内存屏障"仅标记无效队列,"写入内存屏障"仅标记存储缓冲区.虽然完整的记忆障碍同时做到了.
很好,很清楚,但在那之后我看到了这一点
这样做的结果是读取存储器屏障仅在执行它的CPU上加载,因此在读取存储器屏障之后的任何加载之前,读取存储器屏障之前的所有加载似乎都已完成.类似地,写入存储器屏障仅在执行它的CPU上再次存储,并且再次使得写入存储器屏障之前的所有存储看起来在写入存储器屏障之后的任何存储之前已经完成.
所以
读取存储器屏障之前的所有负载似乎在读取存储器屏障之后的任何负载之前已完成
混合了以前解释过的所有内容.这是什么意思?功能"bar"中的哪个负载必须在加载"a"#4之前完成?我理解断言在这个函数中没有内存障碍就会失败,因为处理器可能会读取一个旧值,因为它仍然无法使对象"a"所在的高速缓存行无效.
细节上的解释会非常有帮助,我试着整天都在理解它.
首先十分感谢.