4

我有一个“MyFunction”,我一直在纠结是否应该在上面使用 goto 以及在类似(希望很少见)的情况下。所以我正在努力为这种情况建立一个硬性的习惯。做或不做。

int MyFunction()
{   if (likely_condition)
    {
    condition_met:
        // ...
        return result;
    }
    else /*unlikely failure*/
    {   // meet condition
        goto condition_met;
    }
}

我打算将失败的条件跳转指令的好处用于可能的情况。但是,如果没有这样的事情,我看不出编译器如何知道要简化哪个案例概率。

  1. 它工作正常吗?
  2. 这些好处值得混淆吗?
  3. 是否有更好的(不那么冗长、更结构化、更具表现力)的方法来实现这种优化?
4

4 回答 4

5

在我看来,您尝试做的优化大多已经过时了。大多数现代处理器都内置了分支预测,因此(假设它已经足够注意到)它们跟踪分支被采用的频率,并根据其过去的被采用模式预测分支是否可能被采用. 在这种情况下,速度主要取决于预测的准确程度,而不是预测是接受还是不接受。

因此,您最好使用更简单的代码:

int MyFunction() {   
    if (!likely_condition) {
        meet_condition();
    }
    // ...
    return result;
}
于 2012-10-31T18:22:13.270 回答
4
  1. 看起来只要condition_met:不跳过变量初始化,代码就应该像您期望的那样工作。

  2. 不,你甚至不知道混淆版本会编译成更优化的代码。编译器优化(和处理器分支预测)最近变得非常聪明。

3.

int MyFunction()
{
    if (!likely_condition)
    {
        // meet condition
    }

    condition_met:
        // ...
    return result;
}

或者,如果它有助于您的编译器(检查程序集)

int MyFunction()
{
    if (likely_condition); else
    {
        // meet condition
    }

    condition_met:
        // ...
    return result;
}
于 2012-10-31T18:20:12.423 回答
4

如果现代 CPU 做出正确的分支预测,它将以相同的性能采用该分支。因此,如果这是在一个内部循环中,那么 的性能if (unlikely) { meet condition } common code;将与您所写的相匹配。

此外,如果您在两个分支中拼出公共代码,编译器将生成与您编写的代码相同的代码:将为if子句发出常见情况,而else子句将jmp发送给公共代码。你总是会在更简单的终端案例中看到这一点,比如*out = whatever; return result;. 调试时很难分辨出return你在看哪个,因为它们都被合并了。

于 2012-10-31T18:21:54.920 回答
1

我强烈建议__builtin_expect()对您的特定 C++ 编译器使用宏 (GCC) 或类似的东西(请参阅便携式分支预测提示),而不是使用goto

int MyFunction()
{   if (__builtin_expect(likely_condition))
    {
        // ...
        return result;
    }
    else /*unlikely failure*/
    {   // meet condition
    }
}

正如其他人也提到goto的那样,它容易出错,而且从骨子里是邪恶的。

于 2012-10-31T18:45:26.187 回答