我一直想知道,在一般情况下,在循环之前声明一个丢弃的变量,而不是在循环内部重复,使任何(性能)差异? Java中的一个(非常无意义的)例子:

A)循环前声明:

double intermediateResult;
for(int i=0; i < 1000; i++){
    intermediateResult = i;
    System.out.println(intermediateResult);
}

B)声明(重复)内部循环:

for(int i=0; i < 1000; i++){
    double intermediateResult = i;
    System.out.println(intermediateResult);
}

哪个更好,a还是b?

我怀疑重复的变量声明(例b)在理论上会产生更多的开销,但编译器足够聪明,所以这无关紧要。例b的优点是更紧凑,并且将变量的作用域限制在使用它的地方。尽管如此,我还是倾向于根据示例a编写代码。

编辑:我对Java案例特别感兴趣。


当前回答

作为一般规则,我将变量声明在最内部的可能范围内。如果你不在循环外使用intermediateResult,那么我会用B。

其他回答

我用Node 4.0.0测试了JS,如果有人感兴趣的话。在循环外声明会导致~。平均超过1000次试验,每次试验1亿次循环,性能提高5毫秒。所以我要用最易读/可维护的方式来写,在我看来就是B。我本想把我的代码放在小提琴中,但我使用了现在性能的Node模块。代码如下:

var now = require("../node_modules/performance-now")

// declare vars inside loop
function varInside(){
    for(var i = 0; i < 100000000; i++){
        var temp = i;
        var temp2 = i + 1;
        var temp3 = i + 2;
    }
}

// declare vars outside loop
function varOutside(){
    var temp;
    var temp2;
    var temp3;
    for(var i = 0; i < 100000000; i++){
        temp = i
        temp2 = i + 1
        temp3 = i + 2
    }
}

// for computing average execution times
var insideAvg = 0;
var outsideAvg = 0;

// run varInside a million times and average execution times
for(var i = 0; i < 1000; i++){
    var start = now()
    varInside()
    var end = now()
    insideAvg = (insideAvg + (end-start)) / 2
}

// run varOutside a million times and average execution times
for(var i = 0; i < 1000; i++){
    var start = now()
    varOutside()
    var end = now()
    outsideAvg = (outsideAvg + (end-start)) / 2
}

console.log('declared inside loop', insideAvg)
console.log('declared outside loop', outsideAvg)

这是个有趣的问题。从我的经验来看,当你为代码争论这个问题时,有一个终极问题需要考虑:

为什么变量需要是全局的?

It makes sense to only declare the variable once, globally, as opposed to many times locally, because it is better for organizing the code and requires less lines of code. However, if it only needs to be declared locally within one method, I would initialize it in that method so it is clear that the variable is exclusively relevant to that method. Be careful not to call this variable outside the method in which it is initialized if you choose the latter option--your code won't know what you're talking about and will report an error.

另外,作为旁注,不要在不同的方法之间重复局部变量名,即使它们的目的几乎相同;这让人很困惑。

这取决于语言- IIRC c#优化了这一点,所以没有任何区别,但JavaScript(例如)每次都会完成整个内存分配过程。

我一直认为,如果你在循环中声明变量,那么你就是在浪费内存。如果你有这样的东西:

for(;;) {
  Object o = new Object();
}

然后,不仅需要为每次迭代创建对象,还需要为每个对象分配一个新的引用。看起来,如果垃圾收集器很慢,那么就会有一堆需要清理的悬空引用。

然而,如果你有这样的情况:

Object o;
for(;;) {
  o = new Object();
}

然后,您只需创建一个引用,并每次为它分配一个新对象。当然,它可能需要更长的时间才能超出作用域,但这时只需要处理一个悬空引用。

以下是我在。net中编写和编译的内容。

double r0;
for (int i = 0; i < 1000; i++) {
    r0 = i*i;
    Console.WriteLine(r0);
}

for (int j = 0; j < 1000; j++) {
    double r1 = j*j;
    Console.WriteLine(r1);
}

这是我从。net Reflector中得到的,当CIL被渲染回代码时。

for (int i = 0; i < 0x3e8; i++)
{
    double r0 = i * i;
    Console.WriteLine(r0);
}
for (int j = 0; j < 0x3e8; j++)
{
    double r1 = j * j;
    Console.WriteLine(r1);
}

So both look exactly same after compilation. In managed languages code is converted into CL/byte code and at time of execution it's converted into machine language. So in machine language a double may not even be created on the stack. It may just be a register as code reflect that it is a temporary variable for WriteLine function. There are a whole set optimization rules just for loops. So the average guy shouldn't be worried about it, especially in managed languages. There are cases when you can optimize manage code, for example, if you have to concatenate a large number of strings using just string a; a+=anotherstring[i] vs using StringBuilder. There is very big difference in performance between both. There are a lot of such cases where the compiler cannot optimize your code, because it cannot figure out what is intended in a bigger scope. But it can pretty much optimize basic things for you.