在循环中声明variables是否有任何开销? (C ++)
我只是想知道如果你做了这样的事情,是否会有速度或效率的损失:
int i = 0; while(i < 100) { int var = 4; i++; }
它声明int var
一百次。 在我看来,似乎会有,但我不确定。 相反,这样做会更实际/更快:
int i = 0; int var; while(i < 100) { var = 4; i++; }
还是它们是相同的,速度和效率?
本地variables的堆栈空间通常在函数范围内分配。 所以在循环内部不会发生堆栈指针调整,只需将var
指定为4即可。 因此这两个片段具有相同的开销。
对于原始types和PODtypes,它没有区别。 编译器将在函数的开头为variables分配堆栈空间,并在函数返回时解除分配。
对于具有非平凡构造函数的非POD类types,这将会有所作为 – 在这种情况下,将variables放在循环外部只会调用构造函数和析构函数一次,赋值运算符每次迭代,而将它放在循环会为循环的每次迭代调用构造函数和析构函数。 根据类的构造函数,析构函数和赋值运算符的作用,这可能会也可能不需要。
它们都是相同的,通过查看编译器的function(即使没有将优化设置为高),您可以通过以下方法find它们:
看看编译器(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
无需完全去除循环就可以达到最高效率。
现在最好在循环中声明它,除非它是一个常量,因为编译器将能够更好地优化代码(减lessvariables范围)。
编辑:这个答案现在大多已经过时了。 随着后古典编译器的兴起,编译器无法解决的情况越来越less见。 我仍然可以构build它们,但是大多数人会把这个构造分类为不好的代码。
大多数现代编译器会为你优化这个。 这就是说,我会用你的第一个例子,因为我觉得它更可读。
对于内置types,这两种样式之间可能没有区别(可能是直到生成的代码)。
但是,如果variables是一个具有不平凡的构造函数/析构函数的类,那么运行时成本可能会有很大差异。 我通常会将variables放在循环的内部(以使范围尽可能小),但是如果这样做会导致性能下降,我会将类variables移到循环范围之外。 然而,这样做需要一些额外的分析,因为颂歌的语义可能会改变,所以这只能在语法允许的情况下才能完成。
RAII类可能需要这种行为。 例如,pipe理文件访问生命周期的类可能需要在每个循环迭代中创build和销毁,以正确pipe理文件访问。
假设你有一个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... }
两个循环都具有相同的效率。 他们都将花费无限的时间:)这可能是一个好主意,增加我在循环内。
我曾经进行过一些性能testing,但令我惊讶的是发现情况1实际上更快! 我想这可能是因为在循环中声明variables减less了它的范围,所以它在早期被释放。 不过,那是很久以前的一个非常古老的编译器。 我相信现代编译器在优化差异方面做得更好,但是尽可能缩短variables的范围仍然没有什么坏处。
唯一确定的方法是定时。 但是,如果有一个差异,将是微观的,所以你将需要一个强大的时序循环。
更重要的是,第一个样式是更好的样式,因为它初始化variablesvar,而另一个不初始化。 这和人们应该把variables尽可能地定义为接近他们的使用点的指导意味着第一种forms通常应该是优选的。
我认为大多数答案都缺less一个要考虑的重点:“是否清楚”,显然是所有讨论的事实; 不它不是。 我build议大多数循环代码的效率是非问题(除非你计算一个火星着陆器),所以真正唯一的问题是什么看起来更明智,可读性和可维护性 – 在这种情况下,我build议宣布variables的前面和循环外 – 这只是使它更清晰。 那么像我们这样的人甚至不会浪费时间在网上查看是否有效。
#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次,这意味着循环内的局部variables每次函数调用只分配一次。
只有两个variables,编译器可能会为这两个variables分配一个寄存器。 无论如何这些寄存器都有,所以这不需要时间。 无论是哪种情况,都有2个寄存器写入和1个寄存器读取指令。
这不是真的有开销,但它忽略了开销。
即使他们可能会在堆栈上的同一个地方结束它仍然分配它。 它将为该int分配栈的内存位置,然后在}的末尾释放它。 从无意义的意义上讲,它将使sp(堆栈指针)移动1.在你的情况下,考虑它只有一个局部variables,它只是简单地等同于fp(帧指针)和sp
简短的回答将是:不要担心,几乎相同的工作方式。
但是请尝试阅读更多有关如何组织。 我的本科学校有非常好的讲座如果你想阅读更多请点击这里http://www.cs.utk.edu/~plank/plank/classes/cs360/360/notes/Assembler1/lecture.html