Condividi tramite


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

Questo case study illustra come usare gli strumenti di profilatura di Visual Studio per identificare e risolvere i problemi di prestazioni in un'applicazione di esempio ASP.NET. Per un confronto degli strumenti di profilatura, vedere Quale strumento scegliere?

Si apprenderà:

  • Come usare gli strumenti di profilatura di Visual Studio per analizzare le prestazioni dell'applicazione.
  • Come interpretare i dati di profilatura per trovare colli di bottiglia.
  • Strategie pratiche per ottimizzare il codice usando contatori .NET, conteggi delle chiamate e dati di temporizzazione.

Applicare queste tecniche per migliorare le proprie applicazioni.

Isolare un case study relativo ai problemi di prestazioni

L'app di esempio ASP.NET esegue query su un database simulato ed è basata sull'esempio di diagnostica.

Sintomi principali delle prestazioni:

  • Basso utilizzo CPU: la CPU non è il collo di bottiglia.
  • Elevato numero di thread nel thread pool: il numero di thread aumenta costantemente, indicando l'esaurimento del pool di thread.
  • Risposta lenta dell'applicazione: l'app risponde lentamente a causa di una mancanza di thread disponibili.

Questo case study usa gli strumenti di profilatura di Visual Studio per individuare e risolvere questi problemi, consentendo di rendere il codice più veloce ed efficiente.

Sfida

La risoluzione di questi problemi comporta diverse sfide:

  • Diagnosi dei colli di bottiglia: un utilizzo ridotto della CPU con prestazioni lente può avere più cause. Un uso efficace degli strumenti di profilatura e dell'interpretazione dell'output è essenziale.
  • 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 è fondamentale per superare queste sfide.

Strategia

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

  • Iniziare monitorando le metriche dei contatori .NET durante la raccolta dei dati sulle prestazioni. Lo strumento contatori .NET di Visual Studio è un buon punto di partenza.
  • Per approfondimenti, raccogliere tracce con strumenti di profilatura aggiuntivi, ad esempio lo strumento di strumentazione per i conteggi delle chiamate e i dati temporali.

La raccolta dati richiede le attività seguenti:

  • Impostare l'app sulla versione di rilascio.
  • Selezionare lo strumento dei Contatori .NET nel Profilatore di Prestazioni (ALT+F2).
  • 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. Per 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: Beginner's guide to optimizing code.For a tutorial on this, see Case study: Beginner's guide to optimizing code.
    • 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 esaurimento del pool di thread.
  • Allocation Rate. Per un'app Web che gestisce le richieste, la frequenza deve essere abbastanza costante.
  • GC Heap Size. Segui questo contatore per vedere se l'utilizzo della memoria cresce continuamente e se c'è una fuga. 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 la CPU Usage è bassa, mentre la ThreadPool Thread Count è relativamente elevata.

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 di saturazione del pool di thread. Il pool di thread è costretto a continuare a generare nuovi thread. L'esaurimento del pool di thread si verifica quando il pool non dispone di thread disponibili per elaborare nuove attività di lavoro, portando le applicazioni a rispondere lentamente.

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

Analizzare i conteggi delle chiamate e i dati di intervallo

Esaminiamo una traccia dello strumento di Strumentazione per vedere se possiamo scoprire di più su cosa sta succedendo 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 .diagsession report che mostra i dati riepilogati. Nella traccia acquisita, usiamo il collegamento Apri dettagli nel report e quindi selezioniamo Flame Graph.

Screenshot di Flame Graph nello strumento di monitoraggio.

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

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

Screenshot dell'albero delle chiamate nello strumento di Strumentazione.

Il percorso del codice con utilizzo più elevato della CPU nell'app viene chiamato percorso critico. L'icona di fiamma del percorso attivo (Screenshot che mostra l'icona del percorso attivo.) consente di identificare rapidamente i problemi di prestazioni che potrebbero essere migliorati.

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

Rispetto al tempo impiegato in altre funzioni, i valori Self e Avg Self per la funzione QueryCustomerDB sono molto elevati. A differenza di Total e Avg Total, i valori Self escludono il tempo trascorso 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 funzione QueryCustomerDB.

Fare doppio clic sulla funzione QueryCustomerDB 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, è possibile risparmiare tempo e lasciare Copilot eseguire la ricerca per noi.

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

Can you identify a performance issue in the QueryCustomerDB method?

Suggerimento

È possibile usare comandi con la barra, ad esempio /optimize per formulare domande efficaci per Copilot.

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

Per risolvere, 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 di 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: Beginner's guide to optimizing code. 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, i thread di cui sono in attesa e i deadlock.

Per ulteriori informazioni sull'esaurimento del pool di thread, vedere Rilevamento dell'esaurimento del pool di thread.

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.