2012-03-22 13 views
11

Sembra che non ci siano profilatori di prestazioni .NET gratuiti * che possono eseguire il profilo linea per linea. Pertanto, sto cercando di utilizzare il cronometro per la creazione di profili.Profilatura di applicazioni .NET con Cronometro

* gratis come in libertà, cioè la licenza include applicazioni commerciali.

EDIT: In risposta a quelli che mi hanno detto "compra un profiler", mi piacerebbe, ma se potessi spendere tanti soldi lo spenderei su qualcos'altro. Ho cercato di convincere il mio capo che un profiler è valsa la pena, ma non ho avuto molta fortuna. Questa domanda è per lo più basata sulla curiosità. Non considererei mai il cronometro come sostituto di un vero profiler.

Ho una piccola app di test (scritta in C#) che misura le differenze di prestazioni quando si utilizza un cronometro su base per riga. Il codice di prova è questa:

int n = 100; 
BigInteger f = 1; 
for (int i = n; i > 1; i--) 
{ 
    f *= i; 
} 

Ecco il codice completo: http://pastebin.com/AvbQmT32

Ho un cronometro per ogni riga di codice. Questo è il mio 'profiler'. Ho anche un cronometro per l'intero programma. Questo è il mio 'profiler profiler'.

Ho il programma configurato come modalità di rilascio, qualsiasi CPU (su una macchina x64) e ottimizzazioni disabilitate.

Quando eseguo il programma con il profiler disabili, ottengo qualcosa di simile:

   Line    | Ticks 
------------------------------|---------- 
           | 
Total time:     |  359 

Quando eseguo con il profiler abilitato, ottengo qualcosa di simile:

   Line    | Ticks 
------------------------------|---------- 
           | 
int n = 100;     |   3 
BigInteger f = 1;    |  12 
for (int i = n; i > 1; i--) |  325 
{        | 
    f *= i;     |  539 
}        | 
           | 
Total time:     |  1710 
Stopwatch overhead:   |  831 

Idealmente , il tempo speso per il codice dovrebbe essere uguale in entrambi i casi, ma sembra che i cronometri abbiano un sovraccarico che appare nel loro tempo trascorso.

Ora, spesso non ha senso dover profilare ogni riga di un programma, poiché di solito funziona meglio con un approccio divide et impera. In genere puoi iniziare a profilare blocchi di codice e restringere i problemi relativi alle prestazioni.

Inoltre, nella maggior parte delle applicazioni, la riga media del codice sarà molto più lenta di quelle del programma di test. Ciò significa che ci sarà meno sovraccarico del cronometro.

Tuttavia, c'è ancora un sovraccarico quando si usano i cronometri, specialmente se si usa molto.

Così fino alla domanda:

Qual è il modo più efficace per utilizzare cronometri per il profiling? Come posso minimizzare il sovraccarico? Vale la pena persino racchiudere un cronometro su una singola frase?

Apprezzo il vostro feedback.

+6

Personalmente, i profiler ho acquistato (ANTS Profiler) erano vale ogni centesimo. Non penserei mai più alla profilazione "manuale". –

+1

Siamo spiacenti di chiedere, ma perché non verificare l'utilizzo di alcune delle classi System.Diagnostics? Credo che contenga classi per verificare CPU e utilizzo di Ram, ad esempio. – Eon

+1

La versione gratuita di EQATEC profiler permette il suo utilizzo in un progetto commerciale: http://www.eqatec.com/Profiler/LicenseTerms.aspx – ken2k

risposta

2

Per prima cosa, i risultati non sono affatto sorprendenti. Se hai usato un profiler commerciale, vedresti qualcosa di simile: il tuo programma impiegherà molto più tempo per essere eseguito quando viene profilato rispetto a quando non lo è. Quanto più granulare si imposta al profiler, tanto più tempo ci si può aspettare che prenda. Considerando che affermazioni come "i> 1" e "i--" saranno probabilmente eseguite come istruzioni per singolo processore, diventa ovvio perché la profilazione del tempo di esecuzione di una particolare linea può richiedere molto più tempo rispetto all'esecuzione della linea stessa.

Il fatto che l'analisi è in aumento il tempo complessivo di funzionamento del programma non dovrebbe essere una preoccupazione; come molti altri hanno menzionato, ciò che conta non è il tempo assoluto di esecuzione del programma, ma confrontare il tempo di esecuzione delle singole parti l'una contro l'altra per trovare il collo di bottiglia. Ma c'è un'altra preoccupazione. Il cronometro utilizzerà il timer ad alta frequenza dal SO sottostante se disponibile; ma anche quello potrebbe non essere abbastanza alto. Il timer ad alta frequenza sul mio Windows 7 64-bit i5-2400 (Quad core 3.10 GHz) spicca 3.020.556 volte al secondo. Sembra molto; ma a quel ritmo, il processore poteva eseguire migliaia di istruzioni tra le zecche. Ciò significa che se si sta tentando di misurare il tempo necessario per eseguire una singola istruzione, si sta andando verso l'alto o verso l'alto.

Si sarebbe meglio profiling a livello di metodo. Anche in questo caso ti imbatterai in problemi di frequenza, in particolare se hai metodi piccoli e ben fatti. Ma i risultati saranno più affidabili che a livello di linea; e una volta identificato il metodo che causa il collo di bottiglia, è possibile esaminarlo direttamente per determinare come ottimizzarlo per ottenere prestazioni migliori.

Tutto questo lascia da parte i molti avvertimenti che vanno con profiling delle prestazioni in generale, che sarebbe al di fuori della portata di questo post. Assicurati di fare ulteriori ricerche sull'argomento per capire come interpretare i risultati che ottieni. Come esempio rapido, il tuo profilo può rivelare che la maggior parte del tempo nel tuo programma viene spesa in un particolare metodo; ma questo significa che il metodo stesso è il problema o che altri metodi lo chiamano spesso? Rispondere a questo tipo di domande è dove si trova la vera difficoltà nella profilazione.

1

Hai confrontato il tempo totale senza il tuo profiler con il tuo profiler in modalità di debug? La mia prima ipotesi è che il tempo extra è visualizzato perché le istruzioni extra per il cronometro impediscono l'ottimizzazione di alcuni registri o cicli. Quindi il tempo extra potrebbe derivare da un'ottimizzazione mancante e non da una perdita di tempo interna al cronometro.

Tuttavia, detto questo, anche se il cronometro ha una alta risoluzione, penserei si nota qualche errore di temporizzazione misurare le cose piccolo come una riga di codice. Ad esempio 'int n = 100' è una linea in assembly, quindi in termini tecnici penserei che sarebbe solo 1 tick. Quanta variazione noti nei tuoi numeri dall'inizio alla fine? Se ottieni una variazione che è una percentuale significativa del tuo valore medio, allora quel valore in realtà non ti sta dando molte informazioni.

+0

Le ottimizzazioni sono disabilitate, quindi non sarà un problema. A volte vedo una variazione di circa 1/3 del valore medio, in entrambi i casi. –

+0

Con le ottimizzazioni disabilitate, il passaggio alla modalità di debug probabilmente non rivela il problema, ma è comunque possibile che il rallentamento sia causato dall'overhead extra delle chiamate del cronometro. Le chiamate del cronometro utilizzano internamente diverse variabili che potrebbero forzare il codice principale a fare cose come i registri di ricarica dopo le chiamate del cronometro, ad esempio. – Sogger

+0

Oh, e in termini di variazione 1/3, dipende da cosa stai cercando di determinare il numero, ma una variazione così grande significa che il numero sarà buono solo per confronti di ordine di grandezza (in termini di cosa altre risposte qui stanno dicendo, valide solo per confronti relativi). Pensa a una persona di 6 piedi di altezza misurata con un margine di errore di 1/3, "Oh, sei tra i 4 e gli 8 piedi di altezza." Una specie di grosso problema. – Sogger

4

Considerando che spesso intuitivamente programma Single responsibility principle, non solo per quanto riguarda dei tipi, ma le funzioni anche, direi che non c'è alcuna indicationi profilazione applicazione riga per riga. Sarai più interessato a delineare una "singola responsabilità", quindi ogni singola riga.

Ci sono, naturalmente, i casi in cui si necessità di avere informaiton troppo. Ma non usarlo in tutte le applicazioni, ma in una singola parte di una funzione o una singola funzione. In questo caso StopWatch è la scelta migliore. Si consideri che StopWatch è .NET Class così ha è ancora in testa minimo. Non dovresti cercare assoluto, ma su valori relativi.

Spero che questo aiuti.

2

Non si dovrebbe essere alla ricerca di numeri esatti, si dovrebbe essere alla ricerca di differenze relative. In modo da poter identificare le aree problematiche. Stai cercando dei luoghi da ottimizzare per risolvere il tuo problema di prestazioni.

Se stai sviluppando codice al livello in cui la prestazione è effettivamente un problema e devi profilarlo per trovare il codice incriminato, quindi acquistare uno strumento per eseguire questa attività per te sarà più che ripagarsi nel tempo salvato. Il profiler fornito con Visual Studio Premium è quello che consiglierei.

0

Forse si dovrebbe pensare in termini di tempi di metodo di profiling delle chiamate, e non singole linee. Dopo tutto, una chiamata al metodo è solitamente una singola riga nel codice chiamante. Anche un approccio interessante è quello di utilizzare una libreria di registrazione per registrare il metodo di ingresso e le uscite, per esempio, come questo (utilizzando log4net come il logger di scelta):

public string MyMethod(string arg) 
{ 
    string result = null; 

    if (logger.IsDebugEnabled) logger.Debug("->MyMethod(" + arg + ")"); 

    // do stuff 
    result = "Hello world!"; 

    if (logger.IsDebugEnabled) logger.Debug("<-MyMethod(" + arg + ") = " + result); 

    return result; 
} 

Questo creerà un file di log che è veramente utile per vedere cosa sta facendo la tua applicazione, e ha il bonus di timestamping di ogni riga scritta nel log, così puoi vedere se un particolare metodo richiede molto tempo. Con log4net puoi facilmente modificare la configurazione per disattivare la registrazione, quindi quando non ne hai bisogno non paghi alcuna penalità di prestazioni.

2

Hai provato guardando CInject: http://codeinject.codeplex.com/documentation? Fondamentalmente inietta il codice in una DLL. Fuori dagli schemi sembra avere un monitoraggio delle prestazioni, ma consente anche di creare il proprio codice da iniettare. Sembra perfetto per quello che stai cercando di fare.

Problemi correlati