赫伯·萨特原子武器"Why Standalone Fences are Suboptimal"

Herb Sutter Atomic Weapons "Why Standalone Fences are Suboptimal"

本文关键字:Why Standalone are Suboptimal Fences 武器 赫伯      更新时间:2023-10-16

在他关于记忆屏障(栅栏)的演讲接近尾声时,他给出了以下例子(注意global是原子类型的而不是):

// thread 1                                     // thread 2
widget *temp = new widget();
global = temp;
                                                global->do_something();
                                                global->do_something_else();

后来他说我们应该有完整的围栏,如下所示:

   // thread 1                                     // thread 2
   widget *temp = new widget();
XX mb();   XXXXXXXXXXXXXXXXXXXXX
   global = temp;
                                                   temp2 = global;
                                                XX mb(); XXXXXXXXXXXXXXXX  
                                                   temp2->do_something();
                                                   temp2 = global;
                                                XX mb(); XXXXXXXXXXXXXXXX
                                                   temp2->do_something_else();

我想知道为什么在线程1中需要一个屏障?global依赖于temp,编译器无论如何都不会将global = temp;移动到temp的构造之上。我认为需要内存屏障的一个原因是,语句global=temp;可以在new widget()的构造过程中完成,然后线程2将看到部分构造的global。是否可能在构建新的widget的过程中安排对global的分配?或者记忆障碍是由于其他原因造成的?

同样在线程2中,您不需要temp2->do_something();之后的另一个屏障吗?与当前转换的形式一样,以下语句在执行过程中仍然可以重新排序:

                                                   temp2 = global;
                                                XX mb(); XXXXXXXXXXXXXXXX 
                                                   temp2 = global;
                                                   temp2->do_something();
                                                XX mb(); XXXXXXXXXXXXXXXX
                                                   temp2->do_something_else();

这不是您想要的,因为现在您只在一个temp2上调用成员函数,而不是在执行do_something_else()之前从global读取新值。

如果在线程2中不可能进行这样的重新排序,那么如果temp2->do_something();不能在temp2 = global;之前重新排序,为什么我们首先需要在线程2中设置屏障呢。

我想知道为什么在线程1中需要一个屏障?全局取决于温度编译器不会移动global=temp;高于无论如何,温度。

它与temp内部成员的初始化有关。由于CPU缓存和重新排序,在没有内存屏障的情况下,global可能具有新temp的地址,但temp指向的内存可能看起来未初始化。

至于第二个问题,显然

temp2->do_something();
temp2 = global;

不能作为重新排序

temp2 = global;
temp2->do_something();

因为它将对完全不同的对象执行某些操作!