在循环中声明变量是否有任何开销? (C++)

发布于 2024-07-23 07:01:12 字数 307 浏览 10 评论 0原文

我只是想知道如果您执行以下操作是否会造成速度或效率损失:

int i = 0;
while(i < 100)
{
    int var = 4;
    i++;
}

声明 int var 一百次。 在我看来好像会有,但我不确定。 这样做是否会更实用/更快:

int i = 0;
int var;
while(i < 100)
{
    var = 4;
    i++;
}

或者它们在速度和效率方面是否相同?

I am just wondering if there would be any loss of speed or efficiency if you did something like this:

int i = 0;
while(i < 100)
{
    int var = 4;
    i++;
}

which declares int var one hundred times. It seems to me like there would be, but I'm not sure. would it be more practical/faster to do this instead:

int i = 0;
int var;
while(i < 100)
{
    var = 4;
    i++;
}

or are they the same, speedwise and efficiency-wise?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(13

泅人 2024-07-30 07:01:12

局部变量的堆栈空间通常在函数作用域中分配。 因此循环内部不会发生堆栈指针调整,只是将 4 分配给 var。 因此,这两个片段具有相同的开销。

Stack space for local variables is usually allocated in function scope. So no stack pointer adjustment happens inside the loop, just assigning 4 to var. Therefore these two snippets have the same overhead.

夜灵血窟げ 2024-07-30 07:01:12

对于原始类型和 POD 类型来说,没有区别。 在这两种情况下,编译器都会在函数开头为变量分配堆栈空间,并在函数返回时释放它。

对于具有重要构造函数的非 POD 类类型,它会产生影响 - 在这种情况下,将变量放在循环之外只会调用一次构造函数和析构函数,并且每次迭代都会调用赋值运算符,而将其放在循环内部循环将为循环的每次迭代调用构造函数和析构函数。 根据类的构造函数、析构函数和赋值运算符的作用,这可能是理想的,也可能不是理想的。

For primitive types and POD types, it makes no difference. The compiler will allocate the stack space for the variable at the beginning of the function and deallocate it when the function returns in both cases.

For non-POD class types that have non-trivial constructors, it WILL make a difference -- in that case, putting the variable outside the loop will only call the constructor and destructor once and the assignment operator each iteration, whereas putting it inside the loop will call the constructor and destructor for every iteration of the loop. Depending on what the class' constructor, destructor, and assignment operator do, this may or may not be desirable.

隔纱相望 2024-07-30 07:01:12

它们都是相同的,通过查看编译器的作用(即使没有将优化设置为高),您可以通过以下方式找到答案:

看看编译器(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

这在不完全删除循环的情况下尽可能高效。

They are both the same, and here's how you can find out, by looking at what the compiler does (even without optimisation set to high):

Look at what the compiler (gcc 4.0) does to your simple examples:

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

From these, you can see two things: firstly, the code is the same in both.

Secondly, the storage for var is allocated outside the loop:

         subl    $24, %esp

And finally the only thing in the loop is the assignment and condition check:

L3:
        movl    $4, -12(%ebp)
L2:
        cmpl    $99, -16(%ebp)
        jle     L3

Which is about as efficient as you can be without removing the loop entirely.

天涯沦落人 2024-07-30 07:01:12

如今,最好在循环内声明它,除非它是常量,因为编译器将能够更好地优化代码(减少变量范围)。

编辑:这个答案现在基本上已经过时了。 随着后经典编译器的兴起,编译器无法弄清楚的情况越来越少。 我仍然可以构建它们,但大多数人会将这种构建归类为糟糕的代码。

These days it is better to declare it inside the loop unless it is a constant as the compiler will be able to better optimize the code (reducing variable scope).

EDIT: This answer is mostly obsolete now. With the rise of post-classical compilers, the cases where the compiler can't figure it out are getting rare. I can still construct them but most people would classify the construction as bad code.

手心的海 2024-07-30 07:01:12

大多数现代编译器都会为您优化这一点。 话虽这么说,我会使用你的第一个例子,因为我发现它更具可读性。

Most modern compilers will optimize this for you. That being said I would use your first example as I find it more readable.

笑,眼淚并存 2024-07-30 07:01:12

对于内置类型,两种样式之间可能没有区别(可能就生成的代码而言)。

但是,如果变量是具有重要构造函数/析构函数的类,则运行时成本很可能存在重大差异。 我通常会将变量的范围限制在循环内部(以保持范围尽可能小),但如果这对性能产生影响,我会考虑将类变量移到循环范围之外。 然而,这样做需要一些额外的分析,因为颂歌路径的语义可能会改变,所以只有在语义允许的情况下才能这样做。

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...

}

For a built-in type there will likely be no difference between the 2 styles (probably right down to the generated code).

However, if the variable is a class with a non-trivial constructor/destructor there could well be a major difference in runtime cost. I'd generally scope the variable to inside the loop (to keep the scope as small as possible), but if that turns out to have a perf impact I'd look to moving the class variable outside the loop's scope. However, doing that needs some additional analysis as the semantics of the ode path may change, so this can only be done if the sematics permit it.

An RAII class might need this behavior. For example, a class that manages file access lifetime might need to be created and destroyed on each loop iteration to manage the file access properly.

Suppose you have a LockMgr class that acquires a critical section when it's constructed and releases it when destroyed:

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

is quite different from:

LockMgr lock( myCriticalSection);
while (i< 100) {

    // do stuff...

}
oО清风挽发oО 2024-07-30 07:01:12

两个循环具有相同的效率。 它们都将花费无限长的时间:) 在循环内增加 i 可能是一个好主意。

Both loops have the same efficiency. They will both take an infinite amount of time :) It may be a good idea to increment i inside the loops.

柠檬 2024-07-30 07:01:12

我曾经进行过一些性能测试,令我惊讶的是,情况1实际上更快! 我想这可能是因为在循环内声明变量会减少其范围,因此它会更早被释放。 然而,那是很久以前的事了,在一个非常古老的编译器上。 我确信现代编译器在优化差异方面做得更好,但保持变量范围尽可能短仍然没有坏处。

I once ran some perfomance tests, and to my surprise, found that case 1 was actually faster! I suppose this may be because declaring the variable inside the loop reduces its scope, so it gets free'd earlier. However, that was a long time ago, on a very old compiler. Im sure modern compilers do a better job of optimizing away the diferences, but it still doesn't hurt to keep your variable scope as short as possible.

原来是傀儡 2024-07-30 07:01:12
#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 次,这意味着每次函数调用循环内的局部变量只分配一次。

#include <stdio.h>
int main()
{
    for(int i = 0; i < 10; i++)
    {
        int test;
        if(i == 0)
            test = 100;
        printf("%d\n", test);
    }
}

Code above always prints 100 10 times which means local variable inside loop is only allocated once per each function call.

坚持沉默 2024-07-30 07:01:12

唯一确定的方法就是给它们计时。 但是,即使存在差异,差异也将是微小的,因此您将需要一个强大的定时循环。

更重要的是,第一个是更好的风格,因为它初始化变量 var,而另一个则使其未初始化。 这以及定义变量应尽可能接近其使用点的准则意味着通常应首选第一种形式。

The only way to be sure is to time them. But the difference, if there is one, will be microscopic, so you will need a mighty big timing loop.

More to the point, the first one is better style because it initializes the variable var, while the other one leaves it uninitialized. This and the guideline that one should define variables as near to their point of use as possible, means that the first form should normally be preferred.

高跟鞋的旋律 2024-07-30 07:01:12

如果只有两个变量,编译器可能会为这两个变量分配一个寄存器。 无论如何,这些寄存器都在那里,所以这并不需要时间。 无论哪种情况,都有 2 个寄存器写入指令和 1 个寄存器读取指令。

With only two variables, the compiler will likely be assign a register for both. These registers are there anyway, so this doesn't take time. There are 2 register write and one register read instruction in either case.

热风软妹 2024-07-30 07:01:12

我认为大多数答案都忽略了一个需要考虑的要点:“是否清楚”,显然,通过所有讨论,事实是; 不它不是。
我建议在大多数循环代码中,效率几乎不是问题(除非您计算火星着陆器),所以实际上唯一的问题是什么看起来更明智和可读& 可维护 - 在这种情况下,我建议预先声明变量& 在循环之外——这只是让它变得更清晰。 然后像你这样的人& 我什至懒得浪费时间在网上检查它是否有效。

I think that most answers are missing a major point to consider which is: "Is it clear" and obviously by all the discussion the fact is; no it is not.
I'd suggest in most loop code the efficiency is pretty much a non-issue (unless you calculating for a mars lander), so really the only question is what looks more sensible and readable & maintainable - in this case I'd recommend declaring the variable up front & outside the loop - this simply makes it clearer. Then people like you & I would not even bother to waste time checking online to see if it's valid or not.

尛丟丟 2024-07-30 07:01:12

这不是真的
存在开销,但其开销可以忽略不计。

即使它们可能最终位于堆栈上的同一位置,它仍然会分配它。
它将在堆栈上为该 int 分配内存位置,然后在 } 末尾释放它。 不是在堆自由意义上,它会将 sp(堆栈指针)移动 1。
在你的情况下,考虑到它只有一个局部变量,它只会简单地将 fp(帧指针) 和 sp 等同起来。

简短的答案是:不关心任何一种方式几乎都是一样的。

但请尝试阅读更多有关堆栈如何组织的内容。 我的本科学校在这方面有很好的讲座
如果您想阅读更多内容,请查看此处
http://www.cs .utk.edu/~plank/plank/classes/cs360/360/notes/Assembler1/lecture.html

thats not true
there is overhead however its neglect able overhead.

Even though probably they will end up at same place on stack It still assigns it.
It will assign memory location on stack for that int and then free it at the end of }. Not in heap free sense in sense it will move sp (stack pointer) by 1.
And in your case considering it only has one local variable it will just simply equate fp(frame pointer) and sp

Short answer would be: DONT CARE EITHER WAY WORKS ALMOST THE SAME.

But try reading more on how stack is organized. My undergrad school had pretty good lectures on that
If you wanna read more check here
http://www.cs.utk.edu/~plank/plank/classes/cs360/360/notes/Assembler1/lecture.html

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文