簡體   English   中英

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

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

我不喜歡過早的優化,但是在做一個簡單的任務時很好奇,所以我添加了秒表。 我不明白差別有多大。

每個[richtextbox.text]的字符串數組(7個字符)。

數組長度:5500個元素。

每次經過時間: 0.0015秒

經過的時間: 9.757秒

對於:

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

的foreach:

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

從我閱讀的內容來看,差異應該可以忽略不計,For的速度會稍快一些。

更重要的是,foreach在每次迭代中都有一個條件(除非在循環之前將其“吊起”)。

這里發生了什么?

編輯:我已經將foreach代碼更改為:

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

因此,它現在正在做同樣的事情。 FOR仍需要4.625秒的時間。

我也知道我可以在循環外提取數組,但這不是我在這里測試的內容:)

編輯#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());

問題是txtInput.Linesfor循環中執行了很多次(每行一次)(由於使用txtInput.Lines[i] )。 因此,對於文件的每一行,您說的是“好的,請將該文本框解析為多行-然后讓我進入第n行”,而解析是殺手bit。

為了更公平的比較:

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

這樣, Lines調用僅執行一次(即相當於foreach方案)。

發現此類問題的一種方法是比較時間安排。 慢速的速度比快速的速度慢大約6K,並且您有5.5K條目。 由於5.5K和6K是非常相似的數字,因此它可能會提示您思考:“我是否正在循環中做某些我本不應該做的事情?”

在遍歷數組(或列表)時, forforeach語句之間的編譯后代碼差別很小。

考慮一下這個簡單的代碼,它以三種不同的方式寫出一個字符串列表:

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

現在看一下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));
}

請參閱兩個循環。 foreach語句成為編譯器的for循環。 string.Join()語句而言,它調用SZArrayEnumerator ,它包含對數組的引用以及當前索引值。 在每次.MoveNext()調用時,索引都會遞增,並返回一個新值。 基本上,它等效於以下內容:

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