web-dev-qa-db-fra.com

Linq Count () est-il plus rapide ou plus lent que List.Count ou Array.Length?

La méthode LINQ Count() est-elle plus rapide ou plus lente que List<>.Count ou Array.Length?

45
NotDan

En général plus lent. Le décompte général de LINQ est une opération O(N) tandis que List.Count et Array.Length sont tous deux garantis d'être O(1)

Cependant, dans certains cas, LINQ accordera une attention particulière au paramètre IEnumerable<T> en définissant certains types d’interface tels que IList<T> ou ICollection<T>. Il utilisera ensuite cette méthode Count pour effectuer une opération réelle Count(). Donc, ça va redescendre à O(1). Mais vous payez toujours les frais généraux mineurs de la distribution et de l'appel d'interface. 

60
JaredPar

La méthode Enumerable.Count() recherche ICollection<T>, à l'aide de .Count. Ainsi, dans le cas des tableaux et des listes, ce n'est pas beaucoup plus inefficace (juste un niveau supplémentaire d'indirection).

25
Marc Gravell

Marc a la bonne réponse mais le diable est dans les détails. 

Sur ma machine:

  • Pour les tableaux .Length est environ 100 fois plus rapide que .Count ()
  • Pour les listes .Count est environ 10 fois plus rapide que .Count () - Remarque: Je m'attendrais à des performances similaires de toutes les collections qui implémentent IList<T> 

Les tableaux démarrent plus lentement car .Length n'implique qu'une seule opération. Le compte sur des tableaux implique une couche d'indirection. Ainsi, .Count sur les tableaux démarre 10 fois plus lentement (sur ma machine), ce qui pourrait être l'une des raisons pour lesquelles l'interface est implémentée explicitement. Imaginez si vous aviez un objet avec deux propriétés publiques, .Count et .Length. Les deux font exactement la même chose mais .Count est 10X plus lent. 

Bien sûr, rien de tout cela fait vraiment une grande différence car il faudrait compter vos tableaux et listes des millions de fois par seconde pour ressentir un coup dur pour la performance. 

Code: 

    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<int>).Count;
        });


        TimeAction("List Count", 1000000, () =>
        {
            var tmp1 = list.Count;
        });

        TimeAction("List Count()", 1000000, () =>
        {
            var tmp2 = list.Count();
        });

        Console.ReadKey();
    }

Résultats:

 Durée du tableau écoulée 3 ms 
 Nombre de tableaux () Temps écoulé 264 ms 
 Longueur du tableau après la conversion Temps écoulé 16 ms 
 Lister le temps écoulé 3 ms 
 Liste Compte () Temps écoulé 18 ms 
22
Sam Saffron

Je crois que si vous appelez Linq.Count () sur un ICollection ou un IList (comme un ArrayList ou une List), il renverra simplement la valeur de la propriété Count. La performance sera donc à peu près la même sur les collections simples.

2
Jake Pearson

Je dirais que cela dépend de la liste. Si c'est un IQueryable qui est une table dans une base de données quelque part alors Count () sera beaucoup plus rapide car il n'a pas à charger tous les objets. Mais si la liste est en mémoire, j'imagine que la propriété Count serait plus rapide, sinon plus, identique.

2
Jose

Quelques informations supplémentaires - Compte LINQ - la différence entre l’utiliser et ne pas l’être peut être énorme - et cela ne doit pas nécessairement être non plus sur de grandes collections. J'ai une collection formée de linq à des objets avec environ 6500 articles (gros .. mais pas énorme par tout moyen). Count () dans mon cas prend plusieurs secondes. La conversion en liste (ou tableau, peu importe), le compte est alors quasi immédiate. Avoir ce nombre dans une boucle interne signifie que l'impact pourrait être énorme. Le comte énumère à travers tout. Un tableau et une liste sont tous deux "conscients" de leur longueur et n'ont pas besoin de les énumérer. Toute déclaration de débogage (log4net par exemple) faisant référence à ce compte () ralentira également considérablement le tout. Faites-vous une faveur et si vous avez besoin de référencer, enregistrez souvent la taille du nombre et appelez-la une seule fois sur une collection LINQ, à moins que vous ne la convertissiez en liste et que vous puissiez ensuite faire référence à distance sans impact négatif sur les performances.

Voici un rapide test de ce dont je parlais ci-dessus. Notez que chaque fois que nous appelons Count (), la taille de notre collection change. L’évaluation a donc lieu, ce qui est plus que l’opération «count» attendue. Juste quelque chose à être au courant de:)

    
    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);
                }
            }
        }
    }
0

List.Count ou Array.Length est en effet plus rapide que Linq Count(). Parce que Linq Count() parcourra toute la liste des éléments à compter. List.Count ou Array.Length utilisent leur propriété. 

0
Sajithd