¿Es una declaración LINQ más rápida que un bucle 'foreach'?

124

Estoy escribiendo un administrador de representación de mallas y pensé que sería una buena idea agrupar todas las mallas que usan el mismo sombreador y luego renderizarlas mientras estoy en esa pasada de sombreado.

Actualmente estoy usando un foreachbucle, pero me preguntaba si el uso de LINQ podría aumentar el rendimiento.

Neil Knight
fuente
1
posible duplicado del rendimiento
Daniel Earwicker
1
Considere configurar la respuesta de @ MarcGravell a la aceptada, hay situaciones, linq a sql, por ejemplo, donde linq es más rápido que for / foreach.
paqogomez

Respuestas:

222

¿Por qué LINQ debería ser más rápido? También utiliza bucles internamente.

La mayoría de las veces, LINQ será un poco más lento porque introduce gastos generales. No use LINQ si le importa mucho el rendimiento. Utilice LINQ porque desea un código más corto, más legible y fácil de mantener.

codymanix
fuente
7
Entonces, ¿su experiencia es que LINQ es más rápido y hace que el código sea más difícil de leer y mantener? Por favor explique.
codymanix
87
Creo que lo tenías al revés. Dice que LINQ es MÁS LENTO. Esto se debe a la sobrecarga. También dice que LINQ es más fácil de leer y mantener.
Joseph McIntyre
5
Lo siento. Mientras tanto, teníamos muchas cosas en las que comparábamos linq y para cada rendimiento, y la mayoría de las veces linq era más rápido.
Oferta
34
Para ser honesto, en mi opinión, un bucle foreach es más legible que su método LINQ. Uso LINQ porque es genial :)
LuckyLikey
4
Sí, pero en algunos casos LINQ realmente puede mejorar la legibilidad, así que olvídate de mi comentario sin sentido <3
LuckyLikey
59

LINQ-to-Objects generalmente agregará algunos gastos generales marginales (múltiples iteradores, etc.). Todavía tiene que hacer los bucles y tiene invocaciones de delegado, y generalmente tendrá que hacer algunas desreferencias adicionales para obtener las variables capturadas, etc. En la mayoría del código, esto será virtualmente indetectable, y más de ofrece el código más simple de entender.

Con otros proveedores de LINQ como LINQ-to-SQL, dado que la consulta puede filtrarse en el servidor, debería ser mucho mejor que una plana foreach, pero lo más probable es que no lo hubiera hecho de "select * from foo" todos modos , por lo que no es necesariamente una justa comparación.

Re PLINQ; El paralelismo puede reducir el tiempo transcurrido , pero el tiempo total de la CPU generalmente aumentará un poco debido a los gastos generales de administración de subprocesos, etc.

Marc Gravell
fuente
En otra respuesta, aludió a no usar LINQ en colecciones en memoria, por ejemplo List<Foo>; en su lugar, debería usar un foreachbloque en estas colecciones. La recomendación de usar foreachen estos contextos tiene sentido. Mi preocupación: ¿debería reemplazar las consultas LINQ con solo foreach si detecto un problema de rendimiento? De cara al futuro, consideraré el foreachprimero.
IAbstract
19

Creo que es mejor usar LINQ sobre un foreachbucle, porque te brinda un código mucho más limpio y fácil de entender. Pero LINQ es más lento que foreach. Para obtener más información, consulte el artículo LINQ vs FOREACH vs FOR Loop Performance .

Pranay Rana
fuente
15

LINQ es más lento ahora, pero podría ser más rápido en algún momento. Lo bueno de LINQ es que no tiene que preocuparse por cómo funciona. Si se piensa en un nuevo método que sea increíblemente rápido, la gente de Microsoft puede implementarlo sin siquiera decírselo y su código será mucho más rápido.

Sin embargo, lo que es más importante, LINQ es mucho más fácil de leer. Esa debería ser razón suficiente.

Jouke van der Maas
fuente
3
Me gusta la línea "Microsoft puede implementarlo", ¿es posible? Quiero decir, ¿es posible sin actualizar el marco?
Shrivallabh
1
LINQ nunca será realmente más rápido que la implementación nativa, ya que al final del día, se traduce en la implementación nativa. No hay instrucciones especiales de CPU LINQ ni registros LINQ que se puedan usar para traducir código de máquina LINQ más rápido, y si los hubiera, también serían usados ​​por código que no sea LINQ.
mg30rg
No es cierto, en algún momento, ciertas operaciones de enlace pueden convertirse en multiproceso o incluso utilizar la GPU en algún momento.
John Stock
9

Es posible que obtenga un aumento de rendimiento si usa LINQ paralelo para múltiples núcleos. Consulte Parallel LINQ (PLINQ) (MSDN).

mcintyre321
fuente
5

Estaba interesado en esta pregunta, así que hice una prueba hace un momento. Usando .NET Framework 4.5.2 en una CPU Intel (R) Core (TM) i3-2328M a 2.20GHz, 2200 Mhz, 2 Core (s) con 8GB de ram con Microsoft Windows 7 Ultimate.

Parece que LINQ podría ser más rápido que para cada bucle. Estos son los resultados que obtuve:

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

Sería interesante si algunos de ustedes pudieran copiar y pegar este código en una aplicación de consola y probarlo también. Antes de probar con un objeto (Empleado) intenté la misma prueba con números enteros. LINQ también fue más rápido allí.

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

}
Theo Kand.
fuente
Esto es lo que obtuve: Existe = True Time = 274 Exists = True Time = 314
PmanAce
2
¿Ha considerado hacer el linq primero y foreach después, podría hacer alguna diferencia también
Muhammad Mamoor Khan
3
Interesante. Lo tengo Exists=True Time=184 Exists=True Time=135en una computadora portátil Apache Gaming (Win 10, C # 7.3). Compilado y ejecutado en modo de depuración. Si revierto las pruebas que obtengo Exists=True Time=158 Exists=True Time=194. Parece que Linq está más optimizado, supongo.
James Wilkins
1
Hay un malentendido en esta publicación con respecto a la prueba de objetos. Si bien es definitivamente interesante que List.Exists y .Contains parecen funcionar mejor que foreach. Es importante tener en cuenta que .Exists no es un método de enlace con las entidades y solo funcionará en listas, su método equivalente de linq, .Any (), definitivamente funciona más lento que el foreach.
AbdulG
3

En realidad, esta es una pregunta bastante compleja. Linq hace que ciertas cosas sean muy fáciles de hacer, que si las implementa usted mismo, podría tropezar (por ejemplo, linq .Except ()). Esto se aplica particularmente a PLinq, y especialmente a la agregación paralela implementada por PLinq.

En general, para código idéntico, linq será más lento, debido a la sobrecarga de la invocación de delegados.

Sin embargo, si está procesando una gran variedad de datos y aplicando cálculos relativamente simples a los elementos, obtendrá un gran aumento de rendimiento si:

  1. Utiliza una matriz para almacenar los datos.
  2. Utiliza un bucle for para acceder a cada elemento (a diferencia de foreach o linq).

    • Nota: Al realizar la evaluación comparativa, recuerden todos: si usa la misma matriz / lista para dos pruebas consecutivas, la caché de la CPU hará que la segunda sea más rápida. *
Adam Brown
fuente