Y a-t-il un impact sur les performances lors de l’appel à ToList ()?

Lorsque vous utilisez ToList() , y a-t-il un impact sur les performances à prendre en compte?

J’écrivais une requête pour récupérer des fichiers d’un répertoire, qui est la requête:

ssortingng[] imageArray = Directory.GetFiles(directory);

Cependant, comme j’aime travailler avec List , j’ai décidé de mettre …

List imageList = Directory.GetFiles(directory).ToList();

Y a-t-il un impact sur les performances à prendre en compte lors de la décision de procéder à une conversion comme celle-ci – ou uniquement pour prendre en compte un grand nombre de fichiers? Est-ce une conversion négligeable?

IEnumerable.ToList()

Oui, IEnumerable.ToList() a un impact sur les performances, il s’agit d’une opération O (n) , mais elle ne nécessitera probablement une attention que dans les opérations critiques.

L’opération ToList() utilisera le constructeur List(IEnumerable collection) . Ce constructeur doit faire une copie du tableau (plus généralement IEnumerable ), sinon les modifications futures du tableau d’origine changeront sur le source T[] ce qui ne serait généralement pas souhaitable.

J’aimerais réitérer que cela ne fera qu’une différence avec une liste énorme, copier des morceaux de mémoire est une opération assez rapide à effectuer.

Astuce pratique, par rapport To

Vous remarquerez que dans LINQ, il existe plusieurs méthodes commençant par As (telles que AsEnumerable() ) et To (telles que ToList() ). Les méthodes qui commencent par To nécessitent une conversion comme ci-dessus (c.-à-d. Peuvent avoir un impact sur les performances), et les méthodes qui commencent par As ne nécessitent pas et nécessiteront simplement une conversion ou une opération simple.

Détails supplémentaires sur la List

Voici un peu plus de détails sur le fonctionnement de List au cas où vous seriez intéressé 🙂

Une List utilise également une construction appelée tableau dynamic qui doit être redimensionnée à la demande, cet événement de redimensionnement copie le contenu d’un ancien tableau dans le nouveau tableau. Donc, il commence petit et augmente de taille si nécessaire .

C’est la différence entre les atsortingbuts Capacity et Count sur List . Capacity fait référence à la taille du tableau dans les coulisses, Count est le nombre d’éléments de la List qui est toujours <= Capacity . Ainsi, lorsqu'un élément est ajouté à la liste, en augmentant la Capacity passée, la taille de la List est doublée et le tableau est copié.

Y a-t-il un impact sur les performances lors de l’appel à toList ()?

Oui bien sûr. Théoriquement, même si i++ a un impact sur les performances, cela ralentit le programme pour peut-être quelques tiques.

Que fait-il?

Lorsque vous .ToList , le code appelle Enumerable.ToList() qui est une méthode d’extension qui return new List(source) . Dans le constructeur correspondant, dans le pire des cas, il traverse le conteneur d’éléments et les ajoute un par un dans un nouveau conteneur. Ainsi, son comportement affecte peu la performance. Il est impossible d’être un goulot de performance de votre application.

Quel est le problème avec le code dans la question

Directory.GetFiles parcourt le dossier et renvoie immédiatement tous les noms de fichiers en mémoire, il existe un risque potentiel que la chaîne [] coûte beaucoup de mémoire, ralentissant tout.

Que devrait-on faire alors

Ça dépend. Si vous (et votre logique métier) garantissez que la quantité de fichiers dans le dossier est toujours faible, le code est acceptable. Mais il est toujours suggéré d’utiliser une version paresseuse: Directory.EnumerateFiles dans C # 4. Cela ressemble beaucoup plus à une requête, qui ne sera pas exécutée immédiatement, vous pouvez append plus de requête comme:

 Directory.EnumerateFiles(myPath).Any(s => s.Contains("myfile")) 

qui arrêtera de chercher le chemin dès qu’un fichier dont le nom contient “myfile” est trouvé. C’est évidemment une meilleure performance que .GetFiles .

Y a-t-il un impact sur les performances lors de l’appel à toList ()?

Oui il y a. En utilisant la méthode d’extension Enumerable.ToList() , vous construirez un nouvel object List partir de la collection source IEnumerable qui a bien sûr un impact sur les performances.

Cependant, comprendre List peut vous aider à déterminer si l’impact sur les performances est significatif.

List utilise un tableau ( T[] ) pour stocker les éléments de la liste. Les tableaux ne peuvent pas être étendus une fois qu’ils sont alloués, de sorte que List utilisera un tableau surdimensionné pour stocker les éléments de la liste. Lorsque la List dépasse la taille du tableau sous-jacent, un nouveau tableau doit être alloué et le contenu de l’ancien tableau doit être copié dans le nouveau tableau plus grand avant que la liste puisse s’agrandir.

Lorsqu’une nouvelle List est construite à partir d’un IEnumerable il y a deux cas:

  1. La collection source implémente ICollection : ICollection.Count est utilisé pour obtenir la taille exacte de la collection source et un tableau de sauvegarde correspondant est alloué avant que tous les éléments de la collection source soient copiés dans le tableau de sauvegarde à l’aide d’ ICollection.CopyTo() . Cette opération est assez efficace et correspondra probablement à certaines instructions du processeur pour la copie de blocs de mémoire. Cependant, en termes de performances, la mémoire est requirejse pour la nouvelle baie et des cycles de processeur sont nécessaires pour copier tous les éléments.

  2. Sinon, la taille de la collection source est inconnue et l’énumérateur de IEnumerable est utilisé pour append chaque élément source un par un à la nouvelle List . Initialement, le tableau de sauvegarde est vide et un tableau de taille 4 est créé. Ensuite, lorsque ce tableau est trop petit, la taille est doublée, de sorte que le tableau de sauvegarde se développe comme ceci 4, 8, 16, 32 etc. Chaque fois que le tableau de sauvegarde augmente, il doit être ré-alloué et tous les éléments stockés doivent être copiés. Cette opération est beaucoup plus coûteuse par rapport au premier cas où un tableau de la taille correcte peut être créé immédiatement.

    De plus, si votre collection source contient 33 éléments, la liste utilisera un tableau de 64 éléments gaspillant de la mémoire.

Dans votre cas, la collection source est un tableau qui implémente ICollection sorte que l’impact des performances ne devrait pas vous préoccuper, à moins que votre tableau source soit très volumineux. L’appel de ToList() copiera simplement le tableau source et l’enveloppera dans un object List . Même les performances du second cas ne sont pas une préoccupation pour les petites collections.

“Y a-t-il un impact sur la performance qui doit être pris en compte?”

Le problème avec votre scénario précis est que votre préoccupation réelle en matière de performances réside avant tout dans la vitesse du disque et l’efficacité du cache du disque.

De ce sharepoint vue, l’impact est certainement négligeable au point que NON, il n’est pas nécessaire de le prendre en compte.

MAIS SEULEMENT si vous avez vraiment besoin des fonctionnalités de la structure List<> pour vous rendre soit plus productif, soit votre algorithme plus convivial, soit un autre avantage. Sinon, vous ajoutez simplement un impact de performance insignifiant, sans aucune raison. Dans ce cas, naturellement, vous ne devriez pas le faire! 🙂

ToList() crée une nouvelle liste et y place les éléments, ce qui signifie qu’il ya un coût associé à la pratique de ToList() . Dans le cas d’une petite collection, le coût ne sera pas très élevé, mais une collection importante peut entraîner une baisse de performance en cas d’utilisation de ToList.

En règle générale, vous ne devez pas utiliser ToList () à moins que le travail que vous effectuez ne puisse être effectué sans convertir la collection en liste. Par exemple, si vous souhaitez simplement parcourir la collection, vous n’avez pas besoin d’exécuter ToList.

Si vous effectuez des requêtes sur une source de données, par exemple une firebase database utilisant LINQ to SQL, le coût de ToList est beaucoup plus élevé lorsque vous utilisez ToList avec LINQ to SQL au lieu d’exécuter une exécution différée. dans de nombreux scénarios) il charge instantanément les éléments de la firebase database en mémoire

Ce sera aussi efficace que:

 var list = new List(items); 

Si vous démontez le code source du constructeur qui prend un IEnumerable , vous verrez qu’il fera quelques choses:

  • Appelez collection.Count , donc si collection est un IEnumerable , il forcera l’exécution. Si la collection est un tableau, une liste, etc., elle devrait être O(1) .

  • Si la collection implémente ICollection , elle enregistre les éléments dans un tableau interne en utilisant la méthode ICollection.CopyTo . Ce devrait être O(n) , étant la longueur de la collection.

  • Si collection n’implémente pas ICollection , il parcourra les éléments de la collection et les appenda à une liste interne.

Donc, oui, il consumra plus de mémoire, car il doit créer une nouvelle liste, et dans le pire des cas, ce sera O(n) , car il parcourra la collection pour en faire une copie.

ToList Permet de créer une nouvelle liste et de copier des éléments de la source originale vers la liste nouvellement créée. La seule chose est de copier les éléments de la source d’origine et dépend de la taille de la source.

Considérant les performances de récupération de la liste de fichiers, ToList() est négligeable. Mais pas vraiment pour d’autres scénarios. Cela dépend vraiment de l’endroit où vous l’utilisez.

  • Lorsque vous appelez un tableau, une liste ou une autre collection, vous créez une copie de la collection sous forme de List . Les performances dépendent de la taille de la liste. Vous devriez le faire quand c’est vraiment nécessaire.

    Dans votre exemple, vous l’appelez sur un tableau. Il parcourt le tableau et ajoute les éléments un par un à une liste nouvellement créée. L’impact sur les performances dépend donc du nombre de fichiers.

  • Lorsque vous appelez un IEnumerable , vous matérialisez le IEnumerable (généralement une requête).