Domande frequenti sulle prestazioni LINQ

Sto cercando di fare i conti con LINQ. La cosa che mi infastidisce di più è che, anche se comprendo meglio la syntax, non voglio sacrificare involontariamente le prestazioni per l’espressività.

Sono buoni depositi centralizzati di informazioni o libri per “LINQ efficace”? In caso contrario, qual è la tua tecnica LINQ ad alte prestazioni preferita?

Sono principalmente interessato a LINQ to Objects, ma anche tutti i suggerimenti su LINQ to SQL e LINQ to XML sono ben accetti. Grazie.

La semplice comprensione di cosa sta facendo internamente LINQ dovrebbe fornire informazioni sufficienti per sapere se stai subendo un calo di prestazioni.

Ecco un semplice esempio in cui LINQ aiuta le prestazioni. Considera questo approccio tipico della vecchia scuola:

 List foos = GetSomeFoos(); List filteredFoos = new List(); foreach(Foo foo in foos) { if(foo.SomeProperty == "somevalue") { filteredFoos.Add(foo); } } myRepeater.DataSource = filteredFoos; myRepeater.DataBind(); 

Quindi il codice sopra riportato itererà due volte e assegnerà un secondo contenitore per contenere i valori filtrati. Che spreco! Confrontare con:

 var foos = GetSomeFoos(); var filteredFoos = foos.Where(foo => foo.SomeProperty == "somevalue"); myRepeater.DataSource = filteredFoos; myRepeater.DataBind(); 

Ciò solo itera una volta (quando il ripetitore è vincolato); usa sempre e solo il contenitore originale; filteredFoos è solo un enumeratore intermedio. E se, per qualche ragione, decidi di non bind più tardi il ripetitore, nulla è sprecato. Non riesci nemmeno a ripetere o valutare una volta.

Quando entrate in manipolazioni di sequenze molto complesse, potete potenzialmente guadagnare molto sfruttando l’uso intrinseco di LINQ per il concatenamento e la valutazione pigra. Di nuovo, come con qualsiasi cosa, è solo questione di capire cosa sta facendo in realtà.

Linq, come tecnologia integrata, presenta vantaggi e svantaggi in termini di prestazioni. Il codice dietro i metodi di estensione ha ricevuto notevoli attenzioni prestazionali dal team .NET e la sua capacità di fornire una valutazione lenta significa che il costo di eseguire la maggior parte delle manipolazioni su un insieme di oggetti è distribuito attraverso l’algoritmo più grande che richiede il set manipolato . Tuttavia, ci sono alcune cose che devi sapere che possono creare o distruggere le prestazioni del tuo codice.

Innanzitutto, Linq non salva magicamente il tuo programma il tempo o la memoria necessari per eseguire un’operazione; può solo ritardare quelle operazioni fino a quando assolutamente necessario. OrderBy () esegue un QuickSort, che impiegherà il tempo di nlogn proprio come se avessi scritto il tuo QuickSorter o usato List.Sort () al momento giusto. Quindi, sii sempre attento a ciò che stai chiedendo a Linq di fare una serie quando scrivi delle query; se una manipolazione non è necessaria, cerca di ristrutturare la query o la catena di metodi per evitarlo.

Allo stesso modo, alcune operazioni (ordinamento, raggruppamento, aggregati) richiedono la conoscenza dell’intero set su cui stanno agendo. L’ultimo elemento di una serie potrebbe essere il primo che l’operazione deve restituire dal suo iteratore. Inoltre, poiché le operazioni di Linq non dovrebbero alterare la loro sorgente enumerabile, ma molti degli algoritmi che usano (cioè ordinamenti sul posto), queste operazioni finiscono non solo per valutare, ma per copiare l’intera enumerabile in una struttura finita concreta , eseguendo l’operazione e cedendo attraverso di essa. Quindi, quando usi OrderBy () in un’istruzione, e chiedi un elemento dal risultato finale, TUTTO quello che l’IEnumerable che gli viene dato può essere valutato, salvato in memoria come una matrice, ordinato, quindi restituito un elemento ad una tempo. La morale è che ogni operazione che necessita di un insieme finito invece di un enumerabile dovrebbe essere posta il più tardi ansible nella query, consentendo altre operazioni come Where () e Select () per ridurre la cardinalità e l’impronta di memoria del set sorgente.

Infine, i metodi Linq aumentano drasticamente le dimensioni dello stack di chiamata e l’ingombro della memoria del sistema. Ogni operazione che deve conoscere l’intero set mantiene l’intera sorgente impostata in memoria fino a quando l’ultimo elemento è stato iterato e la valutazione di ogni elemento coinvolgerà uno stack di chiamate almeno due volte più profondo del numero di metodi nella catena o delle clausole nella propria istruzione inline (una chiamata a MoveNext () di ciascun iteratore o che restituisce GetEnumerator, più almeno una chiamata a ogni lambda lungo la strada). Ciò si tradurrà in un algoritmo più grande e più lento di un algoritmo inline intelligentemente progettato che esegue le stesse manipolazioni. Il principale vantaggio di Linq è la semplicità del codice. Creare, quindi ordinare, un dizionario di liste di valori di gruppi non è un codice molto facile da capire (credimi). Le micro-ottimizzazioni possono offuscare ulteriormente. Se la prestazione è la tua preoccupazione principale, allora non usare Linq; aggiungerà circa il 10% di tempo di overhead e più volte il sovraccarico della memoria di manipolare un elenco sul posto. Tuttavia, la manutenibilità è di solito la preoccupazione principale degli sviluppatori e Linq DEFINITELY aiuta lì.

Sul calcio delle prestazioni: se la performance del tuo algoritmo è la prima priorità sacrale, senza compromessi, starai programmando in un linguaggio non gestito come il C ++; .NET sarà molto più lento solo in quanto ambiente di runtime gestito, con compilazione nativa JIT, memoria gestita e thread di sistema aggiuntivi. Adotterei una filosofia secondo cui è “abbastanza buono”; Linq può introdurre rallentamenti per sua natura, ma se non si riesce a capire la differenza e il proprio cliente non può dire la differenza, allora per tutti gli scopi pratici non c’è differenza. “L’ottimizzazione prematura è la radice di tutti i mali”; Fallo funzionare, POI cercare opportunità per renderlo più performante, finché tu e il tuo cliente non sarete d’accordo. Potrebbe sempre essere “migliore”, ma a meno che tu non voglia essere il codice della macchina per l’imballaggio a mano, troverai un punto in meno rispetto al quale puoi dichiarare la vittoria e andare avanti.

Ci sono vari fattori che influenzano le prestazioni.

Spesso, lo sviluppo di una soluzione mediante LINQ offre prestazioni ragionevoli in quanto il sistema può creare un albero di espressioni per rappresentare la query senza eseguire effettivamente la query durante la sua creazione. Solo quando si esegue iterazione sui risultati, viene utilizzato questo albero di espressioni per generare ed eseguire una query.

In termini di efficienza assoluta, se si utilizzano procedure memorizzate predefinite, è ansible che si verifichi un impatto sulle prestazioni, ma generalmente l’approccio da adottare è sviluppare una soluzione utilizzando un sistema che offre prestazioni ragionevoli (come LINQ) e non preoccuparsi di una perdita di qualche percentuale di prestazioni. Se una query è quindi in esecuzione lentamente, forse si guarda all’ottimizzazione.

La realtà è che la maggior parte delle query non avrà il minimo problema con l’esecuzione di LINQ. L’altro fatto è che se la tua query è in esecuzione lentamente, probabilmente è più probabile che si verifichino problemi di indicizzazione, struttura, ecc. Che con la query stessa, quindi anche quando cerchi di ottimizzare le cose spesso non tocchi il LINQ, solo il struttura del database contro cui lavora.

Per la gestione di XML, se hai un documento che viene caricato e analizzato in memoria (come qualsiasi cosa basato sul modello DOM, o su un XmlDocument o altro), avrai un maggiore utilizzo della memoria rispetto ai sistemi che fanno qualcosa come indica di trovare un tag di inizio o fine, ma non di creare una versione completa in memoria del documento (come SAX o XmlReader). Lo svantaggio è che l’elaborazione basata sugli eventi è in genere piuttosto più complessa. Ancora una volta, con la maggior parte dei documenti non ci sarà alcun problema – molti sistemi hanno diversi GB di RAM, quindi prendere un paio di MB che rappresentano un singolo documento XML non è un problema (e spesso elaborate un grande insieme di documenti XML almeno un po ‘ sequenziale). È solo se hai un enorme file XML che richiederebbe 100 di MB che ti preoccupi della scelta particolare.

Tieni presente che LINQ ti consente di scorrere le liste in memoria e così via, quindi in alcune situazioni (ad esempio, dove utilizzerai più volte una serie di risultati in una funzione), puoi utilizzare .ToList o. Array per restituire i risultati. A volte questo può essere utile, sebbene in generale si voglia provare a utilizzare l’interrogazione del database piuttosto in memoria.

Per quanto riguarda i preferiti personali – NHibernate LINQ – è uno strumento di mapping relazionale degli oggetti che ti permette di definire classi, definire dettagli di mapping, e quindi di generare il database dalle tue classi piuttosto che viceversa, e il supporto LINQ è carino buono (sicuramente migliore di quelli di SubSonic).

In linq to SQL non è necessario preoccuparsi troppo delle prestazioni. puoi concatenare tutte le tue affermazioni nel modo in cui ritieni che sia il più leggibile. Linq converte tutte le tue istruzioni in 1 istruzione SQL alla fine, che alla fine viene chiamata / eseguita (come quando si chiama .ToList()

una var può contenere questa istruzione senza eseguirla se si desidera applicare varie istruzioni aggiuntive in condizioni diverse. L’esecuzione alla fine avviene solo quando vuoi tradurre le tue affermazioni in un risultato come un object o un elenco di oggetti.

C’è un progetto di codeplex chiamato i4o che ho usato un po ‘di tempo fa, che può aiutare a migliorare le prestazioni di Linq to Objects nei casi in cui si fanno confronti di uguaglianza, ad esempio

 from p in People where p.Age == 21 select p; 

http://i4o.codeplex.com/ Non l’ho provato con .Net 4 quindi non posso dire con certezza che funzionerà ancora, ma vale la pena provarlo. Per far funzionare la sua magia, per lo più devi semplicemente decorare la tua class con alcuni attributi per specificare quale proprietà deve essere indicizzata. Quando l’ho usato prima però funziona solo con confronti di uguaglianza.