Individuare con PerfView e dotnet-trace la causa di un rallentamento — introduzione pratica all'analisi delle prestazioni .NET
· Go Komura · PerfView, dotnet-trace, ETW, Analisi delle prestazioni, .NET, CSharp, Indagine sui bug, Sviluppo Windows, Consulenza tecnica
Nel precedente articolo, Introduzione al registro eventi di Windows e a ETW, abbiamo spiegato ETW e la definizione di eventi personalizzati con EventSource, lasciando PerfView fuori ambito. Qui riprendiamo da quel punto. Vedremo come usare PerfView e dotnet-trace per individuare la funzione esatta responsabile quando un’app aziendale è lenta, satura la CPU o si blocca occasionalmente.
L’argomento è CPU e tempo di risposta. Per la memoria che cresce continuamente, vedere Distinguere pause GC e perdite di memoria in .NET; per i crash, Leggere crash dump con WinDbg + SOS.
1. Prima la conclusione
- Esistono due tipi di lentezza: CPU satura, CPU bound, e CPU inattiva ma app in attesa, blocked. La prima richiede campionamento CPU; la seconda ThreadTime, cioè context switch. La raccolta CPU predefinita non mostra la causa del secondo caso.1
- Se non siete sicuri, iniziate con dotnet-trace. È multipiattaforma e basato su EventPipe; lo stesso utente che avvia il processo può raccogliere senza privilegi di amministratore. I file
.nettracesi aprono in PerfView e Visual Studio.23 - Usate PerfView quando servono l’intera macchina, codice nativo o tempo bloccato. ETW include eventi kernel e stack nativi, ma avviare una sessione richiede privilegi di amministratore.3
- Il campionamento CPU avviene per impostazione predefinita una volta ogni millisecondo, per processore. Leggete un campione come circa 1 ms di CPU e raccogliete almeno 1.000 campioni, idealmente circa 5.000, prima di concludere.1
- La lettura dipende dalla differenza fra inclusive e exclusive. Exclusive indica dove la funzione stessa consuma CPU; inclusive indica che qualcosa più in basso nell’albero di chiamate è pesante.
- Definite «lento» come una singola operazione misurabile. «L’app è generalmente lenta» non è una raccolta leggibile; fissate invece qualcosa come «questo report richiede 40 secondi» e misurate quella operazione.
| Sintomo | Primo strumento | Cosa osservare |
|---|---|---|
| CPU costantemente alta | dotnet-trace collect / CPU Stacks di PerfView | Funzioni con exclusive alto |
| CPU bassa ma elaborazione lenta o bloccata | PerfView con ThreadTime | Quale thread attende che cosa |
| Si vuole prima una tendenza generale | dotnet-counters | CPU, frequenza GC, coda ThreadPool |
| Si sospetta troppo GC | dotnet-counters → dotnet-trace, eventi GC | Numero e pause GC, punti con più allocazioni |
| Si vuole localizzare una specifica operazione business | Eventi EventSource + strumenti precedenti | Tempo fra eventi e stack in quell’intervallo |
2. ETW ed EventPipe: il rapporto fra gli strumenti
Gli strumenti sembrano molti, ma le fondamenta sono due.
- ETW, Event Tracing for Windows: infrastruttura di traccia di tutto il sistema operativo. Registra kernel e applicazioni sulla stessa linea temporale, ma richiede privilegi di amministratore e funziona solo su Windows.3
- EventPipe: meccanismo di traccia incorporato nel runtime .NET. Non richiede amministratore e si comporta allo stesso modo su tutti i sistemi, ma vede solo codice gestito e runtime: non cattura eventi kernel o stack nativi.2
| dotnet-trace, EventPipe | PerfView, ETW | |
|---|---|---|
| Privilegi amministrativi | Non richiesti per un processo dello stesso utente | Richiesti |
| Destinazione | Un processo .NET specificato | Intera macchina, processi e kernel |
| Stack nativi | Non acquisibili | Acquisibili |
| Tempo bloccato / context switch | Non acquisibile | Acquisibile con ThreadTime |
| Sistemi operativi | Windows / Linux / macOS | Solo Windows |
Da qui deriva il flusso naturale: iniziare con una raccolta leggera in dotnet-trace e passare alla raccolta ETW completa con PerfView se i dati non bastano.
3. Prima della traccia — dieci minuti con dotnet-counters
Prima di raccogliere una traccia, spesso è più rapido osservare le metriche principali del runtime con dotnet-counters.4
dotnet tool install --global dotnet-counters
dotnet-counters ps
dotnet-counters monitor --process-id <PID> --counters System.Runtime
Osservate uso CPU, dimensione e numero di GC dell’heap, lunghezza della coda e numero di thread del ThreadPool, oltre al numero di eccezioni. Se vedete «GC più volte al secondo» o «coda ThreadPool in crescita», sapete se la raccolta successiva deve concentrarsi su allocazioni o blocchi. La lettura delle tendenze di memoria è spiegata in Distinguere pause GC e perdite di memoria in .NET.
4. Raccogliere una traccia con dotnet-trace
dotnet-trace è uno strumento EventPipe.5 Installazione e raccolta di base:
dotnet tool install --global dotnet-trace
dotnet-trace ps
dotnet-trace collect --process-id <PID> --duration 00:00:00:30
Senza opzioni, raccoglie con il profilo predefinito, che include gli eventi principali del runtime e il campionamento dei thread. Il vecchio profilo cpu-sampling è stato ritirato; oggi il predefinito combina dotnet-common e dotnet-sampled-thread-time.5 Potete anche scegliere --profile gc-verbose per GC e campionamento allocazioni, oppure --profile gc-collect per registrare soltanto le occorrenze GC con basso overhead.
Per raccogliere anche il provider EventSource personalizzato dell’articolo precedente, aggiungete --providers.
dotnet-trace collect --process-id <PID> --providers KomuraSoft-OrderService
Il risultato è un file .nettrace. Potete aprirlo in Visual Studio o PerfView, oppure convertirlo in formato speedscope e visualizzarlo nel browser.5
dotnet-trace convert trace.nettrace --format Speedscope
Il formato speedscope è un flame graph leggero visualizzabile in speedscope.app, utile per capire intuitivamente dove vada il tempo e sotto quale albero di chiamate. Per arrivare alla funzione esatta, lo stack viewer di PerfView è più potente.
5. Indagare la CPU con PerfView — leggere lo Stack Viewer
PerfView è uno strumento gratuito di analisi delle prestazioni pubblicato da Microsoft. Si avvia scaricando il singolo file PerfView.exe dalla pagina delle release GitHub, senza installazione.6
5.1 Raccolta
Nella GUI, aprite «Collect > Collect», fate clic su Start Collection, riproducete l’operazione bersaglio e poi fate clic su Stop Collection. Da riga di comando:
PerfView collect /nogui /acceptEULA /maxCollectSec:30
Eseguite come amministratore, perché PerfView deve avviare una sessione ETW.3 La raccolta predefinita include un campione CPU di 1 millisecondo, con stack di chiamate, su ogni processore e gli eventi CLR principali.1 L’overhead di questa configurazione è normalmente di pochi punti percentuali.7
5.2 Leggere CPU Stacks
Aprite il risultato raccolto, .etl.zip, selezionate il processo bersaglio in «CPU Stacks» e si apre lo stack viewer. Nella scheda By Name guardate subito queste colonne:
- Exc, exclusive: numero di campioni in cui stava eseguendo la funzione stessa. È il luogo che consuma realmente CPU.
- Inc, inclusive: totale della funzione e di tutto ciò che chiama. Mostra che qualcosa più in basso nell’albero è pesante.
Il metodo standard è ordinare per Exc e chiedersi dall’alto «è il mio codice o runtime/libreria?». Se il vostro codice è alto nella lista Exc, l’algoritmo o ciclo di quella funzione è il bersaglio diretto. Se è alta invece un’interna di System.String o un serializzatore JSON, risalite dalla scheda Callers usando Inc e individuate da dove nel vostro codice viene chiamata con tanta frequenza.
Controllate anche il numero di campioni. I campioni CPU sono statistiche a intervallo di 1 ms e, con un numero basso, domina il caso. Raccogliete almeno 1.000 campioni, idealmente circa 5.000. Se non bastano, prolungate la raccolta ripetendo l’operazione bersaglio.1
Se una funzione del vostro modulo compare solo come indirizzo, i simboli PDB non sono stati risolti. Conservare i PDB degli artefatti di build è ciò che rende possibile l’indagine. Vedere Che cos’è un PDB, Program Database?.
6. «Lento anche se la CPU è inattiva» — tempo bloccato con ThreadTime
Più della metà delle lentezze reali non riguarda la CPU, ma l’attesa: lock, risposta DB o HTTP, I/O di file, attesa simile a deadlock tramite Task.Result. Nei campioni CPU questa classe di problemi appare come tempo in cui la CPU non veniva usata, perciò la raccolta predefinita non ne rivela la causa.
Attivando Thread Time in PerfView si registrano anche gli eventi di context switch. Si può seguire nella stessa vista quanto tempo ogni thread usi CPU e quanto resti bloccato.1
PerfView collect /nogui /acceptEULA /threadTime /maxCollectSec:30
Per l’analisi aprite «Thread Time Stacks». È lo stesso stack viewer di CPU Stacks, ma i campioni includono BLOCKED_TIME, tempo bloccato, oltre al tempo CPU. Restringetevi all’intervallo dell’operazione lenta e guardate quale stack, cioè quale attesa, accumuli BLOCKED_TIME per il thread che svolgeva il lavoro. Potete arrivare così a una spiegazione come «35 dei 40 secondi totali sono stati spesi in attesa di questa chiamata HTTP».
Quando l’interfaccia smette di rispondere per alcuni secondi dopo un’azione, il nucleo del problema è quasi sempre il thread UI bloccato. Una volta identificato con ThreadTime ciò che lo blocca, la correzione definitiva è riprogettare l’attesa sincrona come asincrona. Per il lato progettuale, vedere Scheda sintetica su async e thread UI in WPF/WinForms.
Poiché ThreadTime registra un evento per ogni context switch, il volume di dati è sensibilmente maggiore della raccolta predefinita. Mantenete breve la finestra di raccolta ed evitate ThreadTime prolungato in produzione.
7. Unirlo a EventSource — capire in termini di business «quale sezione è lenta»
CPU Stacks e Thread Time sono aggregati sull’intero processo. Se volete suddividere una singola operazione di business, per esempio «dove va il tempo in questa esecuzione di elaborazione ordine», entrano in gioco gli eventi Start/Stop di EventSource dell’articolo precedente.
- Nell’app, strumentate eventi come
OrderProcessingStarteOrderProcessingStop; per l’implementazione vedere l’articolo precedente. - Verificate la temporizzazione dell’evento nella vista «Events» di PerfView e restringete l’intervallo dello stack viewer, filtro Start/End, a quella finestra.
- Leggete in quel periodo la ripartizione fra CPU e tempo bloccato.
Così si analizza isolatamente «quella singola esecuzione lenta» senza sommergerla nel rumore delle esecuzioni normali. Gli eventi personalizzati possono essere raccolti anche con dotnet-trace; dopo avere aggiunto la strumentazione, la stessa tecnica funziona su sviluppo e produzione. Nella pratica, la difficoltà dell’indagine dipende meno dall’abilità con lo strumento che dalla presenza di punti di osservazione nell’app.
Riepilogo
- Il punto di partenza è dividere «lento» in CPU-bound e bloccato. Il primo appare nel campionamento CPU, 1 campione ≈ 1 ms; il secondo nella raccolta ThreadTime.
- La divisione di base è dotnet-trace prima, senza privilegi di amministratore, e PerfView quando servono intera macchina, codice nativo o tempo bloccato. Raccogliere con dotnet-trace e leggere in PerfView è una combinazione standard.
- Il nucleo della lettura dello stack viewer è la distinzione fra exclusive e inclusive; verificate sempre di avere abbastanza campioni.
- Eventi EventSource personalizzati che segnino i confini di un’operazione business alzano ulteriormente la precisione dell’indagine.
Komura Software LLC svolge indagini sui problemi di prestazioni delle app aziendali Windows — lentezza, blocchi e CPU satura — oltre a consulenza sulla strumentazione e sul design che rendono un’app più misurabile. Se avete procedura di riproduzione e file di traccia, possiamo occuparci anche della sola analisi.
Articoli correlati
- Introduzione al registro eventi di Windows e a ETW — inserire i log delle app aziendali nel meccanismo standard del sistema operativo
- Distinguere pause GC e perdite di memoria in .NET — passaggi pratici per osservare, confrontare e dimostrare la memoria in crescita
- Leggere crash dump con WinDbg + SOS — introduzione pratica all’analisi dopo la raccolta
- Introduzione alla raccolta di crash dump di Windows — WER/ProcDump/WinDbg
- Che cos’è un PDB, Program Database? — informazioni di debug, simboli e Source Link
- Come confrontare correttamente la velocità di versioni diverse di un programma Windows
- Scheda sintetica su async e thread UI in WPF/WinForms
Ambiti di consulenza correlati
- Indagine sui bug e analisi della causa principale
- Sviluppo di app Windows
- Consulenza tecnica e revisione del design
- Contatti
Riferimenti
-
GitHub, PerfView User’s Guide. La raccolta predefinita usa un campione CPU con stack ogni millisecondo per processore; per concludere sono desiderabili circa 1.000-5.000 campioni; Thread Time raccoglie context switch e consente l’analisi del tempo bloccato. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, EventPipe Overview. EventPipe traccia app .NET senza dipendere da un componente ad alto privilegio come l’amministratore; il suo ambito è codice gestito e runtime, non eventi kernel né stack nativi. ↩ ↩2
-
Microsoft Learn, Collect and View EventSource Traces. La raccolta di tracce ETW richiede sempre privilegi di amministratore; Visual Studio e PerfView possono aprire file .nettrace. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, dotnet-counters diagnostic tool. Monitoraggio di CPU, GC, ThreadPool e altre metriche di un processo in esecuzione tramite contatori System.Runtime. ↩
-
Microsoft Learn, dotnet-trace diagnostic tool. Uso di collect, profili predefiniti dotnet-common / dotnet-sampled-thread-time, ritiro di cpu-sampling, profili gc-verbose / gc-collect e conversione in Speedscope / Chromium. ↩ ↩2 ↩3
-
GitHub, microsoft/perfview. PerfView è uno strumento gratuito per indagare problemi di prestazioni CPU e memoria; l’eseguibile è disponibile direttamente nella pagina delle release. ↩
-
GitHub, microsoft/perfview Issue #598. Un maintainer di PerfView indica un overhead della raccolta predefinita intorno al 3% e la possibilità di ridurre il carico regolando l’intervallo con /CpuSampleMSec. ↩
Articoli correlati
Articoli recenti con gli stessi tag per approfondire argomenti vicini.
Introduzione al registro eventi di Windows e a ETW — usare per le app aziendali i meccanismi standard del sistema operativo
La vostra app aziendale Windows usa soltanto file di log? Registro eventi ed ETW sono registrazioni visibili a un livello diverso, per il...
Oltre appsettings.json — Guida pratica alla gestione della configurazione nelle applicazioni aziendali Windows (impostazioni per ambiente, segreti e percorsi di scrittura)
Uno sguardo pratico alla gestione della configurazione nelle applicazioni aziendali Windows: stratificazione di appsettings.json, scelta ...
Stampa e output PDF nelle applicazioni aziendali Windows — come scegliere tra System.Drawing.Printing, WPF e librerie di report
Organizza in una tabella decisionale la stampa WinForms con PrintDocument, la stampa WPF con FlowDocument/FixedDocument e le alternative ...
Google Ads BtoB con un budget ridotto — come progettare e gestire una routine settimanale efficace con poche decine di migliaia di yen al mese
Guida pratica a Google Ads per aziende BtoB che vogliono iniziare con poche decine di migliaia di yen al mese. Illustra il funzionamento ...
Far comparire il sito nelle ricerche con il nome della zona — guida pratica alla SEO locale per PMI (pagine per area e Google Business Profile)
Per le PMI il cui sito non compare nelle ricerche «nome della zona + settore». L'articolo illustra l'ordine con cui migliorare la SEO loc...
Argomenti correlati
Queste pagine collocano l’argomento in un contesto più ampio di servizi e decisioni.
Argomenti tecnici Windows
Portale su sviluppo Windows, analisi dei problemi e valorizzazione delle risorse esistenti.
Analisi dei bug e guasti di lunga durata
Errori intermittenti, diagnosi delle comunicazioni, crash prolungati e test dei percorsi di errore.
Servizi collegati all’argomento
L’articolo è direttamente collegato ai servizi seguenti.
Analisi dei bug e delle cause
Individuare la causa di un comportamento lento o bloccato è un'indagine sui bug guidata dalle prestazioni.
Sviluppo di applicazioni Windows
Progettare un'app misurabile, per esempio con la strumentazione EventSource, rientra nella consulenza per lo sviluppo di app Windows.
Domande frequenti
Domande che ricorrono nelle consulenze sull’argomento dell’articolo.
- Devo usare PerfView o dotnet-trace?
- Iniziate con dotnet-trace: non richiede privilegi di amministratore, può mirare a un solo processo e si usa da una riga di comando. Se servono stack di codice nativo, lo stato dell'intera macchina o il tempo bloccato per singolo context switch, occorre PerfView basato su ETW. È comune raccogliere con dotnet-trace e aprire poi il file .nettrace in PerfView.
- Qual è il rapporto con il profiler di Visual Studio?
- Se il problema si riproduce su una macchina di sviluppo, gli strumenti CPU e memoria di Visual Studio sono più comodi e spesso sufficienti. PerfView e dotnet-trace servono quando bisogna raccogliere su test o produzione senza Visual Studio, separare raccolta e analisi su macchine diverse o vedere eventi ETW, inclusi EventSource personalizzati e kernel. Visual Studio può aprire anche .nettrace.
- È sicuro usarli in produzione?
- Sono pensati anche per raccolte brevi in produzione, ma limitate la durata da decine di secondi a pochi minuti, provate prima in staging e verificate spazio disco, overhead e dimensione file. ThreadTime e il tracing delle allocazioni generano molti eventi. Una traccia può contenere argomenti della riga di comando: trattatela come un artefatto sensibile.
- Dove si collocano WPR e WPA?
- WPR e WPA sono strumenti più orientati al sistema operativo e basati sullo stesso ETW. Per analisi dettagliate di I/O disco, alimentazione e avvio dell'intera macchina sono più forti. Per CPU, GC e tempo bloccato di un'app .NET, PerfView è più leggibile perché ottimizzato per codice gestito.
Profilo dell’autore
Pagina di presentazione dell’autore dell’articolo.
Go Komura
Rappresentante di KomuraSoft LLC
Specializzato nello sviluppo di software Windows, nella consulenza tecnica e nell’analisi dei malfunzionamenti, soprattutto nei progetti con sistemi esistenti e guasti difficili da riprodurre.
Link pubblici