我正在尝试通过以下简单的示例来理解指令重排:
int a;
int b;
void foo(){
a = 1;
b = 1;
}
void bar(){
while(b == 0) continue;
assert(a == 1);
}
在这个例子中,如果一个线程执行
foo
,另一个线程执行bar
,断言可能会失败。但我不明白为什么会这样。我查阅了Intel手册Vol. 3A, 8.2.2,发现如下内容:
这里没有字符串操作,也没有发现对内存的写入操作不会与其他写入操作重排序,但以下情况除外:
—使用非临时移动指令(MOVNTI、MOVNTQ、MOVNTDQ、MOVNTPS和MOVNTPD)执行的流式存储(写入);以及
—字符串操作(参见第8.2.4.1节)。
NT
的移动指令。那么...为什么会出现写入重排序?还是说,
因此,当我们将对内存的写入操作不会被重排序?
a
和b
缓存起来,并且写入操作并不是针对主存而是针对缓存时,它们可以重排序。
a
和b
会被存储在内存中?在C++语言中并没有这样的要求,在极端情况下,编译器可能会生成仅将这两个变量存储在寄存器中的代码(例如)。 - Ped7gwhile(b == 0){}
成为无限循环。 - Peter Cordesfoo
所做的写入顺序不同。然而,在我引用的手册中明确指出,对内存的写入不能与其他写入重新排序。 - St.Antariob!=0
将确保您也看到a==1
,因为mov [b], 1
是释放存储(与除NT之外的所有x86存储一样),因为x86要求存储以程序顺序从存储缓冲区提交到 L1d。(因此不允许重新排序。只有在编译时重新排序才会在x86上破坏此示例)。但是,如果编译了此无效的C代码,则情况并非如此。您是否声称您实际上重现了所述的重新排序?如果是这样,请发布一个[mcve]。 - Peter Cordes