为什么C ++编译器不能将这个条件布尔赋值作为无条件赋值来优化?
考虑以下function:
void func(bool& flag) { if(!flag) flag=true; }
在我看来,如果标志有一个有效的布尔值,这将等于无条件设置为true
,如下所示:
void func(bool& flag) { flag=true; }
然而gcc和clang都没有这样优化它们 – 都在-O3
优化级别生成以下代码:
_Z4funcRb: .LFB0: .cfi_startproc cmp BYTE PTR [rdi], 0 jne .L1 mov BYTE PTR [rdi], 1 .L1: rep ret
我的问题是:这只是代码是太特殊的情况下,照顾优化,或者有什么好的理由,为什么这样的优化将是不受欢迎的,因为该flag
不是一个参考volatile
? 这似乎是唯一可能的原因是flag
在某种程度上可能具有非true
false
价值,而在阅读时却没有不确定的行为,但我不确定这是否可能。
由于caching一致性考虑,这可能会对程序的性能产生负面影响。 每次func()
时写入flag
都会使包含的caching线变脏。 无论写入的值是否与写入前的目标地址的位完全匹配,都会发生这种情况。
编辑
hvd提供了另外一个很好的理由来阻止这样的优化。 对于所提出的优化来说,这是一个更有说服力的论点,因为它可能导致不确定的行为,而我的(原始的)答案只涉及性能方面。
经过多一点思考,我可以再提出一个例子,为什么要强制禁止编纂者 – 除非他们能够certificate这种转变对某一特定的情况是安全的 – 而不是引入无条件的写作。 考虑这个代码:
const bool foo = true; int main() { func(const_cast<bool&>(foo)); }
如果在func()
无条件地写入,这肯定会触发未定义的行为(写入只读内存将会终止程序,即使写入的效果是不可操作的)。
除了Leon的performance之外,
假设flag
是true
。 假设两个线程一直在调用func(flag)
。 在这种情况下写入的函数不会存储任何flag
,所以这应该是线程安全的。 两个线程访问相同的内存,但只能读取它。 无条件设置flag
为true
意味着两个不同的线程将写入相同的内存。 这是不安全的,即使正在写入的数据与已经存在的数据相同,这也是不安全的。
我不确定C ++在这里的行为,但是在C中,内存可能会改变,因为如果内存中包含1以外的非零值,那么检查将保持不变,但是检查将更改为1。
但是因为我不太stream利的C ++,我不知道这种情况是否可能。