参考Hans Boehm在“原子操作”下的一个(稍微过时)paper。它提到内存模型(当时提出)不会阻止优化编译器将同一变量的一系列加载或存储组合成单个加载。他的例子如下(更新为希望纠正当前语法):
鉴于
atomic<int> v;
代码
while( v.load( memory_order_acquire ) ) { ... }
可以优化为:
int a = v.load(memory_order_acquire);
while(a) { ... }
他表示,显然这会很糟糕。现在我的问题是,由于论文有点陈旧,当前的C ++ 0x内存模型是否会阻止这种类型的优化,还是技术上仍然允许?
我对标准的阅读似乎倾向于被禁止,但使用“获取”语义使其不那么清晰。例如,如果它是“seq_cst”,则模型似乎保证加载必须参与访问的总排序,并且只加载一次因此似乎违反排序(因为它破坏序列发生在关系之前)。
对于获取,我将29.3.2解释为意味着不能进行此优化,因为“获取”操作必须遵守任何“释放”操作。只做一次获取似乎无效。
所以我的问题是当前的模型(在待定标准中)是否会禁止这种类型的优化?如果是,那么哪一部分特别禁止呢?如果不是,是否使用volatile
原子来解决问题?
对于奖金,如果加载操作具有“放松”排序,则允许优化吗?
答案 0 :(得分:2)
C ++ 0x标准试图禁止此优化。
相关词汇来自29.3p13:
实现应该使原子存储在合理的时间内对原子载荷可见。
如果正在执行加载的线程只发出一条加载指令,那么这就违反了,就好像它第一次错过了写操作一样,它永远不会看到它。对于负载使用哪种内存排序无关紧要,memory_order_seq_cst
和memory_order_relaxed
都是相同的。
但是,允许以下优化 ,除非循环中存在强制排序的内容:
while( v.load( memory_order_acquire ) ) {
for(unsigned __temp=0;__temp<100;++__temp) {
// original loop body goes here
}
}
即。如果编译器仍然执行它们,编译器可以生成任意不经常执行实际加载的代码。除非在循环中有其他memory_order_seq_cst
操作,否则甚至允许memory_order_seq_cst
,因为这相当于在其他线程的任何内存访问之间运行100次迭代。
顺便说一下,使用memory_order_acquire
没有你描述的效果---不需要看到发布操作(除了上面引用的29.3p13之外),只是如果它 查看发布操作,然后对其他访问施加可见性限制。
答案 1 :(得分:0)
从您正在链接的论文开始:
Volatiles保证正确的内存操作数量 进行。
标准基本相同:
严格按照标准评估对易失性对象的访问 抽象机器的规则。
这一直是这种情况,因为我认为Dennis Ritchie的第一个C编译器。必须这样,因为内存映射的I / O寄存器不起作用。要从键盘读取两个字符,需要两次读取相应的内存映射寄存器。如果编译器对它必须执行的读取数有不同的想法,那就太糟糕了!