在循環之前或循環中聲明變量之間的區別?

本文翻譯自:Difference between declaring variables before or in loop?

I have always wondered if, in general, declaring a throw-away variable before a loop, as opposed to repeatedly inside the loop, makes any (performance) difference? 我一直想知道,一般而言,在循環之前聲明一個拋棄型變量(而不是在循環內部重複)是否會產生(性能)差異? A (quite pointless) example in Java: Java中的一個(毫無意義的)示例:

a) declaration before loop: a)循環前聲明:

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

b) declaration (repeatedly) inside loop: b)循環內的聲明(反覆):

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

Which one is better, a or b ? ab哪個更好?

I suspect that repeated variable declaration (example b ) creates more overhead in theory , but that compilers are smart enough so that it doesn't matter. 我懷疑重複的變量聲明(示例b在理論上會產生更多開銷,但是編譯器足夠聰明,因此無關緊要。 Example b has the advantage of being more compact and limiting the scope of the variable to where it is used. 示例b的優點是更緊湊,並將變量的範圍限制在使用它的地方。 Still, I tend to code according example a . 儘管如此,我還是傾向於根據示例a進行編碼。

Edit: I am especially interested in the Java case. 編輯:我對Java案例特別感興趣。


#1樓

參考:https://stackoom.com/question/1hwd/在循環之前或循環中聲明變量之間的區別


#2樓

There is a difference in C# if you are using the variable in a lambda, etc. But in general the compiler will basically do the same thing, assuming the variable is only used within the loop. 如果在lambda等中使用變量,則C#有所不同。但是通常,假定變量僅在循環內使用,編譯器基本上會執行相同的操作。

Given that they are basically the same: Note that version b makes it much more obvious to readers that the variable isn't, and can't, be used after the loop. 鑑於它們基本上是相同的:請注意,版本b使讀者更清楚地知道該變量在循環之後不能使用,也不能使用。 Additionally, version b is much more easily refactored. 此外, 版本b更易於重構。 It is more difficult to extract the loop body into its own method in version a. 在版本a中將循環體提取到其自己的方法中更加困難。 Moreover, version b assures you that there is no side effect to such a refactoring. 而且,版本b向您保證,這種重構沒有副作用。

Hence, version a annoys me to no end, because there's no benefit to it and it makes it much more difficult to reason about the code... 因此,版本a無休止地困擾着我,因爲它沒有任何好處,並且使推理代碼變得更加困難...


#3樓

The following is what I wrote and compiled in .NET. 以下是我在.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);
}

This is what I get from .NET Reflector when CIL is rendered back into code. 這是當CIL渲染回代碼時從.NET Reflector中獲得的。

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. 在託管語言中,代碼將轉換爲CL /字節代碼,並且在執行時將其轉換爲機器語言。 So in machine language a double may not even be created on the stack. 因此,在機器語言中,甚至可能不會在堆棧上創建一個double。 It may just be a register as code reflect that it is a temporary variable for WriteLine function. 它可能只是一個寄存器,因爲代碼反映它是WriteLine函數的臨時變量。 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] 在某些情況下,您可以優化管理代碼,例如,如果您必須僅使用string a; a+=anotherstring[i]連接大量string a; a+=anotherstring[i] string a; a+=anotherstring[i] vs using StringBuilder . string a; a+=anotherstring[i]與使用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. 但這可以爲您優化基本的東西。


#4樓

I've always thought that if you declare your variables inside of your loop then you're wasting memory. 我一直以爲,如果在循環內聲明變量,那將浪費內存。 If you have something like this: 如果您有這樣的事情:

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

Then not only does the object need to be created for each iteration, but there needs to be a new reference allocated for each object. 然後,不僅需要爲每個迭代創建對象,而且還需要爲每個對象分配一個新的引用。 It seems that if the garbage collector is slow then you'll have a bunch of dangling references that need to be cleaned up. 看來,如果垃圾收集器運行緩慢,那麼您將有一堆懸掛的引用需要清理。

However, if you have this: 但是,如果您有:

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

Then you're only creating a single reference and assigning a new object to it each time. 然後,您僅創建一個引用並每次爲其分配一個新對象。 Sure, it might take a bit longer for it to go out of scope, but then there's only one dangling reference to deal with. 當然,超出範圍可能會花費更長的時間,但是隻有一個懸而未決的參考文獻需要處理。


#5樓

My practice is following: 我的做法是:

  • if type of variable is simple (int, double, ...) I prefer variant b (inside). 如果變量的類型很簡單(int,double,...),我更喜歡變量b (內部)。
    Reason: reducing scope of variable. 原因:減小變量的範圍。

  • if type of variable is not simple (some kind of class or struct ) I prefer variant a (outside). 如果變量的類型不簡單(某種classstruct ),我更喜歡變體a (外部)。
    Reason: reducing number of ctor-dtor calls. 原因:減少了ctor-dtor調用次數。


#6樓

A) is a safe bet than B).........Imagine if you are initializing structure in loop rather than 'int' or 'float' then what? 與B)相比,A)是一個安全的選擇......想象一下,如果您正在循環中初始化結構而不是“ int”或“ float”,那又是什麼?

like 喜歡

typedef struct loop_example{

JXTZ hi; // where JXTZ could be another type...say closed source lib 
         // you include in Makefile

}loop_example_struct;

//then....

int j = 0; // declare here or face c99 error if in loop - depends on compiler setting

for ( ;j++; )
{
   loop_example loop_object; // guess the result in memory heap?
}

You are certainly bound to face problems with memory leaks!. 您肯定會遇到內存泄漏的問題!。 Hence I believe 'A' is safer bet while 'B' is vulnerable to memory accumulation esp working close source libraries.You can check usinng 'Valgrind' Tool on Linux specifically sub tool 'Helgrind'. 因此,我相信“ A”是更安全的選擇,而“ B”更容易受到內存累積的影響,尤其是在靠近源代碼庫的情況下。您可以檢查Linux上的“ Valgrind”工具,特別是子工具“ Helgrind”。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章