在.NET中,哪个循环运行得更快,'for'或'foreach'?

时间:2008-12-13 19:45:43

标签: c# .net performance for-loop

在C#/ VB.NET / .NET中,哪个循环运行得更快,forforeach

自从我读到for循环比foreach循环工作得更快long time ago我认为它适用于所有集合,泛型集合,所有数组等等。

我搜索谷歌并发现了一些文章,但其中大多数都没有定论(阅读文章评论)并且是开放式的。

理想的是列出每个场景并为其提供最佳解决方案。

例如(只是它应该如何的例子):

  1. 用于迭代1000+的数组 字符串 - for优于foreach
  2. 用于迭代IList(非通用)字符串 - foreach更好 比for
  3. 网上发现了一些相同的参考文献:

    1. Original grand old article by Emmanuel Schanzer
    2. CodeProject FOREACH Vs. FOR
    3. Blog - To foreach or not to foreach, that is the question
    4. ASP.NET forum - NET 1.1 C# for vs foreach
    5. [编辑]

      除了可读性方面,我对事实和数据非常感兴趣。有些应用程序的最后一英里性能优化受到挤压很重要。

40 个答案:

答案 0 :(得分:311)

Patrick Smacchia blogged about this上个月得出以下结论:

  
      List上的
  • for循环比foreach便宜2倍多   列表上的循环。
  •   
  • 在数组上循环比在List上循环便宜约2倍。
  •   
  • 因此,使用for循环使用数组便宜5倍   使用foreach循环使用List   (我相信,这就是我们所做的一切)。
  •   

答案 1 :(得分:155)

foreach循环展示了比for循环更具体的意图

使用foreach循环向使用您的代码的任何人演示,您计划对集合中的每个成员执行某些操作,而不管其在集合中的位置。它还显示您没有修改原始集合(如果您尝试,则抛出异常)。

foreach的另一个优点是它适用于任何IEnumerable,其中for仅对IList有意义,其中每个元素实际上都有一个索引。< / p>

但是,如果需要使用元素的索引,那么当然应该允许使用for循环。但是如果你不需要使用索引,那么只有一个索引会使代码混乱。

据我所知,没有重大的性能影响。在未来的某个阶段,使用foreach调整代码以在多个内核上运行可能更容易,但现在不用担心。

答案 2 :(得分:151)

首先,反诉Dmitry's answer。对于数组,C#编译器为foreach发出的代码与对等for循环的代码相同。这就解释了为什么对于这个基准测试,结果基本相同:

using System;
using System.Diagnostics;
using System.Linq;

class Test
{
    const int Size = 1000000;
    const int Iterations = 10000;

    static void Main()
    {
        double[] data = new double[Size];
        Random rng = new Random();
        for (int i=0; i < data.Length; i++)
        {
            data[i] = rng.NextDouble();
        }

        double correctSum = data.Sum();

        Stopwatch sw = Stopwatch.StartNew();
        for (int i=0; i < Iterations; i++)
        {
            double sum = 0;
            for (int j=0; j < data.Length; j++)
            {
                sum += data[j];
            }
            if (Math.Abs(sum-correctSum) > 0.1)
            {
                Console.WriteLine("Summation failed");
                return;
            }
        }
        sw.Stop();
        Console.WriteLine("For loop: {0}", sw.ElapsedMilliseconds);

        sw = Stopwatch.StartNew();
        for (int i=0; i < Iterations; i++)
        {
            double sum = 0;
            foreach (double d in data)
            {
                sum += d;
            }
            if (Math.Abs(sum-correctSum) > 0.1)
            {
                Console.WriteLine("Summation failed");
                return;
            }
        }
        sw.Stop();
        Console.WriteLine("Foreach loop: {0}", sw.ElapsedMilliseconds);
    }
}

结果:

For loop: 16638
Foreach loop: 16529

接下来,验证Greg关于集合类型的重要性 - 在上面将数组更改为List<double>,结果会得到根本不同的结果。它不仅总体上明显变慢,而且foreach变得比通过索引访问慢得多。话虽如此,我仍然几乎总是更喜欢foreach到for循环,它使代码更简单 - 因为可读性几乎总是重要的,而微优化很少。

答案 3 :(得分:50)

任何时候都有关于性能的争论,你只需要编写一个小测试,这样你就可以使用定量结果来支持你的案例。

使用StopWatch类并重复几百万次,以保证准确性。 (如果没有for循环,这可能很难):

using System.Diagnostics;
//...
Stopwatch sw = new Stopwatch()
sw.Start()
for(int i = 0; i < 1000000;i ++)
{
    //do whatever it is you need to time
}
sw.Stop();
//print out sw.ElapsedMilliseconds

手指越过了这个节目的结果,差异可以忽略不计,你可能只是在最易维护的代码中做任何结果

答案 4 :(得分:48)

总是很接近。对于数组,有时 for稍微快一些,但foreach更具表现力,并提供LINQ等。一般来说,坚持使用foreach

此外,foreach可能会在某些情况下进行优化。例如,索引器可能会使链表变得很糟糕,但foreach可能很快。实际上,由于这个原因,标准LinkedList<T>甚至不提供索引器。

答案 5 :(得分:32)

我的猜测是,在99%的情况下它可能不会很重要,那么为什么你会选择更快而不是最合适的(最容易理解/维护)?

答案 6 :(得分:30)

偏好 foreach循环超过for循环的理由非常充分。如果你可以使用foreach循环,你的老板是对的。

但是,并非每次迭代都是按顺序逐个浏览列表。如果他禁止,那是错的。

如果我是你,我会做的是将你所有的自然转换为递归。那教会了他,这对你来说也是一种很好的心理锻炼。

答案 7 :(得分:30)

两者之间不太可能存在巨大的性能差异。一如既往,面对“哪个更快?”问题,你应该总是想“我可以衡量这个。”

编写两个在循环体中执行相同操作的循环,对它们执行和计时,并查看速度的差异。用一个几乎空的身体和一个类似于你实际做的循环体来做这件事。也可以使用您正在使用的集合类型来尝试它,因为不同类型的集合可以具有不同的性能特征。

答案 8 :(得分:16)

杰弗里·里希特在TechEd 2005上:

  

“多年来我开始学习C#编译器对我来说基本上是骗子。” ......“它涉及许多事情。” ..“就像你做一个foreach循环......”......“...这是你编写的一小段代码,但C#编译器为了做到这一点而吐出来的是它的现象。它推出了一个在那里尝试/ finally块,在finally块内部,它将你的变量转换为IDisposable接口,如果转换为suceeds,则调用它上面的Dispose方法,在循环内部,它在循环内部重复调用Current属性和MoveNext方法,在封面下创建了对象。很多人都使用foreach,因为编码非常简单,很容易做到。“..”foreach在性能方面不是很好,如果你通过使用square迭代了一个集合括号表示法,只是做索引,这只是更快,而且它不会在堆上创建任何对象......“

按需网络直播: http://msevents.microsoft.com/CUI/WebCastEventDetails.aspx?EventID=1032292286&EventCategory=3&culture=en-US&CountryCode=US

答案 9 :(得分:12)

这太荒谬了。没有令人信服的理由禁止for-loop,性能方面或其他。

有关性能基准和其他参数,请参阅Jon Skeet's blog

答案 10 :(得分:11)

如果你使用一组对象,foreach会更好,但如果你增加一个数字,for循环会更好。

请注意,在最后一种情况下,您可以执行以下操作:

foreach (int i in Enumerable.Range(1, 10))...

但它确实表现不佳,与for相比实际上表现更差。

答案 11 :(得分:10)

这可以救你:

public IEnumerator<int> For(int start, int end, int step) {
    int n = start;
    while (n <= end) {
        yield n;
        n += step;
    }
}

使用:

foreach (int n in For(1, 200, 4)) {
    Console.WriteLine(n);
}

为获得更大的胜利,您可以将三位代表作为参数。

答案 12 :(得分:7)

这与大多数“更快”的问题具有相同的两个答案:

1)如果你不衡量,你不知道。

2)(因为...)取决于。

这取决于“MoveNext()”方法的成本,相对于“this [int index]”方法的成本,对于您将迭代的IEnumerable的类型(或类型)。

“foreach”关键字是一系列操作的简写 - 它在IEnumerable上调用GetEnumerator()一次,每次迭代调用一次MoveNext(),它进行一些类型检查,依此类推。最有可能影响性能测量的是MoveNext()的成本,因为它被调用O(N)次。也许它很便宜,但也许不是。

“for”关键字看起来更具可预测性,但在大多数“for”循环中,您会发现类似“collection [index]”的内容。这看起来像一个简单的数组索引操作,但它实际上是一个方法调用,其成本完全取决于您迭代的集合的性质。可能它很便宜,但也许不是。

如果集合的底层结构本质上是一个链表,那么MoveNext很便宜,但索引器可能有O(N)成本,这使得“for”循环的真实成本为O(N * N)。

答案 13 :(得分:7)

“我可以使用任何参数来帮助我说服他使用for循环吗?”

不,如果你的老板微观管理到告诉你使用什么编程语言的水平,你真的没什么可说的。遗憾。

答案 14 :(得分:7)

当您循环遍历数组,列表等常见结构并执行for查询时,foreach - 和LINQ - 循环的速度差异很小在集合上几乎总是稍微慢一点,虽然它写得更好!正如其他海报所说,追求表现力而不是毫秒的额外表现。

到目前为止还没有说过,当编译foreach循环时,编译器会根据迭代的集合对其进行优化。这意味着当你不确定要使用哪个循环时,你应该使用foreach循环 - 它会在编译时为你生成最好的循环。它也更具可读性。

foreach循环的另一个关键优势是,如果您的集合实现发生更改(例如,从int array更改为List<int>),那么您的foreach循环赢了“需要进行任何代码更改:

foreach (int i in myCollection)

无论您的收藏品是什么类型,上述内容都是相同的,而在for循环中,如果您将myCollectionarray更改为{{{},则不会构建以下内容1}}:

List

答案 15 :(得分:7)

这可能取决于您枚举的集合类型及其索引器的实现。但总的来说,使用foreach可能是更好的方法。

此外,它适用于任何IEnumerable - 而不仅仅是索引器。

答案 16 :(得分:6)

每种语言结构都有适当的使用时间和地点。 C#语言有四个独立的iteration statements是有原因的 - 每个都是出于特定目的,并且具有适当的用途。

我建议与老板坐下来,试着理性地解释为什么for循环有目的。有时候for迭代块更清楚地描述算法而不是foreach迭代。如果是这样,则使用它们是合适的。

我还要向你的老板指出 - 性能不是,也不应该是任何实际的问题 - 更多的是以简洁,有意义,可维护的方式表达算法。像这样的微优化完全错过了性能优化,因为任何真正的性能优势都来自算法重新设计和重构,而不是循环重构。

如果经过理性的讨论,仍然存在这种威权观点,那么由你来决定如何进行。就个人而言,我不乐意在一个不鼓励理性思考的环境中工作,并考虑转到不同雇主的另一个职位。但是,我强烈建议在讨厌之前进行讨论 - 可能只是存在一个简单的误解。

答案 17 :(得分:5)

for是否比foreach更快是真的除此之外。我严重怀疑选择一个会对你的表现产生重大影响。

优化应用程序的最佳方法是通过分析实际代码。这将确定占用大部分工作/时间的方法。首先优化它们。如果性能仍然不可接受,请重复此过程。

作为一般规则,我建议远离微观优化,因为它们很少会产生任何重大收益。唯一的例外是在优化已识别的热路径时(即,如果您的分析确定了一些高度使用的方法,那么广泛优化这些方法可能是有意义的)。

答案 18 :(得分:5)

你在里面影响性能的循环,而不是实际的循环结构(假设你的情况不重要)。

答案 19 :(得分:4)

这两个将以几乎完全相同的方式运行。写一些代码来使用它们,然后向他展示IL。它应该显示可比较的计算,意味着性能没有差异。

答案 20 :(得分:4)

您可以在Deep .NET - part 1 Iteration

中了解它

它涵盖了.NET源代码一直到反汇编的结果(没有第一次初始化)。

例如-具有foreach循环的数组迭代: enter image description here

和-使用foreach循环列出迭代: enter image description here

和最终结果: enter image description here

enter image description here

答案 21 :(得分:3)

真的用他的头拧了一下,然后去了一个IQueryable .foreach关闭:

myList.ForEach(c =&gt; Console.WriteLine(c.ToString());

LOL

答案 22 :(得分:3)

for有更简单的逻辑来实现,所以它比foreach更快。

答案 23 :(得分:3)

在大多数情况下,确实没有区别。

通常,当您没有明确的数字索引时,您总是必须使用foreach,并且当您实际上没有可迭代的集合时(例如,在一个二维数组网格中迭代),您总是必须使用它。上三角)。在某些情况下,您可以选择。

有人可能会争辩说,如果魔术数字开始出现在代码中,for循环可能会更难维护。你应该对不能使用for循环而烦恼并且必须构建一个集合或使用lambda来构建一个子集合而不仅仅因为for循环被禁止了。

答案 24 :(得分:3)

杰弗里·里希特谈到了最近一次播客中for和foreach之间的性能差异:http://pixel8.infragistics.com/shows/everything.aspx#Episode:9317

答案 25 :(得分:3)

我发现foreach循环遍历List 更快。请参阅下面的测试结果。在下面的代码中,我使用arrayfor循环分别迭代大小为100,10000和100000的foreach以测量时间。

enter image description here

private static void MeasureTime()
    {
        var array = new int[10000];
        var list = array.ToList();
        Console.WriteLine("Array size: {0}", array.Length);

        Console.WriteLine("Array For loop ......");
        var stopWatch = Stopwatch.StartNew();
        for (int i = 0; i < array.Length; i++)
        {
            Thread.Sleep(1);
        }
        stopWatch.Stop();
        Console.WriteLine("Time take to run the for loop is {0} millisecond", stopWatch.ElapsedMilliseconds);

        Console.WriteLine(" ");
        Console.WriteLine("Array Foreach loop ......");
        var stopWatch1 = Stopwatch.StartNew();
        foreach (var item in array)
        {
            Thread.Sleep(1);
        }
        stopWatch1.Stop();
        Console.WriteLine("Time take to run the foreach loop is {0} millisecond", stopWatch1.ElapsedMilliseconds);

        Console.WriteLine(" ");
        Console.WriteLine("List For loop ......");
        var stopWatch2 = Stopwatch.StartNew();
        for (int i = 0; i < list.Count; i++)
        {
            Thread.Sleep(1);
        }
        stopWatch2.Stop();
        Console.WriteLine("Time take to run the for loop is {0} millisecond", stopWatch2.ElapsedMilliseconds);

        Console.WriteLine(" ");
        Console.WriteLine("List Foreach loop ......");
        var stopWatch3 = Stopwatch.StartNew();
        foreach (var item in list)
        {
            Thread.Sleep(1);
        }
        stopWatch3.Stop();
        Console.WriteLine("Time take to run the foreach loop is {0} millisecond", stopWatch3.ElapsedMilliseconds);
    }

已更新

在@jgauffin建议之后我使用了@johnskeet代码,发现for循环array的速度比跟随的速度快,

  • 阵列的Foreach循环。
  • For list with list。
  • 带有列表的Foreach循环。

请参阅下面的测试结果和代码,

enter image description here

private static void MeasureNewTime()
    {
        var data = new double[Size];
        var rng = new Random();
        for (int i = 0; i < data.Length; i++)
        {
            data[i] = rng.NextDouble();
        }
        Console.WriteLine("Lenght of array: {0}", data.Length);
        Console.WriteLine("No. of iteration: {0}", Iterations);
        Console.WriteLine(" ");
        double correctSum = data.Sum();

        Stopwatch sw = Stopwatch.StartNew();
        for (int i = 0; i < Iterations; i++)
        {
            double sum = 0;
            for (int j = 0; j < data.Length; j++)
            {
                sum += data[j];
            }
            if (Math.Abs(sum - correctSum) > 0.1)
            {
                Console.WriteLine("Summation failed");
                return;
            }
        }
        sw.Stop();
        Console.WriteLine("For loop with Array: {0}", sw.ElapsedMilliseconds);

        sw = Stopwatch.StartNew();
        for (var i = 0; i < Iterations; i++)
        {
            double sum = 0;
            foreach (double d in data)
            {
                sum += d;
            }
            if (Math.Abs(sum - correctSum) > 0.1)
            {
                Console.WriteLine("Summation failed");
                return;
            }
        }
        sw.Stop();
        Console.WriteLine("Foreach loop with Array: {0}", sw.ElapsedMilliseconds);
        Console.WriteLine(" ");

        var dataList = data.ToList();
        sw = Stopwatch.StartNew();
        for (int i = 0; i < Iterations; i++)
        {
            double sum = 0;
            for (int j = 0; j < dataList.Count; j++)
            {
                sum += data[j];
            }
            if (Math.Abs(sum - correctSum) > 0.1)
            {
                Console.WriteLine("Summation failed");
                return;
            }
        }
        sw.Stop();
        Console.WriteLine("For loop with List: {0}", sw.ElapsedMilliseconds);

        sw = Stopwatch.StartNew();
        for (int i = 0; i < Iterations; i++)
        {
            double sum = 0;
            foreach (double d in dataList)
            {
                sum += d;
            }
            if (Math.Abs(sum - correctSum) > 0.1)
            {
                Console.WriteLine("Summation failed");
                return;
            }
        }
        sw.Stop();
        Console.WriteLine("Foreach loop with List: {0}", sw.ElapsedMilliseconds);
    }

答案 26 :(得分:3)

除非您处于特定的速度优化过程中,否则我会说使用哪种方法可以生成最容易阅读和维护的代码。

如果已经设置了迭代器,就像使用其中一个集合类一样,那么foreach是一个很好的选择。如果它是一个整数范围你正在迭代,那么可能更干净。

答案 27 :(得分:2)

完全禁止使用类似for循环的东西似乎有点奇怪。

有一篇有趣的文章here涵盖了两个循环之间的许多性能差异。

我个人会说我发现foreach对for循环更具可读性,但你应该使用最好的手头工作而不必编写额外的长代码来包含foreach循环,如果for循环更合适。 / p>

答案 28 :(得分:2)

我不希望任何人在两者之间找到“巨大”的性能差异。

我想答案取决于您尝试访问的集合是否具有更快的索引器访问实现或更快的IEnumerator访问实现。由于IEnumerator经常使用索引器并且只保存当前索引位置的副本,我希望枚举器访问至少与直接索引访问一样慢或慢,但不是很多。

当然,这个答案并未考虑编译器可能实现的任何优化。

答案 29 :(得分:2)

请记住,for循环和foreach循环并不总是等效的。如果列表更改,列表枚举器将抛出异常,但您不会总是通过正常的for循环获得该警告。如果列表在错误的时间发生变化,您甚至可能会遇到不同的异常。

答案 30 :(得分:1)

我之前做过测试,结果是for循环比foreach循环快得多。原因很简单,foreach循环首先需要为集合实例化IEnumerator

答案 31 :(得分:1)

在我的Windows Mobile项目中,我使用for循环来控制集合。 20个控件花了100毫秒! foreach循环仅使用4 ms。这是一个性能问题......

答案 32 :(得分:1)

我遇到了一个案例,其中foreach方式比For

更快

why foreach is faster than for loop while reading richtextbox lines

我在这个问题上有一个类似于OP的案例。

一个文本框读取大约72K行,我正在访问Lines属性(实际上是一个getter方法)。 (显然通常在winforms中有getter方法不是O(1)。我认为它是O(n),所以文本框越大,从中获取值的时间越长&# 39;属性&#39;。在for循环中我有OP作为for(int i=0;i<textBox1.lines.length;i++) str=textBox1.Lines[i],它真的很慢,因为它每次读取一行时读取整个文本框加上它正在读取整个每次检查条件时都会显示文本框。

Jon Skeet表明你可以一次访问Lines属性(每次迭代甚至一次)。而不是每次迭代两次(这是很多次)。 do string [] strarrlines = textBox1.Lines;并循环通过strarrlines。

但肯定是以相当直观的形式进行for循环并访问Lines属性,这是非常低效的

for (int i = 0; i < richTextBox.Lines.Length; i++)
{
    s = richTextBox.Lines[i];
}

对于文本框或富文本框,它的速度非常慢。

OP,在富文本框上测试该循环,发现&#34; 15000行.for循环需要8分钟才能循环到15000行。而foreach花了几分钟来枚举它。&#34;

该链接的OP发现这个foreach比他上面提到的循环(相同的OP)更有效。就像我一样。

   String s=String.Empty;
   foreach(string str in txtText.Lines)
    {
       s=str;
    }

答案 33 :(得分:1)

我根据for和foreach的收集速度提到了这些细节。

List -For Loop比Foreach Loop快一点

ArrayList - For Loop的速度比Foreach Loop快2倍。

数组 - 两者的速度相同。但是Foreach循环似乎要快一些。

答案 34 :(得分:0)

Ben Watson,“编写高性能.NET代码”的作者:

  

“这些优化对您的计划是否重要?只有您的计划   是CPU绑定的,集合迭代是你的核心部分   处理。正如你所看到的,有些方法可以伤害你   表现,如果你不小心,但这只会是一个问题   您的计划的重要部分开始。我的理念是这样的:   大多数人永远不需要知道这一点,但如果你这样做,那么理解   系统的每一层都很重要,这样你才能做到   聪明的选择“。

最常见的解释可以在这里找到:http://www.codeproject.com/Articles/844781/Digging-Into-NET-Loop-Performance-Bounds-checking

答案 35 :(得分:0)

至少我没有看到我的同事或更高级别的人说,考虑到forforeach之间没有明显的速度差异,这简直荒谬。如果他要求在所有情况下都使用它,同样适用!

答案 36 :(得分:0)

从.NET Framework 4开始,您还可以使用Parallel.For和Parallel.ForEach,如下所述:C# Multithreading Loop with Parallel.For or Parallel.ForEach

答案 37 :(得分:0)

我认为在大多数情况下,它比foreach快一点,但这确实忽略了这一点。我没有提到过的一件事是,在你所谈论的场景中(即大量的网络应用程序),for和foreach之间的性能差异将对网站的性能产生影响。您将受到请求/响应时间和数据库时间的限制,而不是v.foreach。

那就是说,我不明白你对foreach的厌恶。在我看来,在任何一种都可以使用的情况下,foreach通常更清楚。我通常会保留我需要以某种丑陋,非标准的方式遍历集合的情况。

答案 38 :(得分:0)

我需要使用三个嵌套循环(在List<MyCustomType>上)解析一些大数据。我想,使用上面的Rob Fonseca-Ensor的帖子,时间很有趣,并使用 for foreach 来比较差异。

区别在于: foreach(三个foreach嵌套像foreach {foreach {forech {}}}在 171.441 秒内完成了工作,其中{for {for {}}}}在 158.616中完成了秒。

现在13秒大约 13%减少时间 - 这对我来说有点重要。然而,foreach肯定比使用三个索引的fors更具可读性......

答案 39 :(得分:0)

我建议您阅读this以获取具体答案。本文的结论是使用for循环通常比foreach循环更好,更快。