【问题标题】:A faster loop approach ('for' and 'foreach')? [duplicate]更快的循环方法(“for”和“foreach”)? [复制]
【发布时间】:2011-06-07 08:43:32
【问题描述】:

这个问题源于我在foreach 循环上提出上一个问题的原因。我有一个很大的字符串数组(比如数千个),我想遍历数组,并且能够根据特定条件进行突破,我需要最佳性能。

一些示例代码:

for(int i = 0; i < array.length && flag == true; i++){
    //Processing and set flag
}

//..or

foreach(string item in array){
    //processing...set flag
    if(!flag)
        break;
}

哪种方式会更便宜?

【问题讨论】:

  • 老实说一点都没有关系,您的性能问题将在其他地方
  • 你试过自己测量吗?
  • 你自己试过了吗?编写一个测试这个的程序并不难。此外,在第二种方法中,您不需要标志,只需使用 break 代替。
  • @tomas:哦!有一个逻辑错误>

标签: c# loops


【解决方案1】:

您始终可以对它们进行基准测试。使用Stopwatch 并迭代,比如一千万次迭代,看看哪个更快。

不过,我认为您会发现两者几乎相同,因为 JIT 编译器将 array 上的 foreach 优化为基本上 @987654325 @。

flevine100 实际上是正确的,一般forforeach 更有效对于GetEnumerator 方法创建新对象的类型实现IEnumeratorIEnumerator&lt;T&gt;(由于内存分配和方法调用开销);对于System.Collections.Generic 中的大多数集合,情况并非如此,但是,由于它们使用值类型枚举器(更不用说the foreach construct does not actually require an IEnumerable implementation in the first place)显式地实现了IEnumerable

甚至更少数组的情况,特别是因为它们是固定大小的,因此 JIT 编译器优化起来很简单。

【讨论】:

  • 当然,我认为 foreach 会因为其灵活性而产生更多开销。秒表是个好主意,只是想知道是否有人知道 foreach 是否包含额外的可避免开销
  • 感谢编辑。有点像我想的,谢谢:)
【解决方案2】:

我发现 for(...) 比 foreach() 快。我认为这是因为 foreach() 使用了 IEnumerable 管道。

既然您关心速度...在 .NET 4.0 中,如果您的循环体不依赖于共享状态,您应该使用 Parallel.For 或 Parallel.Foreach 将您的处理扩展到多个处理器上。

【讨论】:

  • 有趣 - 我的测试(只是为了好玩)显示相反 - foreach 在数组上 在我的测试中通常优于 for。话虽如此,从实际的角度来看,IMO 并没有“真正的”区别。 (此外,这些类型的基准测试通常存在严重缺陷,因为在很大程度上取决于平台和构建设置......)
  • 同意,通常取决于场景。我的场景显示 for 循环比 foreach 更好,但这可能是我的问题的产物。我有一个非常小的循环体的巨大数组。通常循环体将支配运行时,而不是循环构造的选择。
【解决方案3】:

我不会关注这种级别的微优化。

您有更好的优化机会,尤其是在处理字符串时。 for/foreach 差异将只占整个运行时的一小部分,以至于它的性能基本相同。

最好让算法尽可能“干净”,并寻找其他性能机会如果需要,例如线程化整个例程。

【讨论】:

  • 好的,谢谢。我对多线程不太熟悉.. 想想他们不会在 4 年的编程学位中教你这个
  • @Nicklamor:一般来说,专注于优化你的算法,而不是实际的代码行——当然,除非你测量并找到真正的瓶颈。
【解决方案4】:

在第二个示例中,您没有提前退出子句,但添加 break 代替您的标志将实现这一点。

除了 foreach 使用枚举器和 for 循环将取决于元素访问器的可伸缩性之外,我不清楚内部结构。在列表中,一旦添加了中断,它们实际上是相等的。

【讨论】:

  • 是的,对不起。我昨天编辑了它。出现逻辑错误
【解决方案5】:

如果不进行基准测试,如果两者之间存在明显差异,我会感到非常惊讶(当然,答案很大程度上取决于循环中正在完成的工作和集合的类型)。

根据我的经验,这些事情永远不会在生产代码中造成性能瓶颈。毫无疑问,任何执行任何重要操作的应用程序都涉及某种I/O 或网络交互,而这会造成大部分性能损失。

如果您担心,我强烈建议您分析有问题的代码并找出哪个更快。

【讨论】:

  • 秒表/基准测试似乎是要走的路。想知道如果 foreach 有很多开销或其他东西,是否有人新手
【解决方案6】:

对于一个简单的裸数组,for 循环往往会产生稍小的 IL。比较

    static int[] array = new int[100];

    static void UseForLoop () {
        for (int i = 0; i < array.Length; ++i) {
            Console.WriteLine(array[i]);
        }
    }

    static void UseForeachLoop () {
        foreach (int i in array) {
            Console.WriteLine(i);
        }
    }

从 VS 2010 生成以下 IL 集,默认发布配置:

.method private hidebysig static void UseForLoop() cil managed
{
        .maxstack 2
        .locals init (
                [0] int32 i)
        L_0000: ldc.i4.0 
        L_0001: stloc.0 
        L_0002: br.s L_0014
        L_0004: ldsfld int32[] ConsoleApplication5.Program::array
        L_0009: ldloc.0 
        L_000a: ldelem.i4 
        L_000b: call void [mscorlib]System.Console::WriteLine(int32)
        L_0010: ldloc.0 
        L_0011: ldc.i4.1 
        L_0012: add 
        L_0013: stloc.0 
        L_0014: ldloc.0 
        L_0015: ldsfld int32[] ConsoleApplication5.Program::array
        L_001a: ldlen 
        L_001b: conv.i4 
        L_001c: blt.s L_0004
        L_001e: ret 
}

.method private hidebysig static void UseForeachLoop() cil managed
{
        .maxstack 2
        .locals init (
                [0] int32 i,
                [1] int32[] CS$6$0000,
                [2] int32 CS$7$0001)
        L_0000: ldsfld int32[] ConsoleApplication5.Program::array
        L_0005: stloc.1 
        L_0006: ldc.i4.0 
        L_0007: stloc.2 
        L_0008: br.s L_0018
        L_000a: ldloc.1 
        L_000b: ldloc.2 
        L_000c: ldelem.i4 
        L_000d: stloc.0 
        L_000e: ldloc.0 
        L_000f: call void [mscorlib]System.Console::WriteLine(int32)
        L_0014: ldloc.2 
        L_0015: ldc.i4.1 
        L_0016: add 
        L_0017: stloc.2 
        L_0018: ldloc.2 
        L_0019: ldloc.1 
        L_001a: ldlen 
        L_001b: conv.i4 
        L_001c: blt.s L_000a
        L_001e: ret 
}

..但是那里的关键部分,循环,基本上是相同的。正如其他人所说,这也是一种微优化。这两种方法的 JIT'd x86 可能是相同的,除非您使用复杂的枚举器迭代复杂的集合,否则即使在实际示例中,差异也不会很大。

我会使用更易读的那个——如果速度是真的那么重要的问题,支持 for 循环,但你可能会从算法优化中获得更好的结果。

【讨论】:

    猜你喜欢
    • 2014-10-29
    • 2011-06-13
    • 1970-01-01
    • 2016-02-02
    • 2015-11-27
    • 1970-01-01
    • 1970-01-01
    • 2018-10-03
    • 1970-01-01
    相关资源
    最近更新 更多