我只是想知道,如果你这样做,是否会损失速度或效率:
int i = 0;
while(i < 100)
{
int var = 4;
i++;
}
这段代码声明了int var
变量一百次。我认为应该可以这样做,但我不能确定。相比之下,以下方法是否更加实用/快速:
int i = 0;
int var;
while(i < 100)
{
var = 4;
i++;
}
它们在速度和效率方面是相同的吗?
我只是想知道,如果你这样做,是否会损失速度或效率:
int i = 0;
while(i < 100)
{
int var = 4;
i++;
}
这段代码声明了int var
变量一百次。我认为应该可以这样做,但我不能确定。相比之下,以下方法是否更加实用/快速:
int i = 0;
int var;
while(i < 100)
{
var = 4;
i++;
}
它们在速度和效率方面是相同的吗?
局部变量的堆栈空间通常在函数作用域中分配。因此,在循环内部不会进行堆栈指针调整,只是将4赋值给var
。因此,这两个代码片段具有相同的开销。
var
变量被初始化了,但从未被使用,因此一个合理的优化器可以完全删除它(除非在循环后面的第二个代码片段中该变量被使用)。 - CiaPan对于原始类型和POD类型,这没有任何区别。在两种情况下,编译器将在函数开始时为变量分配堆栈空间,并在函数返回时释放它。
对于具有非平凡构造函数的非POD类类型,情况就不同了——在这种情况下,将变量放在循环外只会在每次迭代时调用一次构造函数和析构函数以及赋值运算符,而将其放在循环内将在每次循环迭代中调用构造函数和析构函数。根据类的构造函数、析构函数和赋值运算符的具体实现,这可能是需要或不需要的。
它们是完全相同的,这里是如何查找编辑器产生的代码以确定(即使未设置高度优化):
查看编译器(gcc 4.0)对简单示例的处理:
1.c:
main(){ int var; while(int i < 100) { var = 4; } }
使用gcc -S 1.c命令来生成汇编代码文件1.s:
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
这大概是在不完全删除循环的情况下你能实现的最高效率了。
i
的语句时,您实际上是如何产生结果的?现代编译器是否足够智能,可以确定每次迭代时希望向前或向后递增多少? - Nicholas Hamilton现在最好在循环内部声明变量,除非它是一个常数,因为编译器能够更好地优化代码(减少变量作用域)。
编辑:这个答案现在大多已经过时。随着后现代编译器的兴起,编译器无法解决的情况越来越少了。我仍然可以构造出这种情况,但大多数人会将其分类为糟糕的代码。
大多数现代编译器都会为您进行优化。尽管如此,我会使用您的第一个示例,因为我认为它更易读。
对于内置类型,两种风格可能没有区别(甚至生成的代码都相同)。
然而,如果变量是带有非平凡构造/析构函数的类,则运行时成本可能会有很大差异。通常我会将变量限定在循环内部(尽可能缩小作用域),但如果发现具有性能影响,我会考虑将类变量移出循环的作用域。但是,这需要进行额外的分析,因为代码路径的语义可能会改变,因此只有在语义允许的情况下才能这样做。
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...
}
这两个循环的效率是相同的。它们都需要无限长的时间 :) 建议在循环内部增加 i 的值。
#include <stdio.h>
int main()
{
for(int i = 0; i < 10; i++)
{
int test;
if(i == 0)
test = 100;
printf("%d\n", test);
}
}
上面的代码总是打印出100 10次,这意味着循环内的局部变量只在每个函数调用时分配一次。
我曾经进行一些性能测试,令我惊讶的是,情况1实际上更快!我想这可能是因为在循环内声明变量会减少其范围,因此它会更早地被释放。但是,那是很久以前,在一个非常老的编译器上。我相信现代编译器会更好地优化掉差异,但仍然不妨将变量范围尽可能缩短。
确定的方法是计时它们。但如果有差异,那么差异将是微不足道的,因此您需要一个非常大的计时循环。
更重要的是,第一种方式是更好的风格,因为它初始化了变量var,而另一种方式则未对其进行初始化。这和指导方针一致,即应尽可能靠近使用点定义变量,这意味着通常应优先选择第一种形式。