Sto leggendo 40.000 piccoli oggetti / righe da SQLite con EF core e ci vogliono 18 secondi, che è troppo lungo per la mia app UWP. Quando ciò accade, l'utilizzo della CPU su un singolo core raggiunge il 100%, ma la velocità di lettura del disco è di circa l'1%.
var dataPoints = _db.DataPoints.AsNoTracking().ToArray();
Senza AsNoTracking()
il tempo impiegato è ancora più lungo.
DataPoint
è un piccolo POCO con alcune proprietà primitive. La quantità totale di dati che sto caricando è 4,5 MB.
public class DataPointDto
{
[Key]
public ulong Id { get; set; }
[Required]
public DateTimeOffset TimeStamp { get; set; }
[Required]
public bool trueTime { get; set; }
[Required]
public double Value { get; set; }
}
Domanda: esiste un modo migliore per caricare questi oggetti o sono bloccato con questo livello di prestazioni?
Curiosità: x86 richiede 11 secondi, x64 richiede 18. "Ottimizza codice" si rade un secondo. L'uso di Async
tempo di esecuzione a 30 secondi.
La maggior parte delle risposte segue la comune saggezza di caricare meno dati, ma in alcune circostanze come qui è assolutamente positivo caricare un sacco di entità. Quindi come lo facciamo?
Causa di scarse prestazioni
È inevitabile che questa operazione duri così a lungo? Beh, non è così. Stiamo caricando solo un megabyte di dati dal disco, la causa delle scarse prestazioni è che i dati sono suddivisi su 40.000 piccole entità. Il database può gestirlo, ma il framework delle entità sembra faticare a creare tutte quelle entità, modificare il tracciamento, ecc. Se non intendiamo modificare i dati, possiamo fare molto.
Carica solo una proprietà, quindi ottieni un elenco di primitive.
List<double> dataPoints = _db.DataPoints.Select(dp => dp.Value).ToList();
Ciò bypassa tutta la creazione di entità normalmente eseguita dal framework di entità. Questa query ha richiesto 0,4 secondi, rispetto ai 18 secondi della query originale. Stiamo parlando del miglioramento di 45 (!) Volte.
Naturalmente la maggior parte delle volte abbiamo bisogno di qualcosa di più di una semplice serie di primitive. Possiamo creare nuovi oggetti direttamente nella query LINQ. Il framework Entity non creerà le entità che normalmente farebbe e l'operazione verrà eseguita molto più velocemente. Possiamo usare oggetti anonimi per comodità.
var query = db.DataPoints.Select(dp => new {Guid ID = dp.sensorID, DateTimeOffset Timestamp = dp.TimeStamp, double Value = dp.Value});
Questa operazione richiede 1,2 secondi rispetto ai 18 secondi per il normale recupero della stessa quantità di dati.
Ho scoperto che nel mio caso l'utilizzo di Tuples anziché di tipi anonimi migliora leggermente le prestazioni, la seguente query eseguita circa il 30% più velocemente:
var query = db.DataPoints.Select(dp => Tuple.Create(dp.sensorID, dp.TimeStamp, dp.Value));
puoi utilizzare una tecnica diversa per caricare tutti i tuoi articoli.
puoi creare la tua logica per caricare parti dei dati mentre l'utente sta scorrendo il ListView (immagino tu lo stia usando).
fortunatamente UWP è un modo semplice per fare questa tecnica. Caricamento incrementale, vedere la documentazione e l'esempio