Condividi tramite


Case study: Isolare un problema di prestazioni (C#, Visual Basic, F#)

Usare gli strumenti di profilatura per analizzare i problemi di prestazioni e isolare le aree problematiche. Questo case study usa un'applicazione di esempio con problemi di prestazioni per illustrare come usare gli strumenti di profilatura per migliorare l'efficienza. Per confrontare gli strumenti di profilatura, vedere Quale strumento scegliere?

Questo case study illustra questi argomenti:

  • Come usare gli strumenti di profilatura di Visual Studio per analizzare le prestazioni dell'applicazione.
  • Come interpretare i dati forniti da questi strumenti per identificare i colli di bottiglia delle prestazioni.
  • Come applicare strategie pratiche per ottimizzare il codice, concentrandosi sui contatori .NET, sui conteggi delle chiamate e sui dati di temporizzazione.

Seguire e quindi applicare queste tecniche alle proprie applicazioni per renderle più efficienti e convenienti.

Isolare un case study relativo ai problemi di prestazioni

L'applicazione di esempio in questo case study è un'app ASP.NET che esegue query su un database simulato. L'esempio è basato sull'esempio di diagnostica.

Il problema principale delle prestazioni con l'applicazione di esempio si trova in modelli di codifica inefficienti. L'applicazione presenta un collo di bottiglia delle prestazioni che ne influisce significativamente sull'efficienza. Il problema include i sintomi seguenti:

  • Basso utilizzo CPU: l'applicazione mostra un utilizzo ridotto della CPU, a indicare che la CPU non è il collo di bottiglia.

  • Numero elevato di thread threadpool: il conteggio dei thread è relativamente elevato e in costante aumento, suggerendo la fame del pool di thread.

  • Risposta lenta dell'applicazione: l'applicazione risponde lentamente a causa della mancanza di thread disponibili per elaborare nuovi elementi di lavoro.

Il case study mira a risolvere questi problemi usando gli strumenti di profilatura di Visual Studio per analizzare le prestazioni dell'applicazione. Comprendendo dove e come migliorare le prestazioni dell'applicazione, gli sviluppatori possono implementare ottimizzazioni per rendere il codice più veloce ed efficiente. L'obiettivo finale è migliorare le prestazioni complessive dell'applicazione, rendendolo più efficiente e conveniente per l'esecuzione.

Proposta

Risolvere i problemi di prestazioni nell'applicazione .NET di esempio presenta diverse problematiche. Queste sfide derivano dalla complessità della diagnosi dei colli di bottiglia delle prestazioni. Le sfide principali per risolvere i problemi descritti sono le seguenti:

  • Diagnosi dei colli di bottiglia delle prestazioni: una delle principali sfide consiste nell'identificare accuratamente le cause principali dei problemi di prestazioni. Un utilizzo ridotto della CPU combinato con prestazioni lente può avere più fattori che contribuiscono. Gli sviluppatori devono usare gli strumenti di profilatura in modo efficace per diagnosticare questi problemi, che richiedono una certa comprensione del funzionamento di questi strumenti e di come interpretare l'output.

  • Vincoli di conoscenza e risorse: i team possono affrontare vincoli correlati a conoscenze, competenze e risorse. La profilatura e l'ottimizzazione di un'applicazione richiedono competenze ed esperienze specifiche e non tutti i team possono avere accesso immediato a queste risorse.

Affrontare queste sfide richiede un approccio strategico che combina un uso efficace di strumenti di profilatura, conoscenze tecniche e un'attenta pianificazione e test. Il case study mira a guidare gli sviluppatori attraverso questo processo, fornendo strategie e informazioni dettagliate per superare queste sfide e migliorare le prestazioni dell'applicazione.

Strategia

Ecco una panoramica generale dell'approccio in questo case study:

  • Per avviare l'indagine, osservare le metriche dei contatori .NET durante la raccolta dei dati sulle prestazioni. Analogamente allo strumento Utilizzo CPU, lo strumento contatori .NET di Visual Studio è anche un buon punto di partenza per un'analisi delle prestazioni.
  • Successivamente, per informazioni dettagliate aggiuntive per isolare i problemi o migliorare le prestazioni, è consigliabile raccogliere una traccia usando uno degli altri strumenti di profilatura. Ad esempio, esaminare i conteggi delle chiamate e i dati di temporizzazione usando lo strumento Strumentazione .

La raccolta dati richiede le attività seguenti:

  • Impostazione dell'app su una build di rilascio.
  • Selezionare lo strumento Contatori .NET dal Profiler prestazioni (ALT+F2). I passaggi successivi coinvolgono lo strumento di strumentazione.
  • Dal Profiler prestazioni avviare l'app e raccogliere una traccia.

Controllare i contatori delle prestazioni

Durante l'esecuzione dell'app, si osservano i contatori nello strumento Contatori .NET. Per le indagini iniziali, alcune metriche chiave da tenere d'occhio includono:

  • CPU Usage. Guardare questo contatore per verificare se si verifica un problema di prestazioni con un utilizzo elevato o basso della CPU. Questo può essere un indizio di tipi specifici di problemi di prestazioni. Ad esempio:
    • Con un utilizzo elevato della CPU, usare lo strumento Utilizzo CPU per identificare le aree in cui è possibile ottimizzare il codice. Per un'esercitazione su questo argomento, vedere Case study: Guida per principianti all'ottimizzazione del codice.
    • Con un utilizzo ridotto della CPU, usare lo strumento Strumentazione per identificare i conteggi delle chiamate e il tempo medio della funzione in base all'ora di clock del muro. Ciò può aiutare a identificare problemi quali contesa o fame del pool di thread.
  • Allocation Rate. Per un'app Web che gestisce le richieste, la frequenza deve essere abbastanza costante.
  • GC Heap Size. Guardare questo contatore per verificare se l'utilizzo della memoria è in continua crescita e potenzialmente in perdita. Se sembra elevato, usare uno degli strumenti di utilizzo della memoria.
  • Threadpool Thread Count. Per un'app Web che gestisce le richieste, controllare questo contatore per verificare se il conteggio dei thread è costante o in aumento a un tasso costante.

Di seguito è riportato un esempio che mostra come è CPU Usage basso, mentre è ThreadPool Thread Count relativamente alto.

Screenshot dei contatori visualizzati nello strumento Contatori .NET.

Un numero di thread in costante aumento con un utilizzo ridotto della CPU può essere un indicatore della fame del pool di thread. Il pool di thread è costretto a continuare a ruotare nuovi thread. La fame del pool di thread si verifica quando il pool non dispone di thread disponibili per elaborare nuovi elementi di lavoro e spesso fa sì che le applicazioni rispondano lentamente.

In base al basso utilizzo della CPU e al numero di thread relativamente elevato e all'uso della teoria di un possibile caso di fame del pool di thread, passare all'uso dello strumento strumentazione.

Analizzare i conteggi delle chiamate e i dati di intervallo

Verrà ora esaminata una traccia dello strumento Strumentazione per verificare se è possibile provare a scoprire di più su cosa sta accadendo con i thread.

Dopo aver raccolto una traccia con lo strumento di strumentazione e averla caricata in Visual Studio, è prima necessario controllare la pagina iniziale del report con estensione diagsession che mostra i dati riepilogati. Nella traccia raccolta si usa il collegamento Apri dettagli nel report e quindi si seleziona Flame Graph.

Screenshot di Flame Graph nello strumento Strumentazione.

La visualizzazione Flame Graph mostra che la QueryCustomerDB funzione (mostrata in giallo) è responsabile di una parte significativa del tempo di esecuzione dell'app.

Fare clic con il pulsante destro del mouse sulla QueryCustomerDB funzione e scegliere Visualizza in Albero delle chiamate.

Screenshot dell'albero delle chiamate nello strumento strumentazione.

Il percorso del codice con il maggior utilizzo della CPU nell'app viene chiamato percorso critico. L'icona di fiamma del percorso attivo (Screenshot che mostra l'icona Percorso critico.) può aiutare a identificare rapidamente i problemi di prestazioni che potrebbero essere migliorati.

Nella visualizzazione Albero delle chiamate è possibile notare che il percorso critico include la QueryCustomerDB funzione , che punta a un potenziale problema di prestazioni.

Rispetto al tempo impiegato in altre funzioni, i valori Self e Avg Self per la QueryCustomerDB funzione sono molto elevati. A differenza di Total e Avg Total, i valori Self escludono il tempo impiegato in altre funzioni, quindi questo è un buon posto per cercare il collo di bottiglia delle prestazioni.

Suggerimento

Se i valori Self erano relativamente bassi anziché alti, è probabile che si voglia esaminare le query effettive chiamate dalla QueryCustomerDB funzione.

Fare doppio clic sulla QueryCustomerDB funzione per visualizzare il codice sorgente per la funzione.

public ActionResult<string> QueryCustomerDB()
{
    Customer c = QueryCustomerFromDbAsync("Dana").Result;
    return "success:taskwait";
}

Facciamo una piccola ricerca. In alternativa, possiamo risparmiare tempo e lasciare che Copilot faccia la ricerca per noi.

Se si usa Copilot, selezionare Chiedi a Copilot dal menu di scelta rapida e digitare la domanda seguente:

Can you identify a performance issue in the QueryCustomerDB method?

Suggerimento

È possibile usare i comandi barra, ad esempio /optimize , per creare domande valide per Copilot.

Copilot indica che questo codice chiama un'API asincrona senza usare await. Si tratta del modello di codice sincrona over-asincrono , che è una causa comune di starvation del pool di thread e può bloccare i thread.

Per risolvere il problema, usare await. In questo esempio Copilot fornisce il suggerimento di codice seguente insieme alla spiegazione.

public async Task<ActionResult<string>> QueryCustomerDB()
{
    Customer c = await QueryCustomerFromDbAsync("Dana");
    return "success:taskwait";
}

Se vengono visualizzati problemi di prestazioni relativi alle query di database, è possibile usare lo strumento Database per verificare se determinate chiamate sono più lente. Questi dati possono indicare un'opportunità per ottimizzare le query. Per un'esercitazione che illustra come usare lo strumento Database per analizzare un problema di prestazioni, vedere Case study: Guida per principianti all'ottimizzazione del codice. Lo strumento Database supporta .NET Core con ADO.NET o Entity Framework Core.

Per ottenere visualizzazioni in Visual Studio per il comportamento dei singoli thread, è possibile usare la finestra Stack paralleli durante il debug. Questa finestra mostra i singoli thread insieme alle informazioni sui thread in attesa, sui thread in attesa e sui deadlock.

Per altre informazioni sulla fame del pool di thread, vedere Detecting threadpool starvation.For additional information on thread pool starvation, see Detecting threadpool starvation.

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.