C # FindAll VS Où la vitesse

Tout le monde connaît les différences de vitesse entre Où et FindAll sur la liste. Je sais que Where fait partie de IEnumerable et que FindAll fait partie de List, je suis juste curieux de savoir ce qui est plus rapide.

La méthode FindAll de la classe List construit en réalité un nouvel object de liste et y ajoute des résultats. La méthode d’extension Where pour IEnumerable itérera simplement sur une liste existante et produira une énumération des résultats correspondants sans créer ni append quoi que ce soit (autre que l’énumérateur lui-même).

Étant donné un petit ensemble, les deux seraient probablement comparables. Toutefois, dans la mesure où la valeur de Where doit être supérieure à celle de FindAll, la nouvelle liste créée pour contenir les résultats devra s’allonger de manière dynamic pour contenir des résultats supplémentaires. L’utilisation de la mémoire de FindAll commencera également à croître de manière exponentielle avec l’augmentation du nombre de résultats correspondants, où, comme où, devrait avoir une utilisation de mémoire minimale constante (en soi … excluant quoi que vous fassiez avec les résultats).

FindAll est évidemment plus lent que Where, car il doit créer une nouvelle liste.

Quoi qu’il en soit, je pense que vous devriez vraiment prendre en compte le commentaire de Jon Hanna – vous devrez probablement effectuer certaines opérations sur vos résultats et la liste serait plus utile que IEnumerable dans de nombreux cas.

J’ai écrit un petit test, il suffit de le coller dans le projet Console App. Il mesure le temps / ticks de: exécution de fonctions, opérations sur la collecte des résultats (pour obtenir une performance «réelle», et pour être sûr que le compilateur n’optimisera pas les données inutilisées, etc. sais comment ça marche encore, désolé).

Remarque: chaque fonction mesurée, à l’exception de WhereIENumerable (), crée une nouvelle liste d’éléments. Je fais peut-être quelque chose de mal, mais clairement, itérer IEnumerable prend beaucoup plus de temps qu’une liste itérative.

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics; namespace Tests { public class Dummy { public int Val; public Dummy(int val) { Val = val; } } public class WhereOrFindAll { const int ElCount = 20000000; const int FilterVal =1000; const int MaxVal = 2000; const bool CheckSum = true; // Checks sum of elements in list of resutls static List list = new List(); public delegate void FuncToTest(); public static long TestTicks(FuncToTest function, ssortingng msg) { Stopwatch watch = new Stopwatch(); watch.Start(); function(); watch.Stop(); Console.Write("\r\n"+msg + "\t ticks: " + (watch.ElapsedTicks)); return watch.ElapsedTicks; } static void Check(List list) { if (!CheckSum) return; Stopwatch watch = new Stopwatch(); watch.Start(); long res=0; int count = list.Count; for (int i = 0; i < count; i++) res += list[i].Val; for (int i = 0; i < count; i++) res -= (long)(list[i].Val * 0.3); watch.Stop(); Console.Write("\r\n\nCheck sum: " + res.ToString() + "\t iteration ticks: " + watch.ElapsedTicks); } static void Check(IEnumerable ieNumerable) { if (!CheckSum) return; Stopwatch watch = new Stopwatch(); watch.Start(); IEnumerator ieNumerator = ieNumerable.GetEnumerator(); long res = 0; while (ieNumerator.MoveNext()) res += ieNumerator.Current.Val; ieNumerator=ieNumerable.GetEnumerator(); while (ieNumerator.MoveNext()) res -= (long)(ieNumerator.Current.Val * 0.3); watch.Stop(); Console.Write("\r\n\nCheck sum: " + res.ToSsortingng() + "\t iteration ticks :" + watch.ElapsedTicks); } static void Generate() { if (list.Count > 0) return; var rand = new Random(); for (int i = 0; i < ElCount; i++) list.Add(new Dummy(rand.Next(MaxVal))); } static void For() { List resList = new List(); int count = list.Count; for (int i = 0; i < count; i++) { if (list[i].Val < FilterVal) resList.Add(list[i]); } Check(resList); } static void Foreach() { List resList = new List(); int count = list.Count; foreach (Dummy dummy in list) { if (dummy.Val < FilterVal) resList.Add(dummy); } Check(resList); } static void WhereToList() { List resList = list.Where(x => x.Val < FilterVal).ToList(); Check(resList); } static void WhereIEnumerable() { Stopwatch watch = new Stopwatch(); IEnumerable iEnumerable = list.Where(x => x.Val < FilterVal); Check(iEnumerable); } static void FindAll() { List resList = list.FindAll(x => x.Val < FilterVal); Check(resList); } public static void Run() { Generate(); long[] ticks = { 0, 0, 0, 0, 0 }; for (int i = 0; i < 10; i++) { ticks[0] += TestTicks(For, "For \t\t"); ticks[1] += TestTicks(Foreach, "Foreach \t"); ticks[2] += TestTicks(WhereToList, "Where to list \t"); ticks[3] += TestTicks(WhereIEnumerable, "Where Ienum \t"); ticks[4] += TestTicks(FindAll, "FindAll \t"); Console.Write("\r\n---------------"); } for (int i = 0; i < 5; i++) Console.Write("\r\n"+ticks[i].ToString()); } } class Program { static void Main(string[] args) { WhereOrFindAll.Run(); Console.Read(); } } } 

Résultats (ticks) - CheckSum activé (quelques opérations sur les résultats), mode: libération sans débogage (CTRL + F5):

  • 16222276 (pour -> liste)
  • 17151121 (foreach -> liste)
  • 4741494 (où -> liste)
  • 27122285 (où -> ienum)
  • 18821571 (findall -> liste)

CheckSum désactivé (n'utilise pas la liste renvoyée du tout):

  • 10885004 (pour -> liste)
  • 11221888 (foreach -> liste)
  • 18688433 (où -> liste)
  • 1075 (où -> ienum)
  • 13720243 (findall -> liste)

Vos résultats peuvent être légèrement différents. Pour obtenir des résultats réels, vous avez besoin de davantage d'itérations.

.FindAll() devrait être plus rapide, il tire parti de la connaissance de la taille de la liste et de sa lecture en boucle dans le tableau interne avec une simple boucle for . .Where() doit déclencher un énumérateur (une classe framework scellée appelée WhereIterator dans ce cas) et effectuer le même travail de manière moins spécifique.

N’oubliez pas cependant que .Where () est énumérable, ne crée pas activement de liste en mémoire et ne la remplit pas. Cela ressemble plus à un stream, de sorte que l’utilisation de la mémoire sur quelque chose de très grand peut avoir une différence significative. En outre, vous pouvez commencer à utiliser les résultats de manière parallèle beaucoup plus rapidement en utilisant l’approche .Where () dans 4.0.

Where est beaucoup, beaucoup plus rapide que FindAll . Peu importe la taille de la liste, Where prend exactement le même temps.

Bien sûr, Where crée simplement une requête. En réalité, il ne fait rien, contrairement à FindAll qui crée une liste.

La réponse de Jrista a du sens. Cependant, la nouvelle liste ajoute les mêmes objects, ce qui ne fait que grandir avec la référence aux objects existants, ce qui ne devrait pas être aussi lent. Tant que l’extension 3.5 / Linq est possible, Où rest mieux de toute façon. FindAll est beaucoup plus logique lorsqu’il est limité à 2.0