Un'istruzione LINQ è più veloce di un ciclo "foreach"?


124

Sto scrivendo un gestore di rendering mesh e ho pensato che sarebbe stata una buona idea raggruppare tutte le mesh che utilizzano lo stesso shader e quindi renderizzarle mentre sono in quel passaggio dello shader.

Attualmente sto utilizzando un foreachloop, ma mi chiedevo se l'utilizzo di LINQ potrebbe darmi un aumento delle prestazioni?



1
Si prega di considerare di impostare la risposta di @ MarcGravell a quella accettata, ci sono situazioni, ad esempio da linq a sql, in cui linq è più veloce di for / foreach.
paqogomez

Risposte:


222

Perché LINQ dovrebbe essere più veloce? Utilizza anche i loop internamente.

La maggior parte delle volte, LINQ sarà un po 'più lento perché introduce overhead. Non utilizzare LINQ se ti interessano molto le prestazioni. Usa LINQ perché desideri un codice più breve, leggibile e gestibile.


7
Quindi la tua esperienza è che LINQ è più veloce e rende il codice più difficile da leggere e da mantenere? Spiega per favore.
codymanix

87
Penso che tu l'abbia fatto al contrario. Sta dicendo che LINQ è PIÙ LENTO. Ciò è dovuto al sovraccarico. Sta anche dicendo che LINQ è più facile da leggere e mantenere.
Joseph McIntyre,

5
Scusate. Nel frattempo abbiamo confrontato molte cose con linq e for or foreach, e la maggior parte delle volte linq era più veloce.
Offler

34
Ad essere onesti, a mio parere, un ciclo foreach è più leggibile del suo metodo LINQ. Uso LINQ perché è fantastico :)
LuckyLikey

4
Sì, ma in alcuni casi LINQ può davvero migliorare la leggibilità, quindi dimentica il mio commento insensato <3
LuckyLikey

59

LINQ-to-Objects in genere aggiungerà alcuni overhead marginali (più iteratori, ecc.). Deve ancora fare i cicli e ha invocazioni di delegati, e generalmente dovrà fare qualche dereferenziazione extra per ottenere le variabili catturate, ecc. Nella maggior parte del codice questo sarà virtualmente non rilevabile e più che consentito dal codice più semplice da capire.

Con altri fornitori di LINQ come LINQ to SQL, quindi dal momento che la query può filtrare a livello di server dovrebbe essere molto meglio di una TV foreach, ma molto probabilmente non avrebbe fatto una coperta "select * from foo" in ogni caso , in modo che non è necessariamente una fiera confronto.

Re PLINQ; il parallelismo può ridurre il tempo trascorso , ma il tempo totale della CPU di solito aumenta leggermente a causa dei costi generali della gestione dei thread, ecc.


In un'altra risposta hai accennato a non usare LINQ su raccolte in memoria, ad esempio List<Foo>; invece, dovrei usare un foreachblocco su queste raccolte. La raccomandazione da utilizzare foreachin questi contesti ha senso. La mia preoccupazione: devo sostituire le query LINQ solo con foreach se rilevo un problema di prestazioni? Andando avanti, prenderò in considerazione il foreachprimo.
IAbstract


15

LINQ è più lento ora, ma potrebbe diventare più veloce a un certo punto. L'aspetto positivo di LINQ è che non devi preoccuparti di come funziona. Se viene ideato un nuovo metodo che sia incredibilmente veloce, le persone in Microsoft possono implementarlo senza nemmeno dirtelo e il tuo codice sarebbe molto più veloce.

Ancora più importante, però, LINQ è molto più facile da leggere. Questo dovrebbe essere un motivo sufficiente.


3
Mi piace la linea "Microsoft può implementarlo" è possibile, voglio dire è possibile senza aggiornare il framework?
Shrivallabh

1
LINQ non diventerà mai veramente più veloce dell'implementazione nativa, poiché alla fine della giornata si traduce nell'implementazione nativa. Non ci sono istruzioni CPU LINQ speciali e registri LINQ che possono essere usati per tradurre più velocemente codice macchina LINQ e, se ci fossero, verrebbero usati anche da codice non LINQ.
mg30rg

Non è vero, a un certo punto alcune operazioni di collegamento potrebbero diventare multi-thread o addirittura utilizzare la GPU a un certo punto.
John Stock,



5

Ero interessato a questa domanda, quindi ho fatto un test proprio ora. Utilizzo di .NET Framework 4.5.2 su una CPU Intel (R) Core (TM) i3-2328M a 2,20 GHz, 2200 Mhz, 2 core con 8 GB di ram con Microsoft Windows 7 Ultimate.

Sembra che LINQ potrebbe essere più veloce rispetto a ogni ciclo. Ecco i risultati che ho ottenuto:

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

Sarebbe interessante se alcuni di voi potessero copiare e incollare questo codice in un'app della console e anche testarlo. Prima di testare con un oggetto (Employee) ho provato lo stesso test con numeri interi. LINQ è stato più veloce anche lì.

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

}

Questo è quello che ho ottenuto: Exists = True Time = 274 Exists = True Time = 314
PmanAce

2
hai considerato di fare il linq prima e per ogni dopo, potrebbe fare anche qualche differenza
Muhammad Mamoor Khan

3
Interessante. Ho Exists=True Time=184 Exists=True Time=135It su un laptop Apache Gaming (Win 10, C # 7.3). Compilato ed eseguito in modalità debug. Se inverto i test ottengo Exists=True Time=158 Exists=True Time=194. Sembra che Linq sia più ottimizzato immagino.
James Wilkins

1
C'è un malinteso in questo post riguardo al test dell'oggetto. Mentre è decisamente interessante che List.Exists e .Contains sembrano funzionare meglio di foreach. È importante notare che .Exists non è un metodo da linq a entità e funzionerà solo su elenchi, il suo metodo equivalente a linq, .Any (), è decisamente più lento del metodo foreach.
AbdulG

3

Questa è in realtà una domanda piuttosto complessa. Linq rende alcune cose molto facili da fare, che se le implementi tu stesso potresti inciampare (es. Linq .Except ()). Ciò si applica in particolare a PLinq e in particolare all'aggregazione parallela implementata da PLinq.

In generale, per codice identico, linq sarà più lento, a causa del sovraccarico della chiamata del delegato.

Se, tuttavia, stai elaborando una vasta gamma di dati e applicando calcoli relativamente semplici agli elementi, otterrai un enorme aumento delle prestazioni se:

  1. Si utilizza un array per memorizzare i dati.
  2. Si utilizza un ciclo for per accedere a ogni elemento (al contrario di foreach o linq).

    • Nota: durante il benchmarking, ricorda a tutti: se usi lo stesso array / elenco per due test consecutivi, la cache della CPU renderà il secondo più veloce. *
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.