我只是想知道如果你做了这样的事情会有任何速度或效率的损失:
int i = 0;
while(i < 100)
{
int var = 4;
i++;
}
宣告int var
一百次。在我看来会有,但我不确定。这样做会更实际/更快:
int i = 0;
int var;
while(i < 100)
{
var = 4;
i++;
}
或者它们是相同的,速度和效率的吗?
答案 0 :(得分:189)
局部变量的堆栈空间通常在函数范围内分配。因此,循环内部不会发生堆栈指针调整,只需将{4}分配给var
。因此,这两个片段具有相同的开销。
答案 1 :(得分:96)
对于原始类型和POD类型,它没有区别。编译器将在函数开头为变量分配堆栈空间,并在函数返回时解除分配。
对于具有非平凡构造函数的非POD类类型,它会产生影响 - 在这种情况下,将变量放在循环之外只会调用构造函数和析构函数一次,并且每次迭代都会调用赋值运算符,而放置它在循环内部将为循环的每次迭代调用构造函数和析构函数。根据类的构造函数,析构函数和赋值运算符的作用,这可能是也可能不是。
答案 2 :(得分:66)
它们都是相同的,通过查看编译器的功能(即使没有将优化设置为高),您可以找到以下内容:
看看编译器(gcc 4.0)对你的简单例子的作用:
1.C:
main(){ int var; while(int i < 100) { var = 4; } }
gcc -S 1.c
1.S:
_main:
pushl %ebp
movl %esp, %ebp
subl $24, %esp
movl $0, -16(%ebp)
jmp L2
L3:
movl $4, -12(%ebp)
L2:
cmpl $99, -16(%ebp)
jle L3
leave
ret
2.C
main() { while(int i < 100) { int var = 4; } }
gcc -S 2.c
2.S:
_main:
pushl %ebp
movl %esp, %ebp
subl $24, %esp
movl $0, -16(%ebp)
jmp L2
L3:
movl $4, -12(%ebp)
L2:
cmpl $99, -16(%ebp)
jle L3
leave
ret
从这些内容中,您可以看到两件事:第一,代码在两者中都是相同的。
其次,var的存储在循环外部分配:
subl $24, %esp
最后,循环中唯一的东西是赋值和条件检查:
L3:
movl $4, -12(%ebp)
L2:
cmpl $99, -16(%ebp)
jle L3
如果不完全删除循环,那么效率就会高得多。
答案 3 :(得分:12)
现在最好在循环中声明它,除非它是常量,因为编译器将能够更好地优化代码(减少变量范围)。
编辑:这个答案现在已经过时了。随着后经典编译器的兴起,编译器无法弄清楚的情况越来越少。我仍然可以构建它们,但大多数人会将构造归类为错误的代码。答案 4 :(得分:10)
大多数现代编译器都会为您优化这一点。话虽如此,我会使用你的第一个例子,因为我发现它更具可读性。
答案 5 :(得分:8)
对于内置类型,两种样式之间可能没有区别(可能直到生成的代码)。
但是,如果变量是一个具有非平凡构造函数/析构函数的类,则运行时成本可能会有很大差异。我通常将变量范围限定在循环内部(以保持范围尽可能小),但如果事实证明具有性能影响,我会将类变量移到循环范围之外。但是,这样做需要一些额外的分析,因为ode路径的语义可能会改变,所以只有在sematics允许的情况下才能这样做。
RAII类可能需要此行为。例如,可能需要在每次循环迭代时创建和销毁管理文件访问生存期的类,以正确管理文件访问。
假设你有一个LockMgr
类,它在构造时获取一个关键部分,并在销毁时释放它:
while (i< 100) {
LockMgr lock( myCriticalSection); // acquires a critical section at start of
// each loop iteration
// do stuff...
} // critical section is released at end of each loop iteration
完全不同于:
LockMgr lock( myCriticalSection);
while (i< 100) {
// do stuff...
}
答案 6 :(得分:6)
两个循环都具有相同的效率。他们都会花费无限的时间:)在循环中增加i可能是个好主意。
答案 7 :(得分:2)
我曾经进行过一些性能测试,令我惊讶的是,发现案例1实际上更快!我想这可能是因为在循环中声明变量会缩小其范围,因此它会更早被释放。然而,那是很久以前,在一个非常古老的编译器上。我确信现代编译器能够更好地优化差异,但保持变量范围尽可能短并不会造成伤害。
答案 8 :(得分:0)
唯一可以确定的方法就是计时。但是差异,如果有的话,将是微观的,所以你需要一个强大的时间循环。
更重要的是,第一个是更好的样式,因为它初始化变量var,而另一个让它未初始化。这个以及人们应该尽可能地将变量定义为接近其使用点的指导意味着通常应首选第一种形式。
答案 9 :(得分:-1)
只有两个变量,编译器可能会为两者分配一个寄存器。无论如何这些寄存器都在那里,所以这不需要时间。在任何一种情况下都有2个寄存器写入和1个寄存器读取指令。
答案 10 :(得分:-2)
我认为大多数答案都缺少一个要考虑的重点:“它是否清楚”,显然所有的讨论都是事实;不它不是。 我建议在大多数循环代码中,效率几乎不是问题(除非你计算火星着陆器),所以真正唯一的问题是什么看起来更明智和可读&amp;可维护 - 在这种情况下,我建议在前面声明变量&amp;在循环之外 - 这简直使它更清晰。然后人们喜欢你和他们我甚至懒得浪费时间在网上查看它是否有效。
答案 11 :(得分:-6)
即使可能它们最终会在堆栈上的同一个地方结束它仍然分配它。 它将为该int分配堆栈上的内存位置,然后在}结尾处释放它。在意义上不是堆自由意义上它会将sp(堆栈指针)移动1。 在你的情况下,考虑到它只有一个局部变量,它只是简单地等于fp(帧指针)和sp
简短的回答是:小心谨慎,几乎不用同样的工作。
但是请尝试阅读有关堆栈组织方式的更多信息。我的本科学校有很好的讲座 如果您想阅读更多,请点击此处 http://www.cs.utk.edu/~plank/plank/classes/cs360/360/notes/Assembler1/lecture.html