131 messaggi dal 06 giugno 2011
Ciao a tutti e da molto che non scrivo, ma vi seguo sempre ;)

Ho dei problemi di perfomance, quando utilizzo ef.

Cerco di spiegarmi meglio, se devo ottenere circa 10.000 record, tramite la chiamat sql perde qualche millisecondo 5/10ms, mentre se la faccio la stessa chiamata tramite ef. e aggiungo toList perde quasi un secondo, è normale?
11.886 messaggi dal 09 febbraio 2002
Contributi
Ciao,
piacere di risentirti :)


tramite la chiamat sql perde qualche millisecondo 5/10ms, mentre se la faccio la stessa chiamata tramite ef. e aggiungo toList perde quasi un secondo, è normale?

Sì, *potrebbe* essere normale ma dipende da cosa hai misurato. Per esempio, nei 5/10 ms c'è anche la lettura del DataReader o quello è solo il tempo di esecuzione della query?

Quando ci sono disparità di prestazioni, di solito, è perché acquisisci delle funzionalità aggiuntive.
In questo caso, i millisecondi extra richiesti da Entity Framework li stai spendendo per "comprare" la possibilità di effettuare query LINQ e di tracciare le eventuali modifiche che apporterai alle entità che si materializzano nel DbContext.

Per esempio, se devi solo presentare una lista di dati a schermo e non hai bisogno di tracciare le modifiche alle entità, allora puoi ottimizzare le prestazioni della query LINQ con l'extension method .AsNoTracking().
Trovi qui un confronto sintetico delle prestazioni.
http://www.c-sharpcorner.com/UploadFile/ff2f08/entity-framework-and-asnotracking/

Se vuoi ottenere prestazioni ancora migliori, dovrai scegliere di sacrificare qualcos'altro. Per esempio, potresti decidere di rinunciare alle query LINQ allo scopo di ottenere prestazioni migliori, anche se questo significherà mettersi a scrivere le query SQL a mano. Quindi, in questo specifico caso, potresti rinunciare ad EF ed avvalerti invece di un micro-ORM come Dapper che si occupa giusto di inviare una query SQL al database e di mappare i records letti dal DataReader sulle tue classi di modello.

Proprio a questo proposito c'è un articolo di Julie Lerman nell'edizione dell'MSDN Magazine di questo mese (dovrebbero pubblicarlo tra qualche giorno, se non hai accesso all'edizione PDF).

In sintesi: ogni situazione richiede l'uso dello strumento appropriato, che si riesce ad identificare solo dopo aver valutato i vari compromessi tra funzionalità è prestazioni.

ciao,
Moreno

Enjoy learning and just keep making
131 messaggi dal 06 giugno 2011
Si avevo provato anche con notracking ma non è cambiato molto.
Ti spiego la mia situazione ho creato un gestionale abbastanza grande e complesso, ho utilizzato ef 6.1 code first, c#, wpf, mvvm e devexpress come librerie di terze parti.

Adesso sono arrivato al punto che devo migliorare la perfomance di tutto il gestionale perchè non è reattivo perde un po troppo tempo nell'elaborare alcune richieste anche visualizzarle solo a video.

Adesso non so se un altro orm come nhibernate è piu veloce ma cambiare orm in questo momento non credo sia la scelta giusta perche significherebbe stravolgere l'applicazione.

Quindi se ef serve qualche ms in piu ok, ma vedo che ce troppa ma troppa differenza di seguito ti faccio l esempio di un codice che sto testando per la velocita:



                sw0.Start();
              
                var listaFiltratata0 = (from x in newContex.CorpoContabilita
                                       where DbFunctions.TruncateTime(x.DataRegistrazione) >= DataIniziale &&
                               DbFunctions.TruncateTime(x.DataRegistrazione) <= DataFinale &&
                                               x.Automatismo != "I" && x.ExtraContabile == false &&
                                               (x.IdIntraCee == null || x.IdIntraCee == Guid.Empty ?
                                               x.TipoConto == "FO" || x.TipoConto == "CL" ? !string.IsNullOrEmpty(x.CodiceIva) : x.TipoConto != "FO" &&
                                               x.TipoConto != "CL" : x.Sezionale == "A") &&
                                               x.Tipo == ListaTipoSelezionato.Codice select x).AsEnumerable();

                sw0.Stop();
                Console.WriteLine("LISTAFILTRATA:"+ sw0.ElapsedMilliseconds);

           sw0.Start();

                var listaFiltratata1 = (from x in newContex.CorpoContabilita
                                        where DbFunctions.TruncateTime(x.DataRegistrazione) >= DataIniziale &&
                                DbFunctions.TruncateTime(x.DataRegistrazione) <= DataFinale &&
                                                x.Automatismo != "I" && x.ExtraContabile == false &&
                                                (x.IdIntraCee == null || x.IdIntraCee == Guid.Empty ?
                                                x.TipoConto == "FO" || x.TipoConto == "CL" ? !string.IsNullOrEmpty(x.CodiceIva) : x.TipoConto != "FO" &&
                                                x.TipoConto != "CL" : x.Sezionale == "A") &&
                                                x.Tipo == ListaTipoSelezionato.Codice
                                        select x).ToList();

                sw0.Stop();
                Console.WriteLine("LISTAFILTRATA tolist:" + sw0.ElapsedMilliseconds);


Considerate che CorpoContabilita è 8000 record.

la prima query la fa in 1 ms , la seconda in 650ms c'è un enorme differenza.

io questo non capisco...
Modificato da brux88 il 02 maggio 2016 09.16 -
11.886 messaggi dal 09 febbraio 2002
Contributi
ciao,
di fatto la prima query non viene eseguita, perché non hai manifestato l'intenzione di volerne leggere i risultati con il .ToList() o con un foreach.
Leggi a proposito della deferred execution delle query LINQ.
https://blogs.msdn.microsoft.com/charlie/2007/12/10/linq-and-deferred-execution/

Per migliorare un po' le prestazioni potresti:
  • Restituire molti meno record, diciamo massimo 50, perché l'utente non visualizzerà mai 8000 record a colpo d'occhio. Usa delle liste paginate nell'interfaccia grafica.
  • Cerca di usare gli indici nel db il più possibile. Ad esempio, questa condizione:
    DbFunctions.TruncateTime(x.DataRegistrazione) <= DataFinale
    

    che non sfrutta l'eventuale indice presente sulla colonna DataRegistrazione, potresti riscriverlo in questo modo:
    var dataFinale = DataFinale.AddDays(1);
    ...
    x.DataRegistrazione < dataFinale
    
  • Se stai usando l'approccio code-first, ecco come creare indici sulle colonne in cui ritieni opportuno crearli.
    https://msdn.microsoft.com/en-us/data/jj591583.aspx#Index
  • Logga le query inviate da EF ed eseguile in SQL Server Management Studio e visualizza il piano di esecuzione. Quello ti aiuterà a capire come può essere ottimizzata la query SQL (e quindi anche la query LINQ).
    https://msdn.microsoft.com/en-us/data/dn469464.aspx#Log


ciao,
Moreno

Enjoy learning and just keep making
131 messaggi dal 06 giugno 2011
ciao, grazie per la risposta e per i link, darò subito un occhiata.
un ultima cosa perche:
DbFunctions.TruncateTime(x.DataRegistrazione) <= DataFinale


non gestisce l'eventuale in indice e invece :

var dataFinale = DataFinale.AddDays(1);
...
x.DataRegistrazione < dataFinale


lo gestisce?
11.886 messaggi dal 09 febbraio 2002
Contributi
ciao,

brux88 ha scritto:

perche (codice) non gestisce l'eventuale in indice e invece (codice) lo gestisce?

Perché quando usi delle funzioni sulle colonne, chiedi al motore database di usare un valore calcolato.
Dato che il valore calcolato non è quello fisicamente presente nei campi che è stato indicizzato, allora il motore sarà costretto a fare uno scan, ovvero ad eseguire la funzione sui campi di tutte le righe della tabella per riuscire a trovare quello/i che soddisfano la tua condizione.

Condizioni del genere, che non riescono a sfruttare gli indici, sono dette non-Sargable.

The typical situation that will make a SQL query non-sargable is to include in the WHERE clause a function operating on a column value.

La funzione che hai usato tu, nel caso specifico, è quella che tronca l'orario dal valore del campo datetime.

In realtà va precisato che, anche se hai usato una condizione non-sargable, il motore database è abbastanza evoluto che potrebbe convertirtela automaticamente in una versione sargable simile a quella che ti suggerivo.
Da questo articolo, al paragrafo "Functions on indexed columns in predicates" si legge:

It's worth noting that SQL Server is getting "smarter" as it evolves. For example, consider the following query, which uses the CAST function on the indexed sale_date column:

SELECT SUM(sale_amount) AS total_sales
FROM Sales
WHERE CAST(sale_date AS DATE) = '20090101';

If you run this query on SQL 2005 or earlier, you'll see an index scan. However, on SQL Server 2008 you'll see an index seek, despite the use of the CAST function. The execution plan reveals that the predicate is transformed into something like the following:

SELECT SUM(sale_amount) AS total_sales
FROM Sales
WHERE sale_date >= '20090101'
AND sale_date < '20090102';

Per verificare che sia questo il caso, dovresti loggare la query prodotta da EF e provare a vedere qual è il suo piano d'esecuzione con SQL Server Management Studio. E' una cosa interessante da conoscere ma, se vuoi evitartelo ma vuoi essere assolutamente sicuro che nel tuo caso l'indice venga usato, tanto vale usare direttamente la versione Sargable. Infatti, il consiglio che dà l'articolo è questo:

However, in general, you should use SARGable predicates where possible, rather than rely on the evolving intelligence of the optimizer.


ciao,
Moreno
Modificato da BrightSoul il 03 maggio 2016 22.32 -

Enjoy learning and just keep making
131 messaggi dal 06 giugno 2011
grazie mille per le delucidazioni, approfondirò, ho dato un occhiata a dapper, mi sa che lo integrero nel mio progetto con ef. Cosi dove mi serve piu prestazioni usero esso.
11.886 messaggi dal 09 febbraio 2002
Contributi
brux88 ha scritto:

ho dato un occhiata a dapper, mi sa che lo integrero nel mio progetto con ef

Certamente, puoi usarli entrambi. Ciascuno ha il proprio ambito di utilizzo in cui eccelle.
Ipotizza di dover effettivamente estrarre 8000 record, perché ad esempio li devi rappresentare come data points su un grafico a linee: in quel caso puoi usare Dapper o addirittura ADO.NET nudo e crudo.

Ecco l'articolo di cui ti parlavo l'altro giorno:
https://msdn.microsoft.com/en-us/magazine/mt703432

ciao,
Moreno

Enjoy learning and just keep making

Torna al forum | Feed RSS

ASPItalia.com non è responsabile per il contenuto dei messaggi presenti su questo servizio, non avendo nessun controllo sui messaggi postati nei propri forum, che rappresentano l'espressione del pensiero degli autori.