web-dev-qa-db-fra.com

Existe-t-il un impact sur les performances lorsque vous appelez ToList ()?

Lorsque vous utilisez ToList(), existe-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:

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

Cependant, comme j'aime bien travailler avec List<>, J'ai décidé de mettre en ...

List<string> imageList = Directory.GetFiles(directory).ToList();

Alors, y a-t-il une sorte d’impact sur les performances qui devrait être pris en compte lorsque vous décidez d’effectuer une conversion de ce type - ou uniquement lorsque vous traitez avec un grand nombre de fichiers? Est-ce une conversion négligeable?

124
Cody

IEnumerable.ToList()

Oui, IEnumerable<T>.ToList() a un impact sur les performances, il s'agit d'une opération O (n) n’exigera probablement que de l’attention lors des opérations critiques en termes de performances.

L'opération ToList() utilisera le constructeur List(IEnumerable<T> collection) . Ce constructeur doit faire une copie du tableau (plus généralement IEnumerable<T>), Sinon les modifications futures du tableau original changeront sur la source T[] Également, ce qui ne serait généralement pas souhaitable.

Je voudrais réitérer que cela ne fera qu'une différence avec une liste énorme, la copie de morceaux de mémoire est une opération assez rapide à effectuer.

Astuce pratique, As vs To

Vous remarquerez dans LINQ qu'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 celle décrite ci-dessus (c’est-à-dire qui peuvent avoir un impact sur les performances), tandis que les méthodes commençant par As ne le font pas et nécessiteront simplement une opération de conversion ou une opération simple.

Détails supplémentaires sur List<T>

Voici un peu plus de détails sur le fonctionnement de List<T> Au cas où cela vous intéresserait :)

Un List<T> Utilise également une construction appelée tableau dynamique, qui doit être redimensionné à la demande. Cet événement de redimensionnement copie le contenu d'un ancien tableau dans le nouveau. Donc, il commence petit et augmente de taille si nécessaire .

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

159
Daniel Imms

Existe-t-il un impact sur les performances lors de l'appel de toList ()?

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

Que fait .ToList?

Lorsque vous appelez .ToList, Le code appelle Enumerable.ToList(), qui est une méthode d'extension qui return new List<TSource>(source). Dans le constructeur correspondant, dans les pires circonstances, , il parcourt le conteneur d'éléments et les ajoute un à un dans un nouveau conteneur. Donc, son comportement affecte peu la performance. Il est impossible d'être un goulot d'étranglement de votre application.

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

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

Qu'est-ce qui devrait être fait alors

Ça dépend. Si vous (ainsi que votre logique métier) garantissez que le montant du fichier dans le dossier est toujours petit, le code est acceptable. Mais il est toujours suggéré d'utiliser une version paresseuse: Directory.EnumerateFiles En C # 4. Cela ressemble beaucoup plus à une requête, qui ne sera pas exécutée immédiatement, vous pouvez ajouter plus de requête dessus comme:

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

qui arrêtera la recherche du chemin dès qu'un fichier dont le nom contient "myfile" est trouvé. Cela a évidemment une meilleure performance que .GetFiles.

34
Cheng Chen

Existe-t-il un impact sur les performances lors de l'appel de toList ()?

Oui il y a. En utilisant la méthode d'extension Enumerable.ToList(), vous construirez un nouvel objet List<T> À partir de la collection source IEnumerable<T> Qui, bien entendu, a un impact sur les performances.

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

List<T> Utilise un tableau (T[]) Pour stocker les éléments de la liste. Les tableaux ne peuvent pas être étendus une fois qu'ils ont été alloués. List<T> Utilisera donc un tableau surdimensionné pour stocker les éléments de la liste. Lorsque List<T> Augmente au-delà de 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 ne puisse s'allonger.

Lorsqu'un nouveau List<T> Est construit à partir d'un IEnumerable<T>, Il y a deux cas:

  1. La collection source implémente ICollection<T>: Alors ICollection<T>.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 ne soient copiés dans le tableau de sauvegarde à l'aide de ICollection<T>.CopyTo(). Cette opération est assez efficace et va probablement correspondre à une instruction de la CPU pour copier des blocs de mémoire. Toutefois, en termes de performances, la mémoire est nécessaire pour le nouveau module RAID et des cycles de processeur sont requis pour la copie de tous les éléments.

  2. Sinon, la taille de la collection source est inconnue et l'énumérateur de IEnumerable<T> Permet d'ajouter chaque élément source au nouveau List<T>. 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 suit: 4, 8, 16, 32, etc. Chaque fois que le tableau de sauvegarde s'agrandit, il doit être réaffecté et tous les éléments stockés doivent être copiés. Cette opération est beaucoup plus coûteuse que dans le 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, ce qui gaspille de la mémoire.

Dans votre cas, la collection source est un tableau qui implémente ICollection<T>. L'impact sur les performances ne devrait donc pas vous préoccuper, sauf si votre tableau source est très volumineux. L'appel de ToList() va simplement copier le tableau source et l'enrouler dans un objet List<T>. Même les performances du second cas ne sont pas inquiétantes pour les petites collections.

15
Martin Liversage

"Y a-t-il un impact sur les performances à prendre en compte?"

Le problème avec votre scénario précis est que d’abord et avant tout, votre véritable préoccupation au sujet des performances provient de la vitesse et de l’efficacité du disque dur.

De ce point de vue, l’impact est sûrement négligeable au point que NO il n’a pas à être pris en compte.

MAIS SEULEMENT si vous avez vraiment besoin des fonctionnalités du List<> structure pour vous rendre soit plus productif, soit votre algorithme plus convivial, ou un autre avantage. Sinon, vous ajoutez simplement à dessein un coup insignifiant en termes de performances, sans aucune raison. Dans ce cas, naturellement, vous ne devriez pas le faire! :)

5
jross

ToList() crée une nouvelle liste et y place les éléments, ce qui signifie que l'opération ToList() a un coût associé. Dans le cas d'une petite collection, le coût ne sera pas très important, mais le fait de disposer d'une grande collection peut nuire aux performances si vous utilisez ToList.

En règle générale, vous ne devez pas utiliser ToList () sauf si le travail que vous effectuez ne peut être effectué sans convertir la collection en liste. Par exemple, si vous voulez juste parcourir la collection, vous n'avez pas besoin de faire ToList

Si vous effectuez des requêtes sur une source de données, par exemple une base de données utilisant LINQ to SQL, le coût de ToList est beaucoup plus coûteux, car lorsque vous utilisez ToList avec LINQ to SQL au lieu de l'exécution différée, c'est-à-dire de charger des éléments en cas de besoin (ce qui peut être avantageux). dans de nombreux scénarios) il charge instantanément les éléments de la base de données en mémoire

4
Haris Hasan

Ce sera aussi efficace que:

var list = new List<T>(items);

Si vous désassemblez le code source du constructeur qui prend un IEnumerable<T>, Vous verrez qu'il fera quelques choses:

  • Appelez collection.Count, Donc si collection est un IEnumerable<T>, L'exécution en sera forcée. Si collection est un tableau, une liste, etc., il devrait s'agir de O(1).

  • Si collection implémente ICollection<T>, Les éléments d'un tableau interne seront sauvegardés à l'aide de la méthode ICollection<T>.CopyTo. Il devrait être O(n), étant n la longueur de la collection.

  • Si collection n'implémente pas ICollection<T>, Il parcourra les éléments de la collection et les ajoutera à une liste interne.

Donc, oui, il consommera 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 le collection faire une copie de chaque élément.

4
Oscar Mederos

Compte tenu des performances de la 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 en tant que List<T>. La performance ici dépend 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 effectue une itération sur 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<T>, vous matérialisez le IEnumerable<T> (généralement une requête).

2
Mohammad Dehghan

ToList créera une nouvelle liste et copiera des éléments de la source d'origine dans la liste nouvellement créée, il ne restera donc plus qu'à copier les éléments de la source d'origine et dépend de la taille de la source.

2
TalentTuner