已经问过that问题了。答案是“局部变量的堆栈空间通常在函数范围内分配”。因此,在循环外部/内部声明变量的开销没有差别。
现在,假设我们在循环内部有一个带有函数的代码片段:
void do_sth(int &i) {int var=i+1;}
int i = 0;
while(i < 100)
{
do_sth(i);
i++;
}
第二个片段,其外部声明了变量:
int i = 0;
int var;
while(i < 100)
{
var = i+1;
i++;
}
我的问题是 - 在实际场景(使用现代编译器)中第一个片段的开销是多少?如果确实存在开销,那么它有多大?它是否与在循环的每个步骤中对整数进行额外的加法(运算符+)相比较?
答案 0 :(得分:2)
启用了优化的现代编译器很可能内联函数调用,只要它符合内联需求(没有外部链接等),因此这两个版本将生成相同的代码。
如果函数没有内联,则确实存在开销:函数调用和函数返回,参数在堆栈中传递。这不过是一个简单的补充。
答案 1 :(得分:1)
找出答案的最佳方法是查看调试器中的反汇编代码。 在这种情况下,第一个代码具有循环中函数调用的开销。根据用于函数的调用约定,函数调用可能会发生不同的事情。汇编代码中的通常情况是将参数推送到堆栈,调用函数,然后函数创建堆栈帧,弹出堆栈以获取参数,并在函数返回时弹出堆栈以获取调用者调用者的堆栈帧。对于功能体非常短的代码,头部可以是实际功能体的10倍左右。 (10条指令与1条指令)。 如果将函数定义为内联函数,则所有开销都会消失。
答案 2 :(得分:0)
如果要求在最基本的级别上优化代码,任何合理的编译器都会为这两个片段生成完全相同的指令。
答案 3 :(得分:0)
当前的编译器足够聪明,可以检查变量和函数的使用情况,并在编译时和链接时优化代码。因此,在优化之后,两段代码之间应该有微不足道的差异。此LLVM链接时间优化文档应提供更好的相关信息。
答案 4 :(得分:0)
int i = 0;
int var;
while(i < 100)
{
var = i+1;
i++;
}
int i = 0;
while(i < 100)
{
int var = i+1;
i++;
}
通常会产生完全相同的代码。但是有充分的理由使用第二种。
它的意图更清晰,代码可以更好地进行优化,因为编译器知道var仅在循环的持续时间内需要
答案 5 :(得分:0)
我假设允许编译器最好地优化代码(否则谈论性能有点无意义)。
如果在示例中编译循环时do_sth
的主体是可见的,编译器很可能会内联它,然后删除var
的赋值(并为{{1}分配堆栈空间})作为死代码,因此对于这种情况,开销实际上并不存在。如果无法内联var
,则函数调用的成本比int的声明更令人担忧。如果函数可以内联,即使do_sth
不是死代码,编译器也有可能将第一个版本转换为第二个版本。所以对于这样的例子来说真的没关系。
如果你的变量是一个更复杂的类型(非POD classtype),这很重要。在这种情况下,第一个版本将为每次迭代调用构造函数和析构函数一次,而第二个版本将为每次迭代调用赋值运算符一次,并且只对构造函数和析构函数调用一次。但请注意,这并没有说明哪个版本更快(取决于这些方法的实现)。
答案 6 :(得分:0)
您确定已正确解决了问题/疑问吗?根据您发布的内容 - do_sth函数将具有通常的函数调用开销。如果你内联它会消除开销。
答案 7 :(得分:0)
我想知道的是 - 整个程序执行时间占这个循环的百分比是多少?
例如,如果这只是一个测试程序,并且您正在执行该代码1e9次并对其进行计时,而不执行任何其他操作,则将 在函数调用时产生显着差异内联与否。
根据我的经验,在任何真实的程序中,这种循环很少占用很多时间。 我认为你不需要被告知这一点,但是一些程序员必须学会一种比例感。 理发不会有助于减肥。