-1

在编写函数时,似乎总是有两个阵营。

  1. 那些希望返回 1 的函数可以在函数中找到(通常在最后)
  2. 那些喜欢线性数据流的人(通常有多个返回)

通常这更多的是个人风格,但我想谈谈性能。在我问这个问题之前,让我先提出一个假设场景:

假设要求状态:

Function should first try to calc ReturnVal based off of A. (Typical Case)
If A is unable to be determined then try to find based off of B. 
If B is unable to be determined then try to find based off of C. 
If all else fails then return C since that is always known.
Function should return enum which states which way the value was found.

因此,可以说我们有以下内容:

enum HowFound
{
  eWithA,
  eWithB,
  eWithC
};

HowFound CalcReturn(int& nValue) const;

我的问题 就速度(jmp 的数量等)而言,您认为编译器在大多数情况下可以更好地优化哪种风格?

样式 1

HowFound CalcReturn(int& nValue) const
{
  HowFound howFound = eWithA;

  const int valWithB = CalcWithB();
  const int valWithC = CalcWithC();

  nValue = CalcWithA( valWithB, valWithC );
  if (nValue == -1)
  {
    nValue = valWithB;
    howFound = eWithB;
    if (nValue == -1)
    {
       nValue = valWithC;
       howFound = eWithC;
    }
  }

  return howFound; 
}

使用样式 1,您存储返回值并且永远不会提前终止函数。你有一个回报,它位于最后。

风格 2

HowFound CalcReturn(int& nValue) const
{
  const int valWithB = CalcWithB();
  const int valWithC = CalcWithC();

  nValue = CalcWithA( valWithB, valWithC );
  if (nValue != -1)
  {
    return eWithA;
  }

  if (valWithB != -1)
  {
    nValue = valWithB;
    return eWithB;
  }

  nValue = valWithC;
  return eWithC;
}

使用样式 2,数据流更加“线性”。一旦找到该值,您就退出该函数。这意味着函数中有更多的终止点。

免责声明:显然每种样式都可以进行一些调整,但我的问题仍然保持不变。另外,是的,我可以编写这两个函数并检查反汇编(我有),但是随着更多“细节”的添加,结果会发生变化。我的问题是关于哪种风格表现更好的可能性更高(如果有的话)。

谢谢!

4

4 回答 4

1

好吧,尽管编译器所做的优化变化很大,但两种代码都将减少为或多或少同等有效的代码。因此执行时间将或多或少相同。但是,如果您正在寻找节省时钟周期的方法,则应该对其进行测试。在那种情况下,故事并没有就此结束。它还取决于硬件优化,尤其是分支预测器。因此,如果您对值的频率和所采用的路径有所了解,并且您正在寻找在时钟周期级别进行保存,那么重构代码以适应路径将会有所帮助。如果您不是在寻找这个,那么请考虑可读性。

于 2013-02-01T16:08:58.783 回答
0

在大多数情况下,编译器将为两种样式生成相同的代码,尽管我注意到您的特定实现可能效率较低(最公平的比较将在elses 中分配;您的分配然后重新分配)。

但是,当然,您的编译器可能会有所不同。

于 2013-02-01T15:57:54.157 回答
0

编译器会将您的两个示例翻译成相同的代码。它更多的是关于“哪个更容易阅读”[这实际上取决于您的代码如何匹配算法的实际描述]。

编辑:我应该说“任何体面的、优化的编译器”——一个非常基本的、优化不佳的编译器实际上可能完全按照你的要求做,而不需要重新安排任何东西,因此最终在一种情况下比另一种情况更好/更差。但是,只要您不故意编写代码以使其不必要地执行冗长的操作(例如,在长字符串上调用 strlen() 或 is_prime(101218819) 或类似的东西,然后不使用该值,您应该得到相同的结果结果)。与往常一样,如果您知道这是代码的重要部分 [基于分析],那么请务必尝试使用您用于项目的编译器和设置,看看它是否有任何不同 - 与往常一样,询问互联网不能替代对代码的关键部分进行基准测试。

于 2013-02-01T15:59:29.507 回答
-2

https://stackoverflow.com/faq#dontask

由于以下原因,这个问题似乎可能不合适:

  • 没有要解决的实际问题:“我很好奇其他人是否有这样的感觉。”</li>
  • 这是一个伪装成问题的咆哮:“______糟透了,我说的对吗?”</li>

除此之外,OP 也没有真正考虑过这个问题。

这是第三种风格:

 HowFound CalcReturn(int& nValue) const
 {
   const int valWithB = CalcWithB();
   const int valWithC = CalcWithC();
   HowFound howFound = eWithC;
   nValue = CalcWithA( valWithB, valWithC );
   if (nValue != -1)
   {
     howFound= eWithA;
   }
   else if (valWithB != -1)
   {
     nValue = valWithB;
     howFound = eWithB;
   } 
   else {
     nValue = valWithC;
   }
   return howFound;
 }

这种风格是A还是B?无论对linear的直觉如何,它似乎都是线性的。它有 1 个返回值,很可能所有 3 个函数都编译为相同的代码。尽管使用一些现代编译器,NRVO在单个出口点上效果更好( http://msdn.microsoft.com/en-us/library/ms364057%28v=vs.80%29.aspx#nrvo_cpp05_topic3,示例 4)。

我增加还是减少了复杂性?

这是更快还是更慢?

我可以通过多少其他方式转换此代码?

如何确定哪个更易于维护?

我可以说每次将一个函数转换为另一个只有 1 个返回值的函数必然会使代码同样或更复杂吗?

更复杂是什么意思?

其中一些是开放式问题......

于 2013-02-01T16:34:20.353 回答