c# - 循环作为 for 循环比 while 循环快得多,然后当语句更改时,while 循环更快。怎么了?

标签 c# performance

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

此外,当我将循环中的 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;
}

最佳答案

虽然这完全是一个微优化,但绝不会成为性能瓶颈。有趣的是,这两者实际上是不同的,有趣的是,当您使用 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,否则由于递增的事实,操作顺序有点不同for while 发生在循环开始处,而 for 操作发生在循环结束处(将循环内容更改为 Console.WriteLine( i) 并看到 While 循环将从 1 开始打印,而 For 循环将从 0 开始打印(尽管两者执行相同的循环迭代次数)。

当循环内容只是一个 ; 时,两个循环在 CIL 中都缩短了 2 行,并删除了以下行(对于两个循环):

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

这就是你的不同之处。当循环执行完全相同的行为时,for 循环的执行与 while 循环完全相同。您注意到的差异完全是由于在调试而不是发布中运行代码,再加上 JIT 并不总是像发布代码优化器那样高效。

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

关于c# - 循环作为 for 循环比 while 循环快得多,然后当语句更改时,while 循环更快。怎么了?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8786843/

相关文章:

c# - ComboBox 和 KeyValuePair 列表无法正常工作

c# - 如何从 C++/C# 调用此 DLL 函数

c# - 在 .Net 中锁定 - 是引用锁定还是对象锁定?

java - hibernate是否默认使用PreparedStatement

sql-server - SQL Server 2008 R2 中的空间会降低性能

c# - 使用 SIMD 查找 Span<ushort> 中是否存在 'ushort' 的最快方法?

c# - 如何将类库包含到(非 MVC)ASP.net Razor 网站中,以便我不必使用 using 语句?

c# - 如何在 StructureMap 中动态创建对象的实例?

python - 合并两个 : dicts vs lists 的最快方法

c++ - 比较两个 C++ 迭代器的性能成本