2012-01-09 81 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