我只是想知道如果您执行以下操作是否会降低速度或效率:
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++;
}
还是在速度和效率上都一样?
Answers:
局部变量的堆栈空间通常在函数范围内分配。因此,循环内不会发生堆栈指针调整,只需将4分配给即可var
。因此,这两个摘要具有相同的开销。
var
变量已初始化但从未使用过,因此合理的优化程序可以将其完全删除(如果变量在循环后的某个地方使用,则第二个片段除外)。
对于基本类型和POD类型,这没有什么区别。两种情况下,编译器都会在函数的开头为变量分配堆栈空间,并在函数返回时取消分配。
对于具有非平凡构造函数的非POD类类型,它将有所作为-在这种情况下,将变量放在循环外只会调用一次构造函数和析构函数,而每次迭代都会调用赋值运算符,而将其放入循环将为循环的每次迭代调用构造函数和析构函数。根据类的构造函数,析构函数和赋值运算符的作用,这可能是理想的,也可能不是理想的。
它们都是相同的,这是通过查看编译器的工作(即使没有将优化设置为高)来找出的方法:
看看编译器(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
在不完全删除循环的情况下,这将尽可能地有效。
如今,最好在循环内声明它,除非它是一个常量,因为编译器将能够更好地优化代码(减少变量作用域)。
编辑:这个答案现在已经过时了。随着后古典编译器的兴起,编译器无法解决的情况越来越少。我仍然可以构造它们,但是大多数人会将其归类为不良代码。
大多数现代编译器都会为您优化此过程。话虽这么说,我会使用您的第一个示例,因为我觉得它更具可读性。
对于内置类型,这两种样式之间可能没有区别(可能直接到生成的代码)。
但是,如果变量是具有非平凡的构造函数/析构函数的类,则运行时成本可能会有很大的不同。我通常将变量的作用域放在循环内部(以使作用域尽可能小),但是,如果结果证明对性能产生影响,我希望将类变量移到循环作用域之外。但是,这样做需要进行一些额外的分析,因为ode路径的语义可能会更改,因此只有在语义允许的情况下才能执行此操作。
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可能是一个好主意。
我曾经进行过一些性能测试,令我惊讶的是,发现情况1实际上更快!我想这可能是因为在循环内声明变量会减小其范围,因此它会较早释放。但是,那是很久以前的,使用的是非常旧的编译器。我相信现代编译器在优化差异方面做得更好,但是将变量范围保持在尽可能短的范围内也无害。
&i
),则会溢出循环变量。
确保唯一的方法是计时它们。但是,如果存在差异,那么差异将是微观的,因此您将需要强大的大时序循环。
更重要的是,第一个是更好的样式,因为它会初始化变量var,而另一个则使变量未初始化。这以及人们应该尽可能在变量的使用点附近定义变量的准则,这意味着通常应该首选第一种形式。