Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
L'ottimizzazione del codice riduce i costi e i tempi di calcolo. Questo case study illustra come usare gli strumenti di profilatura di Visual Studio per identificare e risolvere i problemi di prestazioni in un'applicazione .NET di esempio. Per confrontare gli strumenti di profilatura, vedere Quale strumento scegliere?
Questa guida illustra:
- Come usare gli strumenti di profilatura di Visual Studio per analizzare e migliorare le prestazioni.
- Strategie pratiche per ottimizzare l'utilizzo della CPU, l'allocazione della memoria e le interazioni del database.
Applicare queste tecniche per rendere le proprie applicazioni più efficienti.
Studio di caso di ottimizzazione
L'applicazione .NET di esempio esegue query su un database SQLite di blog e post usando Entity Framework. Esegue molte query, simulando uno scenario reale di recupero dei dati. L'app si basa sull'esempio introduttivo di Entity Framework, ma usa un set di dati più grande.
I principali problemi di prestazioni includono:
- Utilizzo elevato della CPU: calcoli inefficienti o attività di elaborazione aumentano il consumo e i costi della CPU.
- Allocazione di memoria inefficiente: la gestione inadeguata della memoria comporta un'eccessiva garbage collection e prestazioni ridotte.
- Sovraccarichi del database: query inefficienti e chiamate di database eccessive riducono le prestazioni.
Questo case study usa gli strumenti di profilatura di Visual Studio per individuare e risolvere questi problemi, mirando a rendere l'applicazione più efficiente e conveniente.
Sfida
La risoluzione di questi problemi di prestazioni comporta diversi problemi:
- Diagnosi dei colli di bottiglia: l'identificazione delle cause principali dei sovraccarichi elevati di CPU, memoria o database richiede un uso efficace degli strumenti di profilazione e un'interpretazione corretta dei risultati.
- Vincoli di conoscenza e risorse: la profilatura e l'ottimizzazione richiedono competenze ed esperienze specifiche, che potrebbero non essere sempre disponibili.
Un approccio strategico che combina strumenti di profilatura, conoscenze tecniche e test accurati è essenziale per superare queste sfide.
Strategia
Ecco una panoramica generale dell'approccio in questo case study:
- Iniziare con una traccia di utilizzo della CPU usando lo strumento Utilizzo CPU di Visual Studio. Lo strumento Utilizzo CPU di Visual Studio è un buon punto di partenza per le indagini sulle prestazioni.
- Raccogliere tracce aggiuntive per l'analisi di memoria e database:
- Usare lo strumento di allocazione di oggetti .NET per informazioni dettagliate sulla memoria.
- Usare lo strumento Database per esaminare query SQL e tempistiche.
La raccolta dati richiede le attività seguenti:
- Impostare l'app sulla versione di rilascio.
- Selezionare lo strumento Utilizzo CPU in Profiler delle prestazioni (Alt+F2).
- In Performance Profiler avviare l'app e raccogliere una traccia.
Esaminare le aree di utilizzo elevato della CPU
Dopo aver raccolto una traccia con lo strumento Utilizzo CPU e caricarlo in Visual Studio, controllare prima di tutto la pagina iniziale .diagsession report che mostra i dati riepilogati. Usa il collegamento Apri dettagli nel report.
Nella visualizzazione dei dettagli del report aprire la visualizzazione albero delle chiamate. Il percorso del codice con il più elevato utilizzo della CPU nell'app viene chiamato hot path. L'icona di fiamma del percorso critico (
) consente di identificare rapidamente i problemi di prestazioni che potrebbero essere migliorati.
Nella visualizzazione albero delle chiamate è possibile visualizzare un utilizzo elevato della CPU per il metodo GetBlogTitleX nell'app, usando circa 60% condivisione dell'utilizzo della CPU dell'app. Tuttavia, il valore Self CPU per GetBlogTitleX è basso, solo circa 0.10%. A differenza di Total CPU, il valore self CPU esclude il tempo impiegato in altre funzioni, quindi sappiamo di dover esaminare più a fondo l'albero delle chiamate per identificare il reale collo di bottiglia.
GetBlogTitleX effettua chiamate esterne a due DLL LINQ, che utilizzano la maggior parte del tempo della CPU, come dimostrato dai valori Self CPU molto elevati. Questo è il primo indizio che una query LINQ potrebbe essere un'area da ottimizzare.
Per ottenere un albero delle chiamate visualizzato e una visualizzazione diversa dei dati, aprire la visualizzazione Flame Graph. In alternativa, fare clic con il pulsante destro del mouse su GetBlogTitleX e scegliere Visualizza nel flame graph.) Anche in questo caso, sembra che il metodo GetBlogTitleX sia responsabile di gran parte dell'utilizzo della CPU dell'app (mostrato in giallo). Le chiamate esterne alle DLL LINQ vengono visualizzate sotto la casella GetBlogTitleX e usano tutto il tempo di CPU per il metodo.
Raccogliere dati aggiuntivi
Spesso, altri strumenti possono fornire informazioni aggiuntive per facilitare l'analisi e isolare il problema. In questo case study viene adottato l'approccio seguente:
- Esaminare prima di tutto l'utilizzo della memoria. Potrebbe esserci una correlazione tra utilizzo elevato della CPU e utilizzo elevato della memoria, quindi può essere utile esaminare entrambi per isolare il problema.
- Poiché sono state identificate le DLL LINQ, verrà esaminato anche lo strumento Database.
Controllare l'utilizzo della memoria
Per vedere cosa sta accadendo con l'app in termini di utilizzo della memoria, viene raccolta una traccia usando lo strumento di allocazione di oggetti .NET (per C++, è possibile usare invece lo strumento Utilizzo memoria). La visualizzazione Albero delle chiamate nella traccia della memoria mostra il percorso principale e aiuta a identificare un'area di alto utilizzo della memoria. Nessuna sorpresa a questo punto, il metodo GetBlogTitleX sembra generare un sacco di oggetti! Oltre 900.000 allocazioni di oggetti, infatti.
La maggior parte degli oggetti creati sono stringhe, matrici di oggetti e Int32s. È possibile vedere come questi tipi vengono generati esaminando il codice sorgente.
Controllare la query nello strumento Database
In Performance Profiler selezionare lo strumento Database anziché l'utilizzo della CPU (o selezionare entrambi). Dopo aver raccolto una traccia, aprire la scheda Query nella pagina di diagnostica. Nella scheda Query per la traccia database è possibile visualizzare la prima riga che mostra la query più lunga, 2446 ms. La colonna Record mostra quanti record la query legge. È possibile usare queste informazioni per un confronto successivo.
Esaminando l'istruzione SELECT generata da LINQ nella colonna Query, viene identificata la prima riga come query associata al metodo GetBlogTitleX. Per visualizzare la stringa di query completa, espandere la larghezza della colonna. La stringa di query completa è:
SELECT "b"."Url", "b"."BlogId", "p"."PostId", "p"."Author", "p"."BlogId", "p"."Content", "p"."Date", "p"."MetaData", "p"."Title"
FROM "Blogs" AS "b" LEFT JOIN "Posts" AS "p" ON "b"."BlogId" = "p"."BlogId" ORDER BY "b"."BlogId"
Si noti che l'app sta recuperando molti valori di colonna qui, forse più di quanto sia necessario. Si esaminerà ora il codice sorgente.
Ottimizzare il codice
È il momento di esaminare il codice sorgente GetBlogTitleX. Nello strumento Database fare clic con il pulsante destro del mouse sulla query e scegliere Vai al file di origine. Nel codice sorgente per GetBlogTitleXè disponibile il codice seguente che usa LINQ per leggere il database.
foreach (var blog in db.Blogs.Select(b => new { b.Url, b.Posts }).ToList())
{
foreach (var post in blog.Posts)
{
if (post.Author == "Fred Smith")
{
Console.WriteLine($"Post: {post.Title}");
}
}
}
Questo codice usa cicli foreach per cercare nel database tutti i blog con "Fred Smith" come autore. Esaminandolo, è possibile notare che molti oggetti vengono generati in memoria: una nuova matrice di oggetti per ogni blog nel database, stringhe associate per ogni URL e valori per le proprietà contenute nei post, ad esempio l'ID blog.
Vengono eseguite alcune ricerche e vengono fornite alcune raccomandazioni comuni per ottimizzare le query LINQ. In alternativa, è possibile risparmiare tempo e lasciare Copilot eseguire la ricerca per noi.
Quando usiamo Copilot, selezioniamo Chiedi a Copilot dal menu di scelta rapida e digitiamo la seguente domanda:
Can you make the LINQ query in this method faster?
Suggerimento
È possibile usare comandi con barra, ad esempio /optimize, per formulare buone domande per Copilot.
In questo esempio Copilot fornisce le modifiche di codice suggerite seguenti, insieme a una spiegazione.
public void GetBlogTitleX()
{
var posts = db.Posts
.Where(post => post.Author == "Fred Smith")
.Select(post => post.Title)
.ToList();
foreach (var postTitle in posts)
{
Console.WriteLine($"Post: {postTitle}");
}
}
Questo codice include diverse modifiche per ottimizzare la query:
- Aggiunta della clausola
Whereed eliminazione di uno dei cicliforeach. - Proiettata solo la proprietà Title nell'istruzione
Select, che è sufficiente in questo esempio.
Successivamente, si esegue il nuovo test usando gli strumenti di profilatura.
Risultati
Dopo aver aggiornato il codice, viene eseguito di nuovo lo strumento Utilizzo CPU per raccogliere una traccia. La visualizzazione albero delle chiamate mostra che GetBlogTitleX è in esecuzione solo 1754 ms, usando 37% del totale della CPU dell'app, un miglioramento significativo rispetto a 59%.
Passare alla visualizzazione Flame Graph per visualizzare un'altra visualizzazione che mostra il miglioramento. In questa visualizzazione, GetBlogTitleX usa anche una parte più piccola della CPU.
Controlla i risultati nella traccia dello strumento Database, e solo due record vengono letti usando questa query, anziché 100.000. Inoltre, la query è molto semplificata ed elimina l'OPERAZIONE LEFT JOIN non necessaria generata in precedenza.
Successivamente, ricontrolliamo i risultati nello strumento di allocazione degli oggetti di .NET e notiamo che GetBlogTitleX è responsabile solo di 56.000 allocazioni di oggetti, con una riduzione di quasi 95% rispetto ai 900.000!
Iterare
Potrebbero essere necessarie più ottimizzazioni ed è possibile continuare a eseguire l'iterazione con le modifiche del codice per vedere quali modifiche migliorano le prestazioni e consentono di ridurre i costi di calcolo.
Passaggi successivi
Gli articoli e i post di blog seguenti forniscono altre informazioni per imparare a usare in modo efficace gli strumenti per le prestazioni di Visual Studio.
- Caso di studio : Isolamento di un problema di prestazioni
- case study : prestazioni doppie in meno di 30 minuti
- Miglioramento delle prestazioni di Visual Studio con il nuovo strumento di strumentazione