假设我有这样一个结构:
volatile struct { int foo; int bar; } data;
data.foo = 1;
data.bar = 2;
data.foo = 3;
data.bar = 4;所有的作业都保证不会被重新排序吗?
例如,如果没有易失性,编译器显然可以将其优化为两个指令,顺序不同,如下所示:
data.bar = 4;
data.foo = 3;但是对于易失性,编译器不需要这样做吗?
data.foo = 1;
data.foo = 3;
data.bar = 2;
data.bar = 4;(例如,将成员视为独立的不相关的易失性实体-并进行重新排序,我可以想象,如果foo和bar处于页面边界,它可能试图提高引用的局部性。)
另外,对于当前版本的C和C++标准,答案是否一致?
发布于 2020-12-14 20:30:39
C
他们不会被重新排序。
C17 6.5.2.3(3)说:
后面的后缀表达式。运算符和标识符指定结构或联合对象的成员。值是命名成员的值,97),如果第一个表达式是lvalue,则为lvalue。如果第一个表达式具有限定类型,则结果具有指定成员类型的如此限定版本。
由于data具有volatile-qualified类型,data.bar和data.foo也是如此。因此,您将对volatile int对象执行两次赋值。在6.7.3脚注136中,
对被声明为
volatile的对象的操作不应由实现“优化”或重新排序,除非表达式的计算规则允许。
一个更微妙的问题是,编译器是否可以用一条指令来分配它们,例如,如果它们是相邻的32位值,那么它是否可以使用64位存储来设置这两者?我不这么认为,至少GCC和Clang没有尝试过。
发布于 2020-12-15 09:31:12
如果您想在多个线程中使用这一点,那么有一个重要的问题。
虽然编译器不会重新排序对volatile变量的写入(如Nate Eldredge的回答中所描述的那样),但还有一点可能发生写入重新排序,那就是CPU本身。这取决于CPU体系结构,下面的几个例子如下:
英特尔64
见Intel 64架构内存排序白皮书。
虽然商店指令本身没有重新排序(2.2):
它们可能以不同的顺序在不同的CPU中可见(2.4):
Intel 64内存排序允许这两个处理器以不同的顺序查看两个处理器的存储。
AMD 64
AMD64(这是常见的x64)在规格中有类似的行为。
一般情况下,不允许乱写.按照顺序执行的写指令不能将其结果提交(写)到内存,直到所有以前的指令按程序顺序完成为止。然而,处理器可以在私有缓冲区(软件不可见)中保存无序写入指令的结果,直到该结果被提交到内存中。
PowerPC
我记得在使用PowerPC处理器的Xbox 360上必须小心
虽然Xbox 360 CPU不重新排序指令,但它确实重新安排了写入操作,这些操作在指令本身之后完成。PowerPC内存模型特别允许这种写入的重新排列。
为了避免以可移植的方式重新排序,您需要使用记忆栅栏,如C++11、篱笆或C11 篱笆。没有它们,从另一个线程中看到的写入顺序可能是不同的。
维基百科https://en.wikipedia.org/wiki/Memory_barrier#Out-of-order_execution_versus_compiler_reordering_optimizations的文章中也提到了这一点:
此外,由于缓存、缓存一致性协议和轻松的内存排序,其他处理器或内核也不能保证易失性读写的顺序相同,这意味着仅仅易失性变量甚至不能作为线程间标志或互斥变量工作。
https://stackoverflow.com/questions/65295907
复制相似问题