我正在读 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
}
假设这两个函数在不同的处理器上运行。
现在可能发生的情况是,在第二个处理器存储到 b #2 之后,可以看到存储到 a #1,因为第一个处理器排队存储到“a”并继续存储 b 指令。好的,没关系,我们在 #1 和 #2 之间的行中添加一个写栅栏,但是此代码仍然可能会失败,因为第二个处理器可能会将无效消息排队,因此我们在中再添加一个内存栅栏(这次是读栅栏) #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
}
这强制第二个处理器处理所有排队的消息(使 a 无效),并通过将读取的 MESI 消息发送到 #4 上的第一个处理器来再次读取它。好的。接下来文章说
因此许多CPU架构
提供较弱的记忆屏障
只执行一项或一项的指令
这两个中的另一个。大致说来,
“读内存屏障”仅标记
无效队列和“写内存
“barrier”仅标记存储缓冲区。
而一个成熟的记忆障碍
两者都做。
太好了,很清楚,但之后我看到了这个
这样做的效果是读取
内存屏障命令仅加载
执行它的CPU,所以所有
读取内存之前的加载
屏障似乎已经完成
在读取之后的任何负载之前
记忆障碍。同样,写一个
内存屏障仅命令存储,
再次在执行它的CPU上,并且
再次使之前的所有商店
写内存屏障会出现
在任何商店之前完成
遵循写内存屏障。
so
读内存屏障之前的所有加载将
似乎在任何负载之前完成
读内存屏障
这混淆了之前解释的所有内容。这是什么意思?函数“bar”中的哪个加载必须在加载“a”#4 之前完成?我知道断言可能会在这个函数中没有内存屏障的情况下失败,只是因为处理器可能读取旧值,因为它仍然无法使其对象“a”所在的缓存行无效。
详细的解释真的很有帮助,我一整天都在努力理解它。
首先十分感谢。
这是什么意思?
这意味着如果您有:
read
read
read
READ BARRIER
read
read
read
然后读取屏障充当“连接点”,将这些读取分为两批。读屏障之前的所有读取都将在读屏障之后的任何读取开始之前完成。
哪个加载在bar()
必须在加载之前完成a
(#4) 开始了吗?
所有阅读的b
(#3) 被迫先于任何读取a
(#4)。这意味着a
直到之后才阅读b
不再是0。因为foo()
使用写屏障来确保a
已经改为1
(#1) 到那时b
已更改(#2)。因此,这两个屏障共同作用以确保断言语句始终成功。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)