为什么C ++编译器不能将此条件布尔赋值优化为无条件赋值?

时间:2016-10-28 10:29:20

标签: c++ optimization

考虑以下功能:

void func(bool& flag)
{
    if(!flag) flag=true;
}

在我看来,如果flag有一个有效的布尔值,这相当于无条件地将它设置为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的引用,只是因为代码过于特殊而不关心优化,或者是否有任何合理的理由为什么不希望这样的优化? 1}?似乎唯一可能的原因是flag可能会以某种方式具有非true - 或 - false值,而在阅读时没有未定义的行为,但我不是确定这是否可行。

3 个答案:

答案 0 :(得分:102)

由于cache coherence的考虑,这可能会对程序的性能产生负面影响。每次调用flag时写入func()都会弄脏包含的缓存行。无论写入的值与写入前在目标地址处找到的位完全匹配,都会发生这种情况。

修改

hvd提供了另一个阻止此类优化的good reason。对于提议的优化,这是一个更有说服力的论据,因为它可能导致未定义的行为,而我的(原始)答案仅解决了性能方面。

经过多一点思考后,我可以提出另一个例子,为什么编译器应该被强烈禁止 - 除非他们能够证明转换对于特定的上下文是安全的 - 从引入无条件写入。请考虑以下代码:

const bool foo = true;

int main()
{
    func(const_cast<bool&>(foo));
}

func()中无条件写入时,这肯定会触发未定义的行为(写入只读存储器会终止程序,即使写入的效果是非操作的。)

答案 1 :(得分:48)

除了莱昂关于表现的答案:

假设flagtrue。假设有两个线程不断调用func(flag)。在这种情况下,写入的函数不会将任何内容存储到flag,因此这应该是线程安全的。两个线程可以访问同一个内存,但只能读取它。无条件地将flag设置为true意味着两个不同的线程将写入同一内​​存。这是不安全的,即使写入的数据与已经存在的数据相同,这也是不安全的。

答案 2 :(得分:13)

我不确定C ++的行为,但是在C中,内存可能会发生变化,因为如果内存中包含非零的非零值,它将在检查时保持不变,但在检查时更改为1

但由于我不熟悉C ++,我不知道这种情况是否可能。