在c# / VB.NET/。哪个循环运行得更快,for还是foreach?

自从很久以前我读到for循环比foreach循环工作得快,我就认为它适用于所有集合、泛型集合、所有数组等。

我搜索了谷歌,找到了几篇文章,但大多数都是不确定的(阅读文章评论),而且是开放式的。

理想的情况是列出每种情况以及最佳解决方案。

例如(这只是一个例子):

用于迭代1000+的数组 字符串- for比foreach好 对于迭代IList(非泛型)字符串- foreach更好 比

在网上找到了一些相同的参考资料:

由Emmanuel Schanzer撰写的原创文章 CodeProject FOREACH Vs. FOR 博客——去博客还是不去博客,这是个问题 ASP。NET论坛- NET 1.1 c# for vs foreach

(编辑)

除了可读性之外,我对事实和数据真的很感兴趣。在某些应用中,最后一英里的性能优化确实很重要。


当前回答

首先是对德米特里的回答(现已删除)的反诉。对于数组,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循环,因为它使代码更简单——因为可读性几乎总是重要的,而微优化很少是。

其他回答

影响性能的是您在循环中所做的事情,而不是实际的循环构造(假设您的情况不平凡)。

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

至少我没有看到我的同事或上级这么说,考虑到for和foreach之间没有显著的速度差异,这是荒谬的。如果他要求在所有情况下都使用它,同样适用!

我发现foreach循环迭代列表更快。下面是我的测试结果。在下面的代码中,我分别迭代一个大小为100、10000和100000的数组,使用for和foreach循环来测量时间。

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循环比下面的更快,

Foreach循环与数组。 For带列表的循环。 Foreach循环与列表。

请看下面我的测试结果和代码,

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);
    }
    internal static void Test()
    {
        int LOOP_LENGTH = 10000000;
        Random random = new Random((int)DateTime.Now.ToFileTime());

        {
            Dictionary<int, int> dict = new Dictionary<int, int>();
            long first_memory = GC.GetTotalMemory(true);
            var stopWatch = Stopwatch.StartNew();
            for (int i = 0; i < 64; i++)
            {
                dict.Add(i, i);
            }

            for (int i = 0; i < LOOP_LENGTH; i++)
            {
                for (int k = 0; k < dict.Count; k++)
                {
                    if (dict[k] > 1000000) Console.WriteLine("Test");
                }
            }
            stopWatch.Stop();
            var last_memory = GC.GetTotalMemory(true);
            Console.WriteLine($"Dictionary for T:{stopWatch.Elapsed.TotalSeconds}s\t M:{last_memory - first_memory}");

            GC.Collect();
        }


        {
            Dictionary<int, int> dict = new Dictionary<int, int>();
            long first_memory = GC.GetTotalMemory(true);
            var stopWatch = Stopwatch.StartNew();
            for (int i = 0; i < 64; i++)
            {
                dict.Add(i, i);
            }

            for (int i = 0; i < LOOP_LENGTH; i++)
            {
                foreach (var item in dict)
                {
                    if (item.Value > 1000000) Console.WriteLine("Test");
                }
            }
            stopWatch.Stop();
            var last_memory = GC.GetTotalMemory(true);
            Console.WriteLine($"Dictionary foreach T:{stopWatch.Elapsed.TotalSeconds}s\t M:{last_memory - first_memory}");

            GC.Collect();
        }

        {
            Dictionary<int, int> dict = new Dictionary<int, int>();
            long first_memory = GC.GetTotalMemory(true);
            var stopWatch = Stopwatch.StartNew();
            for (int i = 0; i < 64; i++)
            {
                dict.Add(i, i);
            }

            for (int i = 0; i < LOOP_LENGTH; i++)
            {
                foreach (var item in dict.Values)
                {
                    if (item > 1000000) Console.WriteLine("Test");
                }
            }
            stopWatch.Stop();
            var last_memory = GC.GetTotalMemory(true);
            Console.WriteLine($"Dictionary foreach values T:{stopWatch.Elapsed.TotalSeconds}s\t M:{last_memory - first_memory}");

            GC.Collect();
        }


        {
            List<int> dict = new List<int>();
            long first_memory = GC.GetTotalMemory(true);
            var stopWatch = Stopwatch.StartNew();
            for (int i = 0; i < 64; i++)
            {
                dict.Add(i);
            }

            for (int i = 0; i < LOOP_LENGTH; i++)
            {
                for (int k = 0; k < dict.Count; k++)
                {
                    if (dict[k] > 1000000) Console.WriteLine("Test");
                }
            }
            stopWatch.Stop();
            var last_memory = GC.GetTotalMemory(true);
            Console.WriteLine($"list for T:{stopWatch.Elapsed.TotalSeconds}s\t M:{last_memory - first_memory}");

            GC.Collect();
        }


        {
            List<int> dict = new List<int>();
            long first_memory = GC.GetTotalMemory(true);
            var stopWatch = Stopwatch.StartNew();
            for (int i = 0; i < 64; i++)
            {
                dict.Add(i);
            }

            for (int i = 0; i < LOOP_LENGTH; i++)
            {
                foreach (var item in dict)
                {
                    if (item > 1000000) Console.WriteLine("Test");
                }
            }
            stopWatch.Stop();
            var last_memory = GC.GetTotalMemory(true);
            Console.WriteLine($"list foreach T:{stopWatch.Elapsed.TotalSeconds}s\t M:{last_memory - first_memory}");

            GC.Collect();
        }
    }

T:10.1957728s M:2080的字典 字典T:10.5900586 M:1952 字典foreach值T:3.8294776s M:2088 T:3.7981471s M:320 T:4.4861377s M:648