Entity Framework ottimizzare le performance
Direi che una buona pagina da cui partire è questo documento su MSDN :Performance Considerations for Entity Framework Applications, che punta ad una serie di risorse che ho trovato interessanti.
Cosa influisce sulle performance?
Beh, forse è la prima domanda che ci si pone. Cosa succede al momento della creazione dell’ObjectContext, il punto di accesso con cui usiamo EF ? In un bel post del team viene evidenziato il dettaglio delle operazioni fatte ed il relativo costo:
Da una rapida occhiata si vede come tra le operazioni che hanno più costo, in particolare allo start-up, ci sono:
- View Generation: è il momento in cui vengono create delle viste per le operazioni di query ed update relative allo specifico store utilizzato. Questa operazione può essere evitata: usando il tool EdmGen per generare le view, in un file .cs o .vb e compilarle il tutto assieme, come documentato qui. Nella documentazione MSDN questa operazione(View Generation) è considerata la più costosa (high).
- Materializzazione: cioè il processo di creazione degli oggetti del dominio dal valore ritornato dall’ oggetto DBDataReader. Se gli oggetti già esistono nell’ ObjectContext, usando AppendOnly e PreserveChanges come merge option, questo costo può essere trascurato.
- Anche l’Inizializzazione ed il Loading dei metadati ha un costo (moderato rispetto al primo): è il momento comunque in cui avviene l’apertura della connessione vera e propria.
Nell’ 1% c’è il tempo di esecuzione vero e proprio della query. Nello stesso post c’è anche il grafico del dettaglio di questo costo, con la spiegazione delle singole voci, tolto il tempo di start-up quindi. Vi riporto qui per comodità di lettura:
Quando ho questi costi ?
Ci potremmo chiedere. Ogni volta che creo un ObjectContext, per i tempi di start-up? O quando eseguo una query ?
Nella documentazione MSDN vediamo che alcuni di questi costi sono legati (quindi in qualche modo vivono in una cache) all’ Application Domain e non alla creazione della singola istanza di ObjectContext. Guardate la tabella seguente:
Nell’immagine vedete che i costi più alti ci sono una sola volta per ogni Application Domain.
Infatti se in una Console Application eseguite una query con due istanze dello stesso object Context, che esegue la tessa query 4 volte con il primo e altre 4 volte con il secondo, vedete il costo molto alto solo nel primissimo run:
Inoltre come detto, potrei ridurre il costo della fase di generazione delle view, come documentato qui, nel caso il mio application domain venisse creato e distrutto più volte.
Query: preparazione ed esecuzione
Sempre dalla tabella vista in precedenza, si vedono i costi del tempo di preparazione ed esecuzione delle query sono distinti. EF fa in modo che il tempo di preparazione di ogni singola query sia speso una sola volta per query e quindi poi parte del lavoro riutilizzato. Ad ogni modo è abbastanza intuitivo pensare che all’aumentare della complessità della query i tempi di preparazione ed esecuzione aumenteranno inevitabilmente.
Per ottimizzare il tempo complessivo speso per una query è possibile utilizzare delle query compilate ( Documentazione MSDN qui ) . Una query compilata viene creata una volta sola e puoi può essere riutilizzata con diversi parametri.
Trovate un esempio d’utilizzo in questo post del team di prodotto.
Tracking degli oggetti
Un altro aspetto che nella tabella sopra è contraddistinto come low, ma che è proporzionale al numero di oggetti ritornati e quello relativo al tracking, questa funzionalità dell’ ObjectStateManager che tiene traccia della “vita” delle nostre entità: aggiunta, cancellazione, modifica e l’Entity Key viene usata per identificare le istanze degli stessi.
Quando usate come mergeoptions NoTracking, le operazioni di aggiornamento e risoluzione dell’identità della cache dell’ObjectStateManager non vengono eseguite. Questa opzione ha senso se non dobbiamo tracciare operazioni di modifica/cancellazione e inserimento dei nostri oggetti, ne è tipico l’uso in applicazioni ASP.NET in cui si fa il recupero di dati e non si è interessati ad altro.
Entity Data Model per database con tante tabelle
Da quanto visto in precedenza appare anche un altro aspetto che può essere utile investigare : all’aumentare del numero di tabelle del db che fanno parte di uno stesso Entity Data Model (EDM) le performance decrescono. Da questi due post del team (primo e secondo) i suggerimenti sono di pensare di dividere l’EDM in più parti quando il numero di tabelle è elevato, quando cioè si raggiungono le 50-100 tabelle.
La divisione dell EDM in parti con il riutilizzo parziale di tipi tra EDM diversi non è una cosa banale, poichè il designer di Visual Studio non c’aiuta e quindi bisogna operare a mano sul file CSDL. Trovate degli esempi con diverse strategie proprio nei due post che vi segnalo.
Conclusione
L’ottimizzazione delle performance di EF può tenere conto di diversi aspetti, che ho cercato di ricollegare con un po’ di documentazione, direi che può essere utile almeno tenere presente di:
- generare view pre-compilate per ottimizzare lo start-up
- usare compiled query
- usare NoTracking se possibile
- partizionare l’EDM se molto grosso
- …
Link utili:
- Documentazione MSDN
- Exploring the Performance of the ADO.NET Entity Framework - Part 1
- Exploring the Performance of the ADO.NET Entity Framework – Part 2
- Working With Large Models In Entity Framework – Part 1
- Working With Large Models In Entity Framework – Part 2
Comments
Anonymous
April 08, 2009
PingBack from http://www.anith.com/?p=27441Anonymous
October 18, 2009
Dopo aver seguito le istruzioni per ottimizzare il primo avvio di EDM ( http://msdn.microsoft.com/en-us/library/bb896240.aspx) ho rifatto i test di performances ma senza ottenere risultati: Il mio EDM è composto da circa 20 tabelle relazionate tra loro e il problema che riscontro è che la prima chiamata ( query ) impiega 1 minuto ad essere eseguita. Le successive circa 380 ms. Ho inserito le operazioni di prebuild, generato e aggiunto il file .cs e anche i files .msl .ssdl e .csdl Modificato la stringa di connessione e rieseguito il test ma nulla è cambiato. Avete qualche suggerimento??