c# foreach for linq的性能差异

本文关键字:性能 linq foreach for | 更新日期: 2023-09-27 17:50:40

我想知道这两者的区别:

:

string[] arrayOld = new string(){"This", "other", "aaa", ...};
string[] arrayNew = new string[arrayOld.lenght];
for(int i = i < arrayOld.lenght; i++){
   arrayNew[i] = arrayOld[i];
}

FOREACH:

string[] arrayOld = new string(){"This", "other", "aaa", ...};
List<string> listNew = new List<string>();
foreach(string val in arrayOld){
   listNew.add(val);
}
string[] arrayNew = listNew.toArray();

LINQ:

string[] arrayOld = new string(){"This", "other", "aaa", ...};
string[] arrayNew = (from val in arrayOld select val).toArray();

我不想复制一个数组…

这个想法是从arrayOld中的对象构造新对象(可以不同于string,可以包含其他属性…)

我需要性能,所以…

什么是最好的选择,为什么?

c# foreach for linq的性能差异

粗略的结果说明了一切:

class Program
{
    static void Main()
    {
        // Warm-up
        Method1();
        Method2();
        Method3();
        const int Count = 1000000;
        var watch = Stopwatch.StartNew();
        for (int i = 0; i < Count; i++)
        {
            Method1();
        }
        watch.Stop();
        Console.WriteLine("Method1: {0} ms", watch.ElapsedMilliseconds);
        watch = Stopwatch.StartNew();
        for (int i = 0; i < Count; i++)
        {
            Method2();
        }
        watch.Stop();
        Console.WriteLine("Method2: {0} ms", watch.ElapsedMilliseconds);
        watch = Stopwatch.StartNew();
        for (int i = 0; i < Count; i++)
        {
            Method3();
        }
        watch.Stop();
        Console.WriteLine("Method3: {0} ms", watch.ElapsedMilliseconds);
    }
    static void Method1()
    {
        string[] arrayOld = new[] { "This", "other", "aaa" };
        string[] arrayNew = new string[arrayOld.Length];
        for (var i = 0; i < arrayOld.Length; i++)
        {
            arrayNew[i] = arrayOld[i];
        }
    }
    static void Method2()
    {
        string[] arrayOld = new[] { "This", "other", "aaa" }; 
        var listNew = new List<string>(arrayOld.Length);
        foreach(var val in arrayOld)
        { 
            listNew.Add(val); 
        } 
        string[] arrayNew = listNew.ToArray();    
    }
    static void Method3()
    {
        string[] arrayOld = new[] { "This", "other", "aaa" }; 
        string[] arrayNew = (from val in arrayOld select val).ToArray();    
    }
}

打印在我的机器上:

Method1: 72 ms
Method2: 187 ms
Method3: 377 ms

以上皆非。

如果你想复制一个数组,我会尝试:

string[] arrayOld = new { "This", "other", "aaa" };
string[] arrayNew = new string[arrayOld.Length];
arrayOld.CopyTo(arrayNew, 0);

在大多数情况下,您应该选择最清楚地表达您正在编写的特定代码的意图的内容。但是,如果它是一个非常深的内部循环,并且您需要在最后一纳秒内完成,我的经验是(使用发布构建代码)对数组的for循环索引明显快于foreach循环,使用LINQ委托比使用循环内逻辑的简单foreach稍微小一些的性能损失。

这些观察结果是基于人工智能中使用的分数计算算法的微优化工作,其中分数函数被评估了很多很多次。具体的瓶颈和改进的程度是使用分析工具确定的,如果您处于这种情况,我强烈建议使用分析工具。瓶颈很少在你认为的地方。

至于性能for可能超过其他人,因为在那里你直接使用索引器,在foreach中你使用Enumerator,所以你有更多的行来执行(GetEnumerator, MoveNext, Current等)。但是它们之间的区别是非常细微的,并且保证了代码的可读性可维护性

对于LINQ是很多更多的工作。但是想想为什么LINQ会更快?它也在内部使用循环。

大多数时候,LINQ会稍微慢一点,因为它引入了开销。如果您非常关心性能,请不要使用LINQ。使用LINQ是因为你想要更短、更易读、更易维护的代码。


也就是说,如果你对性能太敏感,想要大量生产每一个最后的时钟周期,那么你可能想要在 unsafe 上下文中使用C风格的指针fixed变量

最快的方法是Array.Copy

string[] arrayOld = new string[] { "This", "other", "aaa" };
string[] arrayNew = new string[arrayOld.Length];
Array.Copy(arrayOld, arrayNew, arrayOld.Length);

Daniel A. White的评论,不确定是不是认真的,但他是对的。你听说过秒表课程吗?

我认为现在是时候让你花点时间,学习如何为自己判断表现。如果我们中的一个人真的想正确地回答你的问题,我们将不得不编写性能检查代码,这就是为什么你的问题可能会收到这么多的反对票。因为你应该这么做。但是我们会帮助你的:)

您可以使用StopWatch类以及许多其他技术(计算时钟周期/迭代)来确定哪种方法具有最佳性能。

http://msdn.microsoft.com/en-us/library/system.diagnostics.stopwatch.aspx

using System;
using System.Diagnostics;
using System.Threading;
class Program
{
    static void Main(string[] args)
    {
        Stopwatch stopWatch = new Stopwatch();
        stopWatch.Start();
        Thread.Sleep(10000); // put one of your three scenarios here
        stopWatch.Stop();
        // Get the elapsed time as a TimeSpan value.
        TimeSpan ts = stopWatch.Elapsed;
        // Format and display the TimeSpan value.
        string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}",
            ts.Hours, ts.Minutes, ts.Seconds,
            ts.Milliseconds / 10);
        Console.WriteLine("RunTime " + elapsedTime);
    }
}

(来自msdn的代码片段)