简体   繁体   English

在某些情况下,Foreach比For更快?

[英]Foreach MUCH faster than For in some cases?

I don't like premature optimization but I was curious while doing a simple task so I added a stopwatch. 我不喜欢过早的优化,但是在做一个简单的任务时很好奇,所以我添加了秒表。 I don't understand how the difference can be so big. 我不明白差别有多大。

Array of strings (7 characters) each [richtextbox.text]. 每个[richtextbox.text]的字符串数组(7个字符)。

Length of array: 5500 elements. 数组长度:5500个元素。

Foreach Elapsed Time: 0.0015 seconds 每次经过时间: 0.0015秒

For Elapsed Time: 9.757 seconds 经过的时间: 9.757秒

For: 对于:

if (chkLineBreaks.Checked)
{
    for (int i = 0; i < txtInput.Lines.Length; i++)
    {
        outputStringBuilder.Append($@"'{txtInput.Lines[i]}',");
    }
}

Foreach: 的foreach:

foreach (var line in txtInput.Lines)
{
    outputStringBuilder.Append($@"'{line}',");
    if (chkLineBreaks.Checked)
        outputStringBuilder.AppendLine();
}

From what I've read the difference should be negligible and the For would slightly faster. 从我阅读的内容来看,差异应该可以忽略不计,For的速度会稍快一些。

Even more, the foreach has a condition in each iteration (unless it is being 'hoisted' up before the loop. 更重要的是,foreach在每次迭代中都有一个条件(除非在循环之前将其“吊起”)。

What is going on here? 这里发生了什么?

Edit: I've changed the foreach code to: 编辑:我已经将foreach代码更改为:

int i = 0;
foreach (var line in txtInput.Lines)
{
    outputStringBuilder.Append($@"'{txtInput.Lines[i]}',");
    i++;
}

So it is now doing the same thing. 因此,它现在正在做同样的事情。 It is taking 4.625 seconds .. still about half of the time for the FOR FOR仍需要4.625秒的时间。

Also I know that I can extract the array outside the loop but this is not what I am testing here :) 我也知道我可以在循环外提取数组,但这不是我在这里测试的内容:)

Edit #2: This is the whole code for that section: 编辑#2:这是该部分的全部代码:

Stopwatch sw = new Stopwatch();
        sw.Start();
        //    for (int i = 0; i < txtInput.Lines.Length; i++)
        //    {
        //        outputStringBuilder.Append($@"'{txtInput.Lines[i]}',");
        //    }
        int i = 0;
        foreach (var line in txtInput.Lines)
        {
            outputStringBuilder.Append($@"'{txtInput.Lines[i]}',");
            i++;
        }
        MessageBox.Show(sw.Elapsed.ToString());

The issue is that txtInput.Lines is executing many times (once per line) in your for loop (due to use of txtInput.Lines[i] ). 问题是txtInput.Linesfor循环中执行了很多次(每行一次)(由于使用txtInput.Lines[i] )。 So for every line of the file you are saying 'OK, please parse this textbox into multiple lines - and then get me the nth line' - and the parsing is the killer bit. 因此,对于文件的每一行,您说的是“好的,请将该文本框解析为多行-然后让我进入第n行”,而解析是杀手bit。

For a fairer comparison: 为了更公平的比较:

if (chkLineBreaks.Checked)
{
    var lines = txtInput.Lines;
    for (int i = 0; i < lines.Length; i++)
    {
        outputStringBuilder.Append($@"'{lines[i]}',");
    }
}

this way the Lines call is done only once (ie equivalent to the foreach scenario). 这样, Lines调用仅执行一次(即相当于foreach方案)。

One way to spot these kinds of issues is to compare the timings. 发现此类问题的一种方法是比较时间安排。 The slow one is about 6K slower than the fast one, and you have 5.5K entries. 慢速的速度比快速的速度慢大约6K,并且您有5.5K条目。 Since 5.5K and 6K are very similar numbers, it may prompt you to think 'am I doing something in the loop that I really shouldn't?' 由于5.5K和6K是非常相似的数字,因此它可能会提示您思考:“我是否正在循环中做某些我本不应该做的事情?”

The compiled code sees very little difference between a for and a foreach statement when traversing an array (or list). 在遍历数组(或列表)时, forforeach语句之间的编译后代码差别很小。

Consider this simple code the writes out a list of strings three different ways: 考虑一下这个简单的代码,它以三种不同的方式写出一个字符串列表:

class Program
{
    static void Main(string[] args)
    {
        var list = Enum.GetNames(typeof(System.UriComponents));

        // 1. for each
        foreach (var item in list)
        {
            Console.WriteLine(item);
        }
        // 2. for loop
        for (int i = 0; i<list.Length; i++)
        {
            Console.WriteLine(list[i]);
        }
        // 3. LINQ
        Console.WriteLine(string.Join(Environment.NewLine, list));
    }
}

Now look at the MSIL compiled code, translated back into C# using ILSpy or DotNetPeek . 现在看一下MSIL编译的代码,使用ILSpyDotNetPeek将其转换回C#

// ConsoleApplication1.Program
private static void Main(string[] args)
{
    string[] list = Enum.GetNames(typeof(UriComponents));
    string[] array = list;
    for (int j = 0; j < array.Length; j++)
    {
        string item = array[j];
        Console.WriteLine(item);
    }
    for (int i = 0; i < list.Length; i++)
    {
        Console.WriteLine(list[i]);
    }
    Console.WriteLine(string.Join(Environment.NewLine, list));
}

See the two for loops. 请参阅两个循环。 The foreach statement became a for loop by the compiler. foreach语句成为编译器的for循环。 As far as the string.Join() statement it calls the SZArrayEnumerator which holds a reference to the array, and the current index value. string.Join()语句而言,它调用SZArrayEnumerator ,它包含对数组的引用以及当前索引值。 At each .MoveNext() call the index is incremented and a new value returned. 在每次.MoveNext()调用时,索引都会递增,并返回一个新值。 Basically, it is equivalent to the following: 基本上,它等效于以下内容:

int i = 0;
while (i<list.Length)
{
    Console.WriteLine(list[i]);
    i++;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM