C# 作为for循环,循环比while循环快得多,那么当语句更改时,while循环会更快。会发生什么?

C# 作为for循环,循环比while循环快得多,那么当语句更改时,while循环会更快。会发生什么?,c#,performance,C#,Performance,出于兴趣,我测试了for循环和while循环在做相同的事情时是否有任何区别。是什么原因导致我的计算机(AMD Phenom II X6 1090T@3.20GHz)上的while循环比for循环长2-2.5秒?他们不是在做同样的事情吗?你得到类似的结果了吗 另外,当我替换x=null

出于兴趣,我测试了for循环和while循环在做相同的事情时是否有任何区别。是什么原因导致我的计算机(AMD Phenom II X6 1090T@3.20GHz)上的while循环比for循环长2-2.5秒?他们不是在做同样的事情吗?你得到类似的结果了吗

另外,当我替换
x=null
当然,迭代的次数相当高,但差异不是仍然非常显著吗

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;
}

您可能希望突破cordbg(并小心地启用所有JIT优化)来查看生成的本机代码,以准确了解发生这种情况的原因。。。但是为什么要麻烦呢?在实际代码中,差异不会很大,因为您将在循环中进行实际工作


微观优化完全不切实际的代码不是一个富有成效的练习,IMO。即使是微观优化真实代码通常也不会有成效,除非您已经验证这是瓶颈。

有关信息-无法重新编制:

与任务一起:

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
(这里,我在发布模式下运行,在调试器之外,等等)


这可能是特定于框架的(我使用的是4.0.30319.488,x64)或特定于CPU的(我使用的是Intel i7 4x2.67GHz(加上HT)),但我的第一个猜测是测试是如何运行的。

,而这完全是一个微优化,永远不会成为性能瓶颈。有趣的是,这两者实际上是不同的,有趣的是,当您使用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打印(尽管两者的循环迭代次数相同)

当循环内容仅为a
时,两个循环在CIL中缩短2行,并删除以下行(对于两个循环):

但是,当我们在版本中构建时,代码是非常不同的:

对于这两个循环,
x=null;
之间的差异是没有的,因为优化器已经注意到该值从未变为非null

优化的for和while循环之间的差异如下:

循环的CIL

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
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
和CIL
循环:

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
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
循环的CIL代码相同

这就是您的不同之处。For循环在执行完全相同的行为时,其执行方式与while循环完全相同。您注意到的差异完全是由于在debug而不是release中运行代码,再加上JIT并不总是像release代码优化器那样高效


我很喜欢这个问题,从中我学到了一些东西;我希望其他人也能这样做。+1

不要将
DateTime
用于基准测试-使用
Stopwatch
类,该类专门用于此用途。也许您的编译器已经对某些内容进行了优化。只需几个指针,尝试使用
Stopwatch
进行性能计时目的,并进行多次运行以过滤JIT的冷运行的开销(基本上忽略第一次运行)。感谢Stopwatch提示。。似乎当我使用Stopwatch时,for循环在这两种情况下都更快。但是对于空语句,差异仍然超过1秒。+1如果差异也由于循环中的代码而波动,那么差异可能根本不存在,更不用说性能问题了。您你可能是对的。如果在实际工作中差异仍然很大,我会开始担心这个问题。+1当我实际查看生成的CIL指令时,很容易就明白为什么这两个循环实际执行的不同。你似乎是对的……我检查了,根据我运行tes的方式,似乎得到了截然不同的结果t、 甚至是哪种循环先运行的顺序。这种差异似乎是由理论代码本身以外的所有因素造成的。+1确保代码在JIT中运行,一旦与在发布模式下运行并进行优化相结合,则会对执行时间产生很大的影响哇!+非常有趣。瞧g前进到任何编辑:)这就是为什么这两个循环执行不同的原因。非常好……正如其他答案中提到的,这是一个微优化,我们可能不应该费心去寻找,但谢谢