Linq Count () è più veloce o più lento di List.Count o Array.Length?

Il metodo Linq Count( ) è più veloce o più lento di List.Count o Array.Length ?

In generale più lento. Il conteggio di LINQ in generale è un’operazione O(N) mentre List.Count e Array.Length sono entrambi garantiti come O(1) .

Tuttavia, in alcuni casi, LINQ rappresenterà un caso speciale per il parametro IEnumerable mediante il cast di determinati tipi di interfaccia come IList o ICollection . Quindi utilizzerà quel metodo Count per eseguire un’operazione Count() effettiva. Quindi tornerà a O(1) . Ma paghi comunque il piccolo overhead del cast e della chiamata all’interfaccia.

Il metodo Enumerable.Count() controlla ICollection , usando .Count – quindi, nel caso di matrici e liste, non è molto più inefficiente (solo un livello extra di riferimento indiretto).

Marc ha la risposta giusta ma il diavolo è nei dettagli.

Sulla mia macchina:

  • Per gli array. La lunghezza è circa 100 volte più veloce di .Count ()
  • For Lists .Count è circa 10 volte più veloce di .Count () – Nota: mi aspetterei prestazioni simili da tutte le raccolte che implementano IList

Gli array iniziano più lentamente dal momento che .Length implica solo una singola operazione, .Count sugli array comporta uno strato di riferimento indiretto. Quindi. Il carico sugli array inizia 10 volte più lentamente (sulla mia macchina), il che potrebbe essere uno dei motivi per cui l’interfaccia è implementata esplicitamente. Immagina se tu avessi un object con due proprietà pubbliche, .Count e .Length. Entrambi fanno la stessa identica cosa ma .Count è 10 volte più lento.

Ovviamente, questo non fa davvero la differenza, visto che dovresti contare i tuoi array ed elenchi milioni di volte al secondo per sentire un successo nelle prestazioni.

Codice:

  static void TimeAction(string description, int times, Action func) { var watch = new Stopwatch(); watch.Start(); for (int i = 0; i < times; i++) { func(); } watch.Stop(); Console.Write(description); Console.WriteLine(" Time Elapsed {0} ms", watch.ElapsedMilliseconds); } static void Main(string[] args) { var array = Enumerable.Range(0, 10000000).ToArray(); var list = Enumerable.Range(0, 10000000).ToArray().ToList(); // jit TimeAction("Ignore and jit", 1 ,() => { var junk = array.Length; var junk2 = list.Count; array.Count(); list.Count(); }); TimeAction("Array Length", 1000000, () => { var tmp1 = array.Length; }); TimeAction("Array Count()", 1000000, () => { var tmp2 = array.Count(); }); TimeAction("Array Length through cast", 1000000, () => { var tmp3 = (array as ICollection).Count; }); TimeAction("List Count", 1000000, () => { var tmp1 = list.Count; }); TimeAction("List Count()", 1000000, () => { var tmp2 = list.Count(); }); Console.ReadKey(); } 

risultati:

 Lunghezza della matrice Tempo trascorso 3 ms
 Numero di array () Tempo trascorso 264 ms
 Lunghezza della matrice attraverso il cast Tempo trascorso 16 ms
 Tempo di conteggio elenco trascorso 3 ms
 Elenco conteggio () Tempo trascorso 18 ms

Credo che se chiamate Linq.Count () su un ICollection o su un IList (come un ArrayList o un List), restituirà semplicemente il valore della proprietà Count. Quindi la performance sarà all’incirca uguale per le collezioni normali.

Direi che dipende dalla lista. Se è un IQueryable che è una tabella in un db da qualche parte allora Count () sarà molto più veloce perché non deve caricare tutti gli oggetti. Ma se l’elenco è in memoria, suppongo che la proprietà Count sarebbe più veloce se non lo stesso.

Alcune informazioni aggiuntive – LINQ Count – la differenza tra l’utilizzo e non può essere enorme – e questo non deve essere oltre le collezioni ‘grandi’ neanche. Ho una collezione formata da linq per oggetti con circa 6500 elementi (grande .. ma non enorme in alcun modo). Count () nel mio caso richiede diversi secondi. La conversione in una lista (o in una matrice, qual è) il conteggio è quindi virtualmente immediata. Avere questo conteggio in un ciclo interno significa che l’impatto potrebbe essere enorme. Il conteggio enumera attraverso tutto. Un array e un elenco sono entrambi “auto-consapevoli” delle loro lunghezze e non hanno bisogno di enumerarli. Eventuali istruzioni di debug (log4net per ex) che fanno riferimento a questo conteggio () rallenteranno notevolmente anche di più. Fai un favore a te stesso e se hai bisogno di fare riferimento a questo, spesso salva la dimensione del conteggio e chiamalo solo una volta su una raccolta LINQ a meno che non lo converti in un elenco e quindi possa fare riferimento senza un colpo di rendimento.

Ecco una rapida prova di ciò di cui stavo parlando sopra. Nota ogni volta che chiamiamo Count () le nostre modifiche alle dimensioni della collezione .. quindi avviene la valutazione, che è più di una operazione di conteggio prevista. Solo qualcosa di cui essere a conoscenza:)

 using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace LinqTest { class TestClass { public TestClass() { CreateDate = DateTime.Now; } public DateTime CreateDate; } class Program { static void Main(string[] args) { //Populate the test class List list = new List(1000); for (int i=0; i<1000; i++) { System.Threading.Thread.Sleep(20); list.Add(new TestClass()); if(i%100==0) { Console.WriteLine(i.ToString() + " items added"); } } //now query for items var newList = list.Where(o=> o.CreateDate.AddSeconds(5)> DateTime.Now); while (newList.Count() > 0) { //Note - are actual count keeps decreasing.. showing our 'execute' is running every time we call count. Console.WriteLine(newList.Count()); System.Threading.Thread.Sleep(500); } } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace LinqTest { class TestClass { public TestClass() { CreateDate = DateTime.Now; } public DateTime CreateDate; } class Program { static void Main(string[] args) { //Populate the test class List list = new List(1000); for (int i=0; i<1000; i++) { System.Threading.Thread.Sleep(20); list.Add(new TestClass()); if(i%100==0) { Console.WriteLine(i.ToString() + " items added"); } } //now query for items var newList = list.Where(o=> o.CreateDate.AddSeconds(5)> DateTime.Now); while (newList.Count() > 0) { //Note - are actual count keeps decreasing.. showing our 'execute' is running every time we call count. Console.WriteLine(newList.Count()); System.Threading.Thread.Sleep(500); } } } } 

List.Count o Array.Length è effettivamente più veloce di Linq Count() . Perché Linq Count() eseguirà l’iterazione dell’intero elenco di elementi da contare. List.Count o Array.Length usano la loro proprietà.