2013-03-03 8 views
23

Sto usando una versione ottimizzata dell'algoritmo di Levenshtein in qualche codice di ricerca che sto costruendo. Ho dei test unitari funzionali per verificare che l'algoritmo restituisca i risultati corretti, ma in questo contesto la prestazione dell'algoritmo è anche estremamente importante.Come posso testare l'ottimizzazione delle prestazioni dell'unità in C#?

Sto cercando di aggiungere un po 'di copertura di test al progetto in modo che se eventuali modifiche future influenzano le ottimizzazioni, verranno visualizzate come test in errore - perché l'algoritmo è deterministico e in esecuzione contro i dati di test noti, questo potrebbe essere dettagliato come contare il numero di istruzioni eseguite per un dato set di input di test. In altre parole, non sto cercando di misurare le prestazioni dell'algoritmo utilizzando i timer: sono interessato a testare effettivamente il comportamento interno dell'algoritmo anziché solo l'output.

Qualche idea su come affrontare questo in C# /. NET 4?

MODIFICA: Il motivo per cui non voglio utilizzare solo il tempo di wall-clock è che varierà con il carico della CPU e altri fattori fuori dal controllo del test. Ciò potrebbe portare a test che falliscono quando il server di compilazione è sotto carico, per esempio. Ci sarà essere il monitoraggio orologio da parete come parte del sistema distribuito.

EDIT 2: Pensa in questo modo ... come applicheresti il ​​rosso-> verde-> refactor quando le prestazioni sono un requisito fondamentale?

+1

Il numero di istruzioni eseguite non equivale necessariamente alla prestazione. Perché l'orologio non è il parametro di interesse qui? –

+0

Concordato - ma se il numero di istruzioni eseguite in risposta a uno specifico insieme di input dovesse cambiare drasticamente a seguito di una modifica al codice, mi piacerebbe saperlo. Questo è il genere di cose che sto cercando. –

+2

Non userei i test unitari per verificarlo. Se gli sviluppatori stanno cambiando il codice funzionante, completo e altamente ottimizzato, allora dovrebbero sapere esattamente cosa stanno facendo. Non è così probabile che questo codice abbia bisogno di essere "ottimizzato" giusto? Allora perché preoccuparsi? Le prestazioni sono una caratteristica non funzionale. –

risposta

30

Ho intenzione di rispondere alla terza parte della tua domanda, poiché ho fatto questo con un certo successo diverse volte.

come si applica rosso-> verde-> refactor quando le prestazioni sono un requisito critico ?

  1. Scrivi pinning test per la cattura di regressioni, per quello che si prevede di cambiare e di altri metodi che possono rallentare a seguito delle modifiche.
  2. Scrive un test delle prestazioni che non riesce.
  3. Miglioramenti delle prestazioni, esecuzione frequente di tutti i test.
  4. Aggiorna i tuoi test di blocco per fissare meglio le prestazioni.

scrittura pinning test

Creare un metodo di supporto come questo per tempo ciò che si desidera appuntare.

private TimeSpan Time(Action toTime) 
{ 
    var timer = Stopwatch.StartNew(); 
    toTime(); 
    timer.Stop(); 
    return timer.Elapsed; 
} 

Quindi scrivere un test che asserisce il tuo metodo non richiede tempo:

[Test] 
public void FooPerformance_Pin() 
{ 
    Assert.That(Time(()=>fooer.Foo()), Is.LessThanOrEqualTo(TimeSpan.FromSeconds(0)); 
} 

Quando non riesce (con il tempo effettivo trascorso nel messaggio di errore), aggiornare il tempo con qualcosa di un po 'più del tempo reale Riesegui e passerà. Ripeti questa operazione per altre funzioni le cui prestazioni potrebbero influire sulle tue modifiche, finendo con qualcosa di simile.

[Test] 
public void FooPerformance_Pin() 
{ 
    Assert.That(Time(()=>fooer.Foo()), Is.LessThanOrEqualTo(TimeSpan.FromSeconds(0.8)); 
} 
[Test] 
public void BarPerformance_Pin() 
{ 
    Assert.That(Time(()=>fooer.Bar()), Is.LessThanOrEqualTo(TimeSpan.FromSeconds(6)); 
} 

scrivere un test in mancanza di prestazioni

mi piace chiamare questo tipo di test un "test di adescamento".È solo il primo passo di un test di blocco.

[Test] 
public void FooPerformance_Bait() 
{ 
    Assert.That(Time(()=>fooer.Foo()), Is.LessThanOrEqualTo(TimeSpan.FromSeconds(0)); 
} 

Ora, lavorare sui miglioramenti delle prestazioni. Esegui tutti i test (pinning e adescamento) dopo ogni tentativo di miglioramento. Se hai successo, vedrai il tempo calare nell'output di errore del test di innesco e nessuno dei tuoi test di pinning fallirà.

Quando si è soddisfatti dei miglioramenti, aggiornare il test di blocco per il codice modificato ed eliminare il test di innesco.

Cosa fai adesso con questi test?

La cosa meno preoccupante da fare è contrassegnare questi test con l'attributo Explicit e tenerli in giro per la prossima volta che si desidera controllare le prestazioni.

Sul lato opposto dello spettro di lavoro, la creazione di un sottosistema ragionevolmente controllato in CI per l'esecuzione di questo tipo di test è un ottimo modo per monitorare le regressioni delle prestazioni. Nella mia esperienza ci sono molte preoccupazioni su di loro che "falliscono a caso a causa del carico della CPU da qualcos'altro" rispetto ai guasti reali. Il successo di questo tipo di sforzo dipende più dalla cultura della squadra che dalla capacità di esercitare il controllo sull'ambiente.

+0

Non sono sicuro di aver capito il tuo commento finale. Sicuramente è ** tutto ** a che fare con un CI ben controllato che garantisce che i test delle prestazioni vengano eseguiti su una macchina specifica, uno alla volta, con il maggior numero possibile di processi in background e così via disabilitati? E forse anche assicurando che ad es. le cache vengono svuotate prima dell'inizio della corsa. Se non esegui la maggior parte/tutto quanto sopra, non vedo come puoi eseguire una regressione delle prestazioni significativa (a meno che non imposti una soglia molto allentata). –

+0

Penso che probabilmente hai ragione, in particolare per quanto riguarda la necessità di un sottosistema di CI ben controllato per monitorare le regressioni delle prestazioni. Penso che un agente di TeamCity dedicato sia probabilmente un buon approccio qui - abbiamo fatto cose simili per cose come test di Selenio a lunga esecuzione prima. Grazie per una risposta così completa. –

+0

@OliCharlesworth, hai provato a eseguire test delle prestazioni come questo in esecuzione in CI? C'era un ** lotto ** di pushback la prima volta che l'ho fatto. Il fatto è che, sebbene ci sia voluto del lavoro per creare, non era molto lavoro per renderli stabili. È stato molto più un lavoro discutere con gli oppositori prima di provarlo. YMMV, la maggior parte di questi test misura qualcosa dell'ordine di secondi, non di millisecondi. I test di perf rapidità saranno più fragili per le condizioni esterne, mi aspetto. – tallseth

Problemi correlati