C++ 功能速度提前终止或在结束时终止
在编写函数时,似乎总是有两个阵营C++ 功能速度提前终止或在结束时终止,c++,performance,visual-studio,C++,Performance,Visual Studio,在编写函数时,似乎总是有两个阵营 希望在函数中找到1的返回(通常在末尾) 喜欢线性数据流(通常具有多个回报)的人 通常这是个人风格,但我想谈谈表现。 在我提问之前,让我先提出一个假设情景: 假设需求说明: 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 i
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,数据流更“线性”。一旦找到该值,就退出该函数。这意味着函数中有更多的终止点
免责声明:显然,每种风格都可以进行一些调整,但我的问题仍然是一样的。此外,是的,我可以编写这两个函数并检查反汇编(我有),但随着更多“细节”的添加,结果会发生变化。我的问题是哪种风格更有可能表现得更好(如果有的话)
谢谢 在大多数情况下,编译器将为这两种样式生成相同的代码,尽管我注意到您的特定实现可能效率较低(最公平的比较是在
else
s中赋值;您的赋值然后重新赋值)
但是,当然,编译器可能会有所不同。编译器会将两个示例翻译成相同的代码。它更多的是关于“哪个更容易阅读”[这实际上取决于您的代码如何匹配算法的实际描述]
编辑:我应该说“任何像样的、优化的编译器”——一个非常初级的、优化不好的编译器实际上可以完全按照您的要求执行,而无需重新安排任何内容,从而在一种情况下比另一种情况更好/更差。但是,只要您没有故意编写代码,使其不必要地执行冗长的操作(例如,对长字符串调用strlen(),或者调用is_prime(101218819)或类似的东西,然后不使用该值,您应该会得到相同的结果)。和往常一样,如果你知道这是你的代码的一个重要部分[基于评测],那么一定要尝试一下你的项目使用的编译器和设置,看看它是否有什么不同-和往常一样,在互联网上询问并不能代替对代码关键部分进行基准测试 好吧,尽管编译器所做的优化有很多不同,但这两种代码都将被简化为效率大致相同的代码。因此,执行时间将大致相同。但是,如果您正在寻找时钟周期节省,您应该测试它。在这种情况下,故事不会就此结束。它还取决于硬件优化,尤其是分支预测器。因此,如果您对值的频率和所采用的路径有了概念,并且希望在时钟周期级别进行保存,那么重构代码以适应路径将有所帮助。如果你不想看这个,那就看可读性吧 由于以下原因,这个问题似乎可能不合适:
- 没有实际的问题需要解决:“我很好奇别人是否和我一样。”
- 这是一个伪装成问题的咆哮:“糟透了,我说得对吗?”
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型?它似乎是线性的,不管是什么样的线性直觉。它有1个返回,并且很可能所有3个函数都编译成相同的代码。尽管使用了一些现代编译器,但NRVO在使用单个出口点时工作得更好(例4)
我增加了还是减少了复杂性
这是快还是慢
我还可以用多少其他方法转换此代码
如何确定哪个更易于维护
我可以说,一个函数到另一个函数的每一次转换只返回1次,必然会使代码变得同样复杂或更复杂吗
更复杂到底意味着什么
其中有些问题是开放式的…你试过分析这些问题吗?a)没有区别。b) 不,这不是风格的问题。来自1号阵营的人们根本不理解C++中的编程是如何工作的,我不同意C++的风格资源管理。@我同意有一些关于样式1的争论,但是在一般情况下,方法2的优点显然超过了它的缺点:当函数更自然地表示为多个出口导致更复杂时,将一个函数重组为单出口。代码。这基本上是决定性的论据。@thang-增加函数的复杂性以使其更易于调试?!但这也不会使调试变得更难吗?@安全性问题。问题是关于C++的。如果你使用C++而不使用RAII,那么你就做错了,你应该感觉不好。虽然它可能被表述得不清楚,但我确实试着把它包含在需求中。请注意“(典型情况)”,如最高概率。@BabelFish上述答案有两部分。首先是一般情况。第二种情况下,您需要节省时钟周期。如果您想了解一般情况,请忽略第二部分。我想补充一件关于一般优化的事情。现在的编译器非常聪明。它们被设计成压缩所有可能的优化。您的代码并不复杂。因此,大多数编译器将生成同样高效的代码。因此,对于上面的代码,请考虑可读性。选择一个与逻辑匹配的代码,因为代码或多或少会像注释一样相同(即使传递相互测试视图)。