多个 If 语句与使用逻辑运算符评估条件的单个语句

Multiple If statements versus single statement with conditions evaluated using logical operators

我正在编写一个函数,在实际执行任务之前检查几个条件。这是通过许多 if 语句完成的。像这样:

bool foo()
{
    if(invalid())
        return false;
    if(dont_execute())
        return false;
    // .. etc
    // Actual execution here
    return true;
}

在此函数中,将多个条件更改为有什么好处:

bool foo()
{
    if(invalid() || dont_execute() /* || .. etc */)
        return false;
    // Actual execution here
    return true;
}

感觉第一种风格可读性更好。我想知道的是,使用多个 if 语句而不是使用逻辑运算符组合是否会对性能产生任何影响。

使用测试用例

bool invalid();

bool dont_execute();

void execute();

bool foo()
{
    if(invalid())
        return false;
    if(dont_execute())
        return false;
    execute();
    return true;
}

bool foo2()
{
    if(invalid() || dont_execute() /* || .. etc */)
        return false;
    execute();
    return true;
}

您可以看到 foofoo2 都被 GCC 9.2 和 Clang 9 编译成完全相同的程序集,带有 -O2 优化标志,参见 godbolt .例如 GCC 的输出是

foo():
        sub     rsp, 8
        call    invalid()
        test    al, al
        je      .L2
.L4:
        xor     eax, eax
        add     rsp, 8
        ret
.L2:
        call    dont_execute()
        test    al, al
        jne     .L4
        call    execute()
        mov     eax, 1
        add     rsp, 8
        ret
foo2():
        sub     rsp, 8
        call    invalid()
        test    al, al
        je      .L8
.L10:
        xor     eax, eax
        add     rsp, 8
        ret
.L8:
        call    dont_execute()
        test    al, al
        jne     .L10
        call    execute()
        mov     eax, 1
        add     rsp, 8
        ret

虽然这并不意味着永远不会有区别,至少编译器认为他们不需要在这两种情况下做任何不同的事情,即使他们不知道调用的函数是做什么的。

所以我建议您不要担心性能,而是选择您认为更具可读性的内容。

不,没有性能影响。如果我们 compare the assembly 这两个函数,我们可以看到这两个函数是相同的。

示例:

bool f1();
bool f2();

bool combined()
{
    if (f1() || f2())
        return false;

    return true;
}

bool separate()
{
    if (f1())
        return false;

    if (f2())
        return false;

    return true;
}

这里是程序集:

combined():
        sub     rsp, 8
        call    f1()
        mov     r8d, eax
        xor     eax, eax
        test    r8b, r8b
        jne     .L1
        call    f2()
        xor     eax, 1
.L1:
        add     rsp, 8
        ret

separate():
        sub     rsp, 8
        call    f1()
        mov     r8d, eax
        xor     eax, eax
        test    r8b, r8b
        jne     .L7
        call    f2()
        xor     eax, 1
.L7:
        add     rsp, 8
        ret