英特尔上的指令重新排序

时间:2018-08-08 11:42:10

标签: c assembly x86 memory-barriers

我试图通过以下简单示例了解指令的重新排序:

int a;
int b;

void foo(){
   a = 1;
   b = 1;
}

void bar(){
   while(b == 0) continue;
   assert(a == 1);
}

众所周知,在此示例中,如果一个线程执行foo,而另一个线程执行bar,则断言可能会失败。但是我不明白为什么。我咨询了Intel manual Vol. 3A, 8.2.2,发现了以下内容:

  

对存储器的写入不会与其他写入一起重新排序,因为   下列例外:

     

-使用以下命令执行的流式存储(写入)   非临时移动指令(MOVNTI,MOVNTQ,MOVNTDQ,MOVNTPS和   MOVNTPD);和

     

-字符串操作(请参见第8.2.4.1节)。

这里没有字符串操作,也没有注意到NT移动指令。那么...为什么可以对写进行重新排序?

还是记忆很重要

  

内存 的写入没有重新排序

?因此,当我们缓存了ab时,写到主存储器中的不是写到主存储器,而是写到了主存储器。

2 个答案:

答案 0 :(得分:4)

如果一个线程正在运行foo,而另一个线程正在运行bar,则程序的行为将是未定义

不允许您同时对非原子变量(例如int)进行读写。

因此在这种情况下可以进行指令排序。

答案 1 :(得分:3)

您的前提是错误的。只有编译时重新排序才能在x86 1 上破坏此示例。

x86 asm存储是发行存储。它们只能按程序顺序从存储缓冲区提交到L1d缓存。

在看到a之后,

b=1不能仍然处于共享状态;这意味着运行foo的线程让其存储顺序混乱。这就是对内存的写入不会与其他写入操作一起重新排序,意味着会将其存储到可缓存的内存中。

如果再次由运行foo的线程的RFO使它处于共享状态 ,则它将具有a的更新值。


脚注1.当然,自旋循环将优化为if (b==0) infinite_loop,因为数据争用UB使编译器可以提升负载。参见MCU programming - C++ O2 optimization breaks while loop

您似乎在询问C规则,同时假设代码会天真地/直接转换为x86 asm。您可以使用轻松的原子来实现,但不能使用volatile来实现,因为volatile访问不能与其他volatile访问重新排序(在编译时)。