LINQ语句比“ foreach”循环快吗?


124

我正在编写一个“网格渲染”管理器,并认为最好将所有使用同一着色器的网格进行分组,然后在我通过该着色器通道时进行渲染。

我目前正在使用foreach循环,但想知道使用LINQ是否可以提高性能?



1
请考虑将@MarcGravell的答案设置为已接受的答案,在某些情况下,例如linq到sql,其中linq比for / foreach更快。
paqogomez 2014年

Answers:


222

为什么LINQ应该更快?它还在内部使用循环。

在大多数情况下,LINQ会慢一些,因为它会引入开销。如果您非常关心性能,请不要使用LINQ。使用LINQ是因为您需要较短的更好可读性和可维护性的代码。


7
因此,您的经验是LINQ更快并且使代码更难阅读和维护?请解释。
codymanix 2011年

87
我想你落后了。他说LINQ更慢。这是由于头顶。他还说LINQ更易于阅读和维护。
约瑟夫·麦金太尔

5
抱歉。同时,我们在很多方面比较了linq和forfor或foreach性能,并且大多数时候linq更快。
Offler

34
老实说,foreach循环比LINQ方法更具可读性。我使用LINQ是因为它很酷:)
LuckyLikey 2015年

4
是的,但是在某些情况下LINQ可能会真正提高可读性,所以请忘记我的盲目评论<3
LuckyLikey

59

LINQ-to-Objects 通常会增加一些边际开销(多个迭代器等)。它仍然必须执行循环,具有委托调用,并且通常将不得不做一些额外的解引用才能获取捕获的变量等。在大多数代码中,这实际上是不可检测的,并且简单易懂的代码所提供的更多

对于LINQ-to-SQL之类的其他LINQ提供程序,则由于查询可以在服务器上进行过滤,因此它应该比flat 过滤器好得多foreach,但是"select * from foo" 无论如何,您很可能不会做完所有的覆盖,因此不一定公平比较。

Re PLINQ; 并行性可以减少经过的时间,但是由于线程管理等开销,总的CPU时间通常会稍微增加。


在另一个答案中,您暗示没有在内存集合中使用LINQ-例如List<Foo>;相反,我应该foreach在这些集合上使用一个块。foreach在这些情况下使用的建议确实有意义。我的担心:foreach 如果我检测到性能问题,是否应该仅用LINQ查询替换?展望未来,我将考虑第foreach一个。
IAbstract 2012年


15

LINQ现在比较慢,但是在某些时候可能会变快。LINQ的优点在于您不必关心它的工作方式。如果想出一种新方法非常快,那么Microsoft的人员甚至可以在不告诉您的情况下实施该新方法,并且您的代码会更快。

但是,更重要的是,LINQ更加易于阅读。那应该是足够的理由。


3
我喜欢“ Microsoft可以实现它”这一行,这是可能的,我的意思是不升级框架就可以吗?
Shrivallabh 2015年

1
LINQ永远不会真正比本地实现快,因为到最后,它会转换为本地实现。没有特殊的LINQ CPU指令和LINQ寄存器可用于转换更快的LINQ机器代码-如果有的话,非LINQ代码也将使用它们。
mg30rg

事实并非如此,某些时候某些链接操作可能会变成多线程,甚至在某些时候甚至会利用GPU。
John Stock



5

我对这个问题很感兴趣,所以我刚才做了一个测试。在Intel(R)i3-2328M CPU @ 2.20GHz,2200 Mhz,2个Core和具有8GB ram的Microsoft Windows 7 Ultimate上使用.NET Framework 4.5.2。

看起来LINQ可能比每个循环都要快。这是我得到的结果:

Exists = True
Time   = 174
Exists = True
Time   = 149

如果你们中的某些人可以将此代码复制并粘贴到控制台应用程序中并进行测试,那将很有趣。在使用对象(员工)进行测试之前,我尝试使用整数进行相同的测试。LINQ在那里也更快。

public class Program
{
    public class Employee
    {
        public int id;
        public string name;
        public string lastname;
        public DateTime dateOfBirth;

        public Employee(int id,string name,string lastname,DateTime dateOfBirth)
        {
            this.id = id;
            this.name = name;
            this.lastname = lastname;
            this.dateOfBirth = dateOfBirth;

        }
    }

    public static void Main() => StartObjTest();

    #region object test

    public static void StartObjTest()
    {
        List<Employee> items = new List<Employee>();

        for (int i = 0; i < 10000000; i++)
        {
            items.Add(new Employee(i,"name" + i,"lastname" + i,DateTime.Today));
        }

        Test3(items, items.Count-100);
        Test4(items, items.Count - 100);

        Console.Read();
    }


    public static void Test3(List<Employee> items, int idToCheck)
    {

        Stopwatch s = new Stopwatch();
        s.Start();

        bool exists = false;
        foreach (var item in items)
        {
            if (item.id == idToCheck)
            {
                exists = true;
                break;
            }
        }

        Console.WriteLine("Exists=" + exists);
        Console.WriteLine("Time=" + s.ElapsedMilliseconds);

    }

    public static void Test4(List<Employee> items, int idToCheck)
    {

        Stopwatch s = new Stopwatch();
        s.Start();

        bool exists = items.Exists(e => e.id == idToCheck);

        Console.WriteLine("Exists=" + exists);
        Console.WriteLine("Time=" + s.ElapsedMilliseconds);

    }

    #endregion


    #region int test
    public static void StartIntTest()
    {
        List<int> items = new List<int>();

        for (int i = 0; i < 10000000; i++)
        {
            items.Add(i);
        }

        Test1(items, -100);
        Test2(items, -100);

        Console.Read();
    }

    public static void Test1(List<int> items,int itemToCheck)
    {

        Stopwatch s = new Stopwatch();
        s.Start();

        bool exists = false;
        foreach (var item in items)
        {
            if (item == itemToCheck)
            {
                exists = true;
                break;
            }
        }

        Console.WriteLine("Exists=" + exists);
        Console.WriteLine("Time=" + s.ElapsedMilliseconds);

    }

    public static void Test2(List<int> items, int itemToCheck)
    {

        Stopwatch s = new Stopwatch();
        s.Start();

        bool exists = items.Contains(itemToCheck);

        Console.WriteLine("Exists=" + exists);
        Console.WriteLine("Time=" + s.ElapsedMilliseconds);

    }

    #endregion

}

这就是我得到的:存在=真实时间= 274存在=真实时间= 314
PmanAce

2
您是否考虑过先进行linq,然后再进行一次,可能也会有所不同
穆罕默德·马穆尔·汗

3
有趣。我Exists=True Time=184 Exists=True Time=135在Apache Gaming笔记本电脑(Win 10,C#7.3)上得到了它。编译并以调试模式运行。如果我逆转考试,我会得到Exists=True Time=158 Exists=True Time=194。我猜似乎Linq更优化了。
詹姆斯·威尔金斯

1
这篇文章中关于对象测试存在误解。虽然List.Exists和.Contains的性能似乎比foreach更好,但绝对很有趣。重要的是要注意,.Exists不是LINQ to Entity方法,并且只能在列表上使用,它的LINQ等效方法.Any()的性能肯定比foreach慢。
AbdulG

3

这实际上是一个非常复杂的问题。Linq使某些事情变得很容易做到,如果您自己实现它们,则可能会遇到麻烦(例如linq .Except())。这尤其适用于PLinq,尤其适用于PLinq实施的并行聚合。

通常,对于相同的代码,由于委托调用的开销,linq会变慢。

但是,如果要处理大量数据,并对元素进行相对简单的计算,则在以下情况下,您将获得巨大的性能提升:

  1. 您使用数组来存储数据。
  2. 您使用for循环访问每个元素(与foreach或linq相对)。

    • 注意:基准测试时,请大家记住-如果您对两个连续的测试使用相同的阵列/列表,则CPU缓存将使第二个测试更快。*
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.