提问者:小点点

为什么C编译器不优化这个条件布尔赋值作为无条件赋值?[重复]


考虑以下函数:

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

我的问题是:仅仅是代码太特殊而不关心优化,或者有什么好的理由说明这种优化是不需要的,因为标志不是对易失性的引用?似乎唯一的原因是标志可能以某种方式具有非true-or-false值,而在读取它时没有未定义的行为,但我不确定这是否可能。


共3个答案

匿名用户

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

编辑

HVD提供了另一个阻止这种优化的好理由。这是反对提议的优化的更有说服力的论点,因为它可能导致未定义的行为,而我(最初)的回答只涉及性能方面。

经过更多的思考,我可以再举一个例子,为什么编译器应该被严格禁止——除非他们能证明转换对特定上下文是安全的——引入无条件写入。考虑这段代码:

const bool foo = true;

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

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

匿名用户

除了里昂对表演的回答:

假设标志true。假设两个线程不断调用func(标志)。在这种情况下,编写的函数不存储标志的任何内容,所以这应该是线程安全的。两个线程确实访问了同一个内存,但只是为了读取它。无条件地将标志设置为true意味着两个不同的线程将写入同一个内存。这是不安全的,即使正在写入的数据与已经存在的数据相同,这也是不安全的。

匿名用户

我不确定C在这里的行为,但在C中,内存可能会发生变化,因为如果内存包含1以外的非零值,它将通过检查保持不变,但通过检查更改为1。

但是由于我的C语言不是很流利,我不知道这种情况是否可能。

相关问题