就性能而言,是否有任何理由支持函数中的单个返回语句而不是多个返回语句?
考虑下面的简单例子。
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)
请注意,我意识到意见在风格方面存在分歧,因此我希望答案集中在性能方面。
回还是不回,这是一个问题。是吗 ?
对于第一个片段,优化器很可能为两种替代方案生成相同的代码。以 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