C++ 中的多个返回语句和性能

Fra*_*ank 5 c++ performance

就性能而言,是否有任何理由支持函数中的单个返回语句而不是多个返回语句?

考虑下面的简单例子。

int min(int a, int b)
{
    if (a < b)
        return a;

    return b;
}

int min2(int a, int b)
{
    int result;

    if (a < b)
        result = a;
    else
        result = b;

    return result;
}

enum class Fruit { Apple, Orange, Banana };
std::string fruitToString(Fruit f)
{
    switch (f)
    {
        case Fruit::Apple:
            return "Apple";
        case Fruit::Orange:
            return "Orange";
        case Fruit::Banana:
            return "Banana";
        default:
            return "Unknown Fruit";
    }
}

std::string fruitToString2(Fruit f)
{
    std::string result;

    switch (f)
    {
        case Fruit::Apple:
            result = "Apple";
            break;
        case Fruit::Orange:
            result = "Orange";
            break;
        case Fruit::Banana:
            result = "Banana";
            break;
        default:
            result = "Unknown Fruit";
    }

    return result;
}
Run Code Online (Sandbox Code Playgroud)

请注意,我意识到意见在风格方面存在分歧,因此我希望答案集中在性能方面。

Chr*_*phe 4

回还是不回,这是一个问题。是吗 ?

对于第一个片段,优化器很可能为两种替代方案生成相同的代码。以 GCC 6.2 为例(参见在线编译器):

    cmp     edi, esi
    mov     eax, esi
    cmovle  eax, edi
    ret
Run Code Online (Sandbox Code Playgroud)

顺便说一句,它仍然是相同的汇编代码:

int min3(int a, int b)
{
    return a < b? a:b;
}
Run Code Online (Sandbox Code Playgroud)

在这里,性能与值的统计分布更相关:如果a99% 是最大的,则恢复速度b可能a会快几纳秒:因为在大多数情况下会少一次移动。但对于大多数应用程序来说,这些值并不是那么可预测的,而且性能差异非常小,以至于永远不会被注意到。

开关示例

对于切换, 如果有足够的值来证明较小的初始开销是合理的,则编译器有可能使用分支表。对于非常大的值列表,map使用对键进行二分搜索肯定会提供更好的结果。

然而,这里它会产生一系列比较,并且再次强调,开关中值的顺序(例如最频繁的第一个)比返回方法对性能的影响更大。

如果您在 godbolt 上尝试您的代码片段,您会发现直接返回将产生更小且明显更快的代码。这似乎是由于该string类型使优化器的寿命变得更加困难。但我不会在更一般的情况下押注这种情况(例如,在两个函数的相同代码const char*中再次使用结果)

当日报价:

过早的优化是万恶之源——Donald Knuth