英特尔指令重新排序
Instruction reordering on intel
我试图通过以下简单示例来理解指令重新排序:
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,发现如下:
Writes to memory are not reordered with other writes, with the
following exceptions:
— streaming stores (writes) executed with the
non-temporal move instructions (MOVNTI, MOVNTQ, MOVNTDQ, MOVNTPS, and
MOVNTPD); and
— string operations (see Section 8.2.4.1).
这里没有字符串操作,我也没有注意到NT
移动指令。那么...为什么可以重新排序写入?
或者
中的内存是否重要
Writes to memory are not reordered
?因此,当我们有 a
和 b
缓存时,写入不是写入主内存,而是写入缓存。
如果一个线程是 运行 foo
而另一个线程是 运行 bar
那么你的程序的行为将是 undefined.
您不能同时读取和写入非原子变量,例如 int
。
因此在这种情况下指令重新排序是允许的。
你的前提是错误的。只有编译时重新排序才能在 x861.
上破坏此示例
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
访问重新排序。
我试图通过以下简单示例来理解指令重新排序:
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,发现如下:
Writes to memory are not reordered with other writes, with the following exceptions:
— streaming stores (writes) executed with the non-temporal move instructions (MOVNTI, MOVNTQ, MOVNTDQ, MOVNTPS, and MOVNTPD); and
— string operations (see Section 8.2.4.1).
这里没有字符串操作,我也没有注意到NT
移动指令。那么...为什么可以重新排序写入?
或者
中的内存是否重要Writes to memory are not reordered
?因此,当我们有 a
和 b
缓存时,写入不是写入主内存,而是写入缓存。
如果一个线程是 运行 foo
而另一个线程是 运行 bar
那么你的程序的行为将是 undefined.
您不能同时读取和写入非原子变量,例如 int
。
因此在这种情况下指令重新排序是允许的。
你的前提是错误的。只有编译时重新排序才能在 x861.
上破坏此示例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
访问重新排序。