2012-01-09 44 views
4

出於興趣,我測試了一下for循環和while循環做同樣的事情是否有區別。是什麼導致我的電腦(AMD Phenom II X6 1090T @ 3.20GHz)比while循環花費了大約2-2.5秒的while循環?他們不是在做同樣的事嗎?你有類似的結果嗎?循環比for循環快得多,while語句改變時while循環更快。怎麼了?

另外,當我用空語句替換循環中的x = null;語句時,while循環將顯着加快。這裏發生了什麼?

當然,迭代次數非常高,但是差異仍然非常顯着?

static void Main(string[] args) 
{ 
    String x; 
    const Int64 FIVE_BN = 5000000000; 
    Int64 i = 0; 

    DateTime start = DateTime.Now; 
    for (; FIVE_BN > i; i++) 
     x = null; //Replace with only ; in both loops and the for loop is faster 
    Console.Out.WriteLine(FIVE_BN.ToString() + " times (for): " + (DateTime.Now - start)); 

    i = 0; 

    start = DateTime.Now; 
    while(FIVE_BN > i++) 
     x = null; //Replace with only ; in both loops and the for loop is faster 
    Console.Out.WriteLine(FIVE_BN.ToString() + " times (while): " + (DateTime.Now - start)); 

    Console.Read(); 
    return; 
} 
+2

不要使用'DateTime'作爲基準 - 使用專門用於這種用途的'Stopwatch'類。 – Oded 2012-01-09 10:18:45

+0

也許某些東西已經被你的編譯器優化了。 – 2012-01-09 10:18:46

+1

只需幾個指針,嘗試使用'Stopwatch'進行性能計時,並執行多次運行以過濾掉正在進行JIT運行的冷啓動(基本忽略第一次運行)。 – 2012-01-09 10:19:00

回答

11

雖然這完全是一個微不足道的優化問題。有趣的是,這兩個實際上是不同的,有趣的是,當你提取方法均環與VS2010我得到如下:

private static String forLoop(ref Int64 i) 
{ 
    String x; 

    for (; FIVE_BN > i; i++) 
     x = null; //Replace with only ; in both loops and the for loop is faster 
    return x; 
} 

private static void whileloop(ref String x, ref Int64 i) 
{ 
    while (FIVE_BN > i++) 
     x = null; //Replace with only ; in both loops and the for loop is faster 
} 

這是很有趣......這表明,這兩種功能是確有不同。

現在當我們;替換循環的邏輯,我們得到以下的提取方法代替:

private static Int64 forLoopShort(Int64 i) 
{ 

    for (; FIVE_BN > i; i++) 
     ; //Replace with only ; in both loops and the for loop is faster 
    return i; 
} 

private static Int64 whileLoopShort(Int64 i) 
{ 

    while (FIVE_BN > i++) 
     ; //Replace with only ; in both loops and the for loop is faster 
    return i; 
} 

這說明了爲什麼循環運行基本上與此配置相同。我們需要看看優化後的CLR編碼看起來是什麼樣子(儘管優化器可能實際上刪除了這兩個函數之間的任何顯着差異)。這就是爲什麼在內聯(而不是提取到方法中)時它們是如何不同的用於以後的編輯。

編輯:

的CIL揭示了差異:

For循環具有.maxstack 2但while循環具有.maxstack 4,否則在操作順序有點性差異,由於事實,增量while發生在循環的開始,但for操作發生在循環結束時(將循環的內容更改爲Console.WriteLine(i)並查看While循環將從1打印,但For循環將從0打印(兩者都會盡管循環次數相同)

當環路內容只是一個;兩個環路是2線短於CIL除去了以下行(兩個迴路):

IL_0006: ldnull 
IL_0007: stloc.0 

然而,當我們建立在釋放代碼非常不同:

對於任一循環,x = null;;之間的區別不是什麼,因爲優化程序已經注意到該值從不變爲非空值。是

for和while循環優化的區別如下:

CIL for循環:

IL_0000: ldc.i4.0 
IL_0001: conv.i8 
IL_0002: stloc.0 
IL_0003: br.s  IL_000a 
IL_0005: ldloc.0 
IL_0006: ldc.i4.1 
IL_0007: conv.i8 
IL_0008: add 
IL_0009: stloc.0 
IL_000a: ldc.i8  0x12a05f200 
IL_0013: ldloc.0 
IL_0014: bgt.s  IL_0005 
IL_0016: ret 

而且CIL while循環:

IL_0000: ldc.i4.0 
IL_0001: conv.i8 
IL_0002: stloc.0 
IL_0003: ldc.i8  0x12a05f200 
IL_000c: ldloc.0 
IL_000d: dup 
IL_000e: ldc.i4.1 
IL_000f: conv.i8 
IL_0010: add 
IL_0011: stloc.0 
IL_0012: bgt.s  IL_0003 
IL_0014: ret 

所以我們可以看到,一個優化while循環比for循環快2個操作,但它使用更多的堆棧空間。

這兩者之間的區別似乎完全與i++發生位置的差異有關。

事實上,這是通過使一個新的方法證實:

private static void forLoopVeryShort() 
{ 
    string x; 
    Int64 i = 0; 

    for (; FIVE_BN > i++;) 
     ; //Replace with only ; in both loops and the for loop is faster 
} 

爲內置當此for方法(在任一釋放或調試)CIL代碼是相同的,所述while循環。

有謊言你的區別。當循環執行完全相同的行爲時,循環執行與while循環完全相同。您注意到的差異完全是由於在調試中運行代碼而不是發佈,與JIT並不總是像發佈代碼優化器一樣高效。

我很喜歡這個問題,我從中學到了一些東西;我希望別人也這樣做。 +1

+0

哇! +努力。很有意思。期待着任何編輯:) – 2012-01-11 12:25:58

+0

你剛剛提醒我編輯這個,我現在就做它... – Seph 2012-01-11 12:45:13

+0

而你去那裏,這就是爲什麼兩個循環**做**不同。 – Seph 2012-01-11 13:24:17

8

你可能會想打破Cordbg中出來(並仔細使所有JIT優化),以查看生成的本地代碼摸出究竟爲什麼這發生......但何必呢?在real代碼中,差異不會很大,因爲你將在循環中做真正的工作。

微型優化完全不現實的代碼並不是一個有效的練習,IMO。即使微優化實際代碼是通常沒有成果,除非你已經證實這是瓶頸。

+0

+1如果由於循環內部的代碼而導致差異波動,這種差異可能甚至不存在*在野外,更不用說在性能問題附近。 – 2012-01-09 10:21:43

+0

你可能是對的。如果在做實際工作時差異仍然很大,我會開始擔心這一點。 – 2012-01-09 10:42:51

+0

+1當我真正查看生成的CIL指令時,很容易明白爲什麼這兩個循環實際執行不同的結果 – Seph 2012-01-11 14:06:53

3

對於信息 - 不能瑞普:

與分配:

5000000000 times (for): 00:00:15.0488608 
5000000000 times (while): 00:00:12.7107270 

只需;

5000000000 times (for): 00:00:15.0558611 
5000000000 times (while): 00:00:12.7297281 

(在這裏,我在釋放模式運行,調試器外, etc)

它是可能,這是框架特定的(我使用4.0.30319.488,x64)或CPU特定的(我使用英特爾i7 4x2.67GHz(加HT)),但我的第一個猜測是如何測試是正在運行。

+0

您看起來是正確的......我檢查了一下,根據我如何運行測試看起來截然不同,甚至首先運行哪種循環的順序。除了理論代碼本身之外,這種差異似乎是由其他一切引起的。 – 2012-01-09 10:53:46

+0

+1確保代碼通過JIT運行,一旦已經與優化運行的發佈模式結合使用,就會對執行時間產生重大影響 – Seph 2012-01-11 14:09:25