Chiunque conosca eventuali differenze di velocità tra Dove e FindAll nell'elenco. So che Where fa parte di IEnumerable e FindAll è parte di List, sono solo curioso di sapere cosa è più veloce.C# FindAll VS Where Speed
risposta
Il metodo FindAll della classe List <T> crea effettivamente un nuovo oggetto elenco e aggiunge risultati. Il metodo di estensione Dove per IEnumerable <T> semplicemente iterare elenco esistente e cedere un'enumerazione dei risultati corrispondenti senza creare o aggiungere nulla (diverso dal enumeratore stessa.)
Dato un piccolo insieme, i due probabilmente sarebbe eseguire in modo comparabile. Tuttavia, dato un set più grande, Dove dovrebbe sovraperformare FindAll, poiché il nuovo Elenco creato per contenere i risultati dovrà crescere dinamicamente per contenere ulteriori risultati. L'utilizzo della memoria di FindAll inizierà anche a crescere in modo esponenziale con il crescere del numero di risultati corrispondenti, dove dovrebbe avere un utilizzo costante della memoria minimo (in sé e per sé ... escludendo qualunque cosa tu faccia con i risultati.)
L'eccezione è dove si desidera effettivamente avere un elenco in seguito (forse è necessario chiamare "Conteggio" o cambiare membri, o scorrere più volte). Mentre 'Where()' batte 'FindAll()', 'FindAll()' batte 'Where(). ToList()'. –
@JonHanna: Mentre inizialmente pensavo di essere d'accordo, in realtà non ne sono sicuro. Avete riferimenti che indicano che un .ToList() è più lento di un .FindAll()? Chiamare .ToList() su una query sarebbe ** essere ** l'iterazione dell'enumerabile, e quindi dovrebbe mantenere la sua efficienza di memoria. Non solo, alcune implementazioni interne del punto in cui l'iteratore potrebbe persino essere in grado di creare un elenco delle dimensioni giuste (allocazione della memoria) in anticipo, sovraperformando FindAll in questi casi. Non sono specificamente in disaccordo, tuttavia sarebbe bello avere una solida referenza che chiarisca il vantaggio di FindAlls. – jrista
Questa risposta è completamente sbagliata. Vedi @Wory che si è preso la briga di misurare effettivamente. –
.FindAll()
essere più veloce, si avvantaggia di conoscere già le dimensioni della lista e il looping attraverso la matrice interna con un semplice ciclo for
. .Where()
deve accendere un enumeratore (una classe di struttura sigillata denominata WhereIterator
in questo caso) e svolgere lo stesso lavoro in un modo meno specifico.
Tuttavia, tenere presente che.()() È enumerabile, non crea attivamente un elenco in memoria e lo riempie. È più simile a un flusso, quindi l'utilizzo della memoria su qualcosa di molto grande può avere una differenza significativa. Inoltre, potresti iniziare a usare i risultati in modo parallelo molto più velocemente usando l'approccio .Where() in 4.0.
WhereEnumerableIterator, anziché WhereIterator, viene effettivamente utilizzato a meno che non si includa l'indice nella clausola where. WhereEnumerableIterator è notevolmente più efficiente di WhereIterator. Nel caso dell'elenco
Nel caso di chiamare Dove su una matrice o Elenco
@jrista - I ** completamente ** ha mancato lo stack del caso in '.Dove() sovraccarico restituendo quelli, grazie! Guardando attraverso quel codice sono d'accordo. Dovrebbe avere, nel peggiore dei casi, prestazioni uguali ma quasi sempre migliori. Inoltre, SO sarebbe inutile se non fosse per le persone che impiegano il tempo extra per educare gli altri, ad es. tu e questi commenti, +1 per insegnarmi qualcosa. –
Where
è molto, molto più veloce di FindAll
. Non importa quanto sia grande la lista, Where
richiede esattamente lo stesso tempo.
Ovviamente Where
crea solo una query. In realtà non fa nulla, a differenza di FindAll
che crea un elenco.
Questo può essere tecnicamente vero, ma penso che sia abbastanza chiaro che l'OP si sta interrogando sulle prestazioni nel contesto di enumerare effettivamente il risultato, non la chiamata al metodo nudo stesso. –
La risposta di jrista rende sensati. Tuttavia, la nuova lista aggiunge gli stessi oggetti, quindi cresce solo con riferimento agli oggetti esistenti, che non dovrebbero essere così lenti. Fintanto che l'estensione 3.5/Linq è possibile, dove sta comunque meglio. FindAll ha molto più senso se limitato con 2.0
FindAll è ovviamente più lento di Dove, perché è necessario creare un nuovo elenco.
In ogni caso, penso che dovresti prendere in considerazione il commento di Jon Hanna: probabilmente dovrai eseguire alcune operazioni sui risultati e la lista sarebbe più utile di IEnumerable in molti casi.
Ho scritto un test di piccole dimensioni, è sufficiente incollarlo nel progetto App Console. Misura il tempo/tick di: esecuzione della funzione, operazioni sulla raccolta dei risultati (per ottenere il perfetto utilizzo "reale", e per essere sicuro che il compilatore non ottimizzerà i dati inutilizzati ecc. - Sono nuovo di C# e non lo faccio sapere come funziona ancora, mi dispiace).
Avviso: ogni funzione misurata tranne WhereIENumerable() crea una nuova lista di elementi. Potrei fare qualcosa di sbagliato, ma chiaramente iterando IEnumerable richiede molto più tempo dell'iterazione iteratrice.
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<Dummy> list = new List<Dummy>();
public delegate void FuncToTest();
public static long TestTicks(FuncToTest function, string 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<Dummy> 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<Dummy> ieNumerable)
{
if (!CheckSum) return;
Stopwatch watch = new Stopwatch();
watch.Start();
IEnumerator<Dummy> 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.ToString() + "\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<Dummy> resList = new List<Dummy>();
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<Dummy> resList = new List<Dummy>();
int count = list.Count;
foreach (Dummy dummy in list)
{
if (dummy.Val < FilterVal)
resList.Add(dummy);
}
Check(resList);
}
static void WhereToList()
{
List<Dummy> resList = list.Where(x => x.Val < FilterVal).ToList<Dummy>();
Check(resList);
}
static void WhereIEnumerable()
{
Stopwatch watch = new Stopwatch();
IEnumerable<Dummy> iEnumerable = list.Where(x => x.Val < FilterVal);
Check(iEnumerable);
}
static void FindAll()
{
List<Dummy> 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();
}
}
}
Risultati (zecche) - CheckSum abilitato (alcune operazioni sui risultati), la modalità: rilascio senza debug (CTRL + F5):
- 16222276 (per -> Elenco)
- 17151121 (foreach -> elenco)
- 4741494 (dove -> elenco)
- 27122285 (dove -> ienum)
- 18 821.571 (findall -> Elenco)
CheckSum disabilitata (non si utilizza lista restituita a tutti):
- 10885004 (per -> Elenco)
- 11221888 (foreach -> Elenco)
- 18688433 (dove -> elenco)
- 1075 (dove -> ienum)
- 13720243 (findall -> elenco)
I risultati possono essere leggermente diversi, per ottenere risultati reali sono necessarie più iterazioni.
i tuoi test vanno bene. Mostrano che il meccanismo LINQ è più lento di operare direttamente sulla lista. Non una sorpresa. Il tuo "1075 (dove -> ienum)" è sbagliato in quello che usa un dove senza aver attraversato gli elementi risultanti non eseguirà mai realmente un dove! –
Scusate Carlo, ma chiama il suo metodo "Check()" anche nel dove-> ienum implementatin. Check() itera tutte le raccolte, quindi i suoi risultati SONO interamente validi. Di conseguenza, anche la mia risposta è corretta ... la risposta che hai chiamato "torto". – jrista
possibile duplicato di [FindAll vs Where extension-method] (http://stackoverflow.com/questions/1531702/findall-vs-where-extension-method) –