Recentemente ho iniziato ad usare LINQ un bel po ', e non ho davvero visto alcuna menzione di run-time la complessità per uno dei metodi di LINQ. Ovviamente, ci sono molti fattori in gioco qui, quindi cerchiamo di limitare la discussione al fornitore di pianura IEnumerable
LINQ to Objects. Inoltre, supponiamo che qualsiasi Func
passato come selettore/mutatore/ecc. Sia un'operazione O (1) economica.Quali garanzie ci sono sulla complessità in fase di esecuzione (Big-O) dei metodi LINQ?
Appare evidente che tutte le operazioni single-pass (Select
, Where
, Count
, Take/Skip
, Any/All
, ecc) sarà O (n), in quanto solo bisogno di percorrere la sequenza una volta; anche se questo è soggetto alla pigrizia.
Le cose sono più torbida per le operazioni più complesse; gli operatori set-like (Union
, Distinct
, Except
, ecc.) funzionano utilizzando GetHashCode
per impostazione predefinita (afaik), quindi sembra ragionevole supporre che stiano utilizzando internamente una tabella hash, rendendo anche queste operazioni O (n), in generale. Che dire delle versioni che utilizzano uno IEqualityComparer
?
OrderBy
avrebbe bisogno di una specie, quindi molto probabilmente stiamo guardando O (n log n). Cosa succede se è già stato ordinato? Che ne dici se io dico OrderBy().ThenBy()
e fornire la stessa chiave per entrambi?
Ho potuto vedere GroupBy
(e Join
) utilizzando l'ordinamento o l'hashing. Cos'è questo?
Contains
sarebbe O (n) su un List
, ma O (1) su un HashSet
- fa controllare LINQ il contenitore sottostante per vedere se può accelerare le cose?
E la vera domanda - finora, ho preso sulla fede che le operazioni sono performante. Tuttavia, posso fare affidamento su quello? I contenitori STL, ad esempio, specificano chiaramente la complessità di ogni operazione. Esistono garanzie simili sulle prestazioni LINQ nelle specifiche della libreria .NET?
Altre domande (in risposta ai commenti):
Non avevo davvero pensato a spese generali, ma non mi aspettavo che ci fosse molto per i semplici Linq-to-Objects. Il post di CodingHorror parla di Linq-to-SQL, dove posso capire l'analisi della query e fare in modo che SQL aggiunga costi: c'è un costo simile anche per il fornitore di oggetti? Se è così, è diverso se stai usando la sintassi dichiarativa o funzionale?
Anche se non posso davvero rispondere alla tua domanda, voglio commentare che in generale la maggior parte della performance sarà "overhead" rispetto alle funzionalità di base. Questo, naturalmente, non è il caso quando si hanno dataset molto grandi (> 10k items) quindi sono curioso nel caso in cui lo si voglia sapere. – Henri
Ri: "è diverso se stai usando la sintassi dichiarativa o funzionale?" - Il compilatore traduce la sintassi dichiarativa nella sintassi funzionale in modo che siano uguali. –
"I contenitori STL specificano chiaramente la complessità di ogni operazione" I contenitori .NET specificano chiaramente anche la complessità di ogni operazione. Le estensioni Linq sono simili agli algoritmi STL, non ai contenitori STL. Proprio come quando si applica un algoritmo STL a un container STL, è necessario combinare la complessità dell'estensione Linq con la complessità delle operazioni del contenitore .NET per analizzare correttamente la complessità risultante. Ciò include la contabilità per le specializzazioni dei modelli, come menziona la risposta di Aaronaught. – Timbo