首页 文章

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

提问于
浏览
289

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

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

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

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

例如(只是它应该是一个例子):

  • 用于迭代1000个字符串的数组 - for 优于 foreach

  • 用于迭代 IList (非泛型)字符串 - foreach 优于 for

网上发现了一些相同的参考文献:

[编辑]

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

30 回答

  • 3

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

    不,如果你的老板在微观管理方面达到了告诉你使用什么编程语言的水平,你真的没什么可说的 . 抱歉 .

  • 7

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

    此外,它适用于任何 IEnumerable - 而不仅仅是索引器的东西 .

  • 12

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

    List上的for循环比List上的foreach循环便宜2倍多 . 在数组上循环比在List上循环便宜约2倍 . 因此,使用for循环on array比使用foreach在List上循环便宜5倍(我相信,这就是我们所做的) .

  • 4

    foreach loops demonstrate more specific intent than for loops .

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

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

    但是,如果需要使用元素的索引,那么当然应该允许使用 for 循环 . 但是,如果您不需要使用索引,那么使用索引会使代码混乱 .

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

  • 1

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

  • 6

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

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

    我个人会说我发现foreach在for循环中更具可读性但你应该使用最好的手头工作而不必编写超长代码如果for循环更合适,则包含foreach循环 .

  • 6

    首先,反诉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循环,它使代码更简单 - 因为可读性几乎总是重要的,而微优化很少 .

  • 2

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

    为了准确,使用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
    

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

  • 143

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

    此外, foreach 可能会在某些情况下进行优化 . 例如,链接列表可能是索引器可怕的,但它可能会很快 foreach . 实际上,标准 LinkedList<T> 甚至没有为此提供索引器 .

  • 48

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

  • 153

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

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

  • 3

    有很好的理由 prefer foreach 循环超过 for 循环 . 如果你可以使用 foreach 循环,你的老板是对的 .

    但是,并非每次迭代都只是逐个按顺序遍历列表 . 如果他是禁止的,那是错的 .

    如果我你,我会做的是 turn all of your natural for loops into recursion . 那_876070也是一个很好的心理练习 .

  • 2

    Jeffrey Richter在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

  • 3

    这是荒唐的 . 没有令人信服的理由禁止for循环,性能方面或其他方面 .

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

  • 3

    在使用对象集合的情况下, foreach 更好,但如果增加数字,则 for 循环更好 .

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

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

    但它肯定表现不佳,与 for 相比,它实际上表现更差 .

  • 290

    这应该可以节省你:

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

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

  • 7

    当你循环遍历数组,列表等常见结构时, forforeach -loop的速度差异很小,并且对集合进行 LINQ 查询几乎总是稍微慢一点,尽管写起来更好!正如其他海报所说,追求表现力而不是毫秒的额外表现 .

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

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

    foreach (int i in myCollection)
    

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

    for (int i = 0; i < myCollection.Length, i++)
    
  • 11

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

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

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

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

  • 32

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

    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) .

  • 5

    这是你在循环中做的影响性能的东西,而不是实际的循环结构(假设你的情况是非平凡的) .

  • 3

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

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

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

  • 2

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

  • 4

    我找到了循环 List fasterforeach 循环 . 请参阅下面的测试结果 . 在下面的代码中,我使用 forforeach 循环分别迭代大小为100,10000和100000的 array 以测量时间 .

    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代码,发现带有 arrayfor 循环比以下更快,

    • 带数组的Foreach循环 .

    • For循环列表 .

    • 带有列表的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);
        }
    
  • 5

    因为它有更简单的逻辑实现所以它比foreach更快 .

  • 11

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

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

  • 48

    Jeffrey Richter谈到了最近一次播客的for和foreach之间的性能差异:http://pixel8.infragistics.com/shows/everything.aspx#Episode:9317

  • 30

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

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

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

  • 9

    真的用他的头拧紧,然后换一个IQueryable .foreach关闭:

    myList.ForEach(c => Console.WriteLine(c.ToString());

    大声笑

  • 7

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

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

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

  • 30

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

相关问题