2011-01-24 6 views
8

Abbiamo alcuni test unitari che falliscono quando si esegue in modalità di rilascio in modalità debug. Se collego un debugger in modalità di rilascio, i test passano. C'è troppo codice per pubblicare qui, quindi sto solo cercando le migliori pratiche per il debug dei problemi relativi alla modalità di rilascio. Ho controllato:Il codice si comporta in modo diverso in modalità Release vs Debug

SOLUZIONE: In questo caso è perché ero paragonando variabili floating point per la parità. Non ho potuto cambiare i carri a decimale senza un importante refactoring così ho aggiunto un metodo di estensione:

public static class FloatExtension 
{ 
    public static bool AlmostEquals(this float f1, float f2, float precision) 
    { 
     return (Math.Abs(f1 - f2) <= precision); 
    } 

    public static bool AlmostEquals(this float f1, float f2) 
    { 
     return AlmostEquals(f1, f2, .00001f); 
    } 

    public static bool AlmostEquals(this float? f1, float? f2) 
    { 
     if (f1.HasValue && f2.HasValue) 
     { 
      return AlmostEquals(f1.Value, f2.Value); 
     } 
     else if (f1 == null && f2 == null) 
     { 
      return true; 
     } 
     return false; 
    } 
} 
+1

Un paio di domande. 1. Che tipo di errori riesci a dare alla domanda un po 'di "sapore"? 2. Hai controllato i metodi condizionali? –

+0

Il problema principale è che il metodo Equals restituisce false. Tuttavia, se prendo ogni affermazione individualmente, tutti ritornano veri. Se tento di collegare un debugger, il problema scompare. –

+0

È in virgola mobile (tipo di dati doppio ecc.)? – stefan

risposta

3

Dal momento che sembra essere in virgola mobile, ci sono così tante cose che possono andare storte. Vedere: C# - Inconsistent math operation result on 32-bit and 64-bit e Double precision problems on .NET

Ci sono tante cose che possono essere cestinato con virgola mobile. E confrontare i galleggianti per l'uguaglianza è un no-no generale. Puoi controllare la differenza più piccola di un epsilon ragionevole.

+0

Vedere anche http://stackoverflow.com/questions/2342396/why-does-this-floating-point-calculation-give-different-results-on-different-mach/2343351#2343351 –

8

Una cosa che potrebbe causare il comportamento che state vedendo è un errore che provoca un race condition. Il collegamento di un debugger può modificare la tempistica del codice in modo tale che la condizione della competizione non venga più attivata.

Per risolvere il problema, utilizzare la sincronizzazione in modo appropriato ogni volta che si hanno più thread che accedono ai dati.


mi sto paragonando alcuni valori float nel metodo IsEqual.

Sembra una pessima idea. Non dovresti confrontare i float per l'uguaglianza perché i calcoli in virgola mobile non sono precisi al 100% e puoi ottenere errori di rappresentazione e arrotondamento. Confronta per vedere se sono sufficientemente vicini. Per i calcoli che coinvolgono denaro, probabilmente si preferisce utilizzare il tipo decimal.

+0

Grazie per la risposta, tuttavia questa particolare parte dell'applicazione è a thread singolo. –

+0

Devono essere i numeri in virgola mobile. Farò alcune modifiche e convalidare. –

0

Come suggerisce Mark, questo è solitamente il risultato di un problema correlato alla temporizzazione, spesso una condizione di competizione o un problema di sincronizzazione.

Un modo comune per gestire questo tipo di problema consiste nell'utilizzare le istruzioni "stampa" nelle aree interessate per mostrare cosa sta succedendo. Se le istruzioni di stampa (Console.WriteLine, Response.Write, registrazione o qualsiasi altra cosa) risolvono il problema, memorizzano i valori nelle variabili globali e stampano i globali una volta che il problema si è manifestato.

L'ultima volta che mi è successo è stato nel codice che stava leggendo da una porta seriale. L'attività di debug ha causato un cambiamento del timing sufficiente a influenzare il modo in cui venivano bufferati i byte dalla porta seriale, che ha cambiato il modo in cui il buffer è stato analizzato. Dal momento che le istruzioni di stampa hanno cambiato i tempi, ho dovuto memorizzare i dati fino all'output successivo.

2

Domande da porsi -

  1. è il mio codice threaded? Le differenze di tempo influiranno sull'output
  2. Qualcuno sta chiamando Debug.Assert() con un'espressione che effetti collaterali?
  3. Quali oggetti implementano IDisposable() e alcuni lo fanno in modo tale da modificare lo stato?
  4. Sei P/Invocando nel codice non gestito?

Il numero 3 è molto probabilmente un cattivo ragazzo in questo caso.La raccolta dei dati obsoleti può essere molto diversa nel debug e nel rilascio e si potrebbe scoprire che quando un oggetto viene sottoposto a garbage collection sta influenzando il risultato di un successivo test unitario.

E FYI, se si utilizza NUnit e TestDriven.NET - i due test eseguiti in diversi ordini.

+0

+1 per menzionare le differenze nella garbage collection. Nella modalità di debug, gli oggetti tendono a rimanere in vita più a lungo (ad es. Fino alla fine di un metodo) per supportare il debug, mentre in modalità di rilascio, gli oggetti vengono spesso raccolti prima. – stakx

2

Questo è spesso il caso in cui la compilazione di debug non è ottimizzata per impostazione predefinita e, anche se la si attiva, il comportamento durante il debug è molto diverso. Puoi disabilitare "Ottimizza codice" dalle impostazioni del progetto per tutti gli assemblaggi nella scheda Proprietà-> Costruisci.

Ci sono certamente altri cambiamenti che possono causare differenze, come la tua menzione I metodi condizionali sono uno. Questi ho trovato raramente la causa dei problemi, per me è quasi sempre l'ottimizzatore.

I classici getcha's dell'ottimizzatore includono metodi che vengono "in linea" in modo che non vengano visualizzati in uno stack di chiamate. Ciò causa problemi quando si utilizzano le classi System.Diagnostics.StackFrame per determinare il punto di esecuzione corrente. Allo stesso modo ciò influenzerà il risultato di MethodBase.GetCurrentMethod o altre funzioni/comportamenti che si basano sul metodo di esecuzione.

Poi ci sono ovviamente molte cose che ho visto fare all'ottimizzatore che semplicemente non riesco a spiegare affatto. Uno di questi esempi è stato documentato e discusso in un post "HashDerivedBytes - replacing Rfc2898DeriveBytes, but why?" ma non ho mai risolto il mistero. So solo che l'ottimizzatore appena rotto ha rotto Rfc2898DeriveBytes quando viene utilizzato per generare una serie di byte derivati. Stranamente, questo si è rotto solo quando i byte generati non erano equamente divisibili per la dimensione dell'algoritmo di hash utilizzato (20) e hanno prodotto risultati non corretti solo dopo i primi 20 byte.

Il fatto è che le ottimizzazioni che influiscono negativamente sul codice non sono una novità per i compilatori. La maggior parte degli sviluppatori C++ della vecchia scuola te lo diranno subito e poi, come ho fatto io, in una lunga e lunga storia su come hanno lavorato intorno a loro;)

+1

Quindi, se una build con ottimizzazione ON viene avviato con un debugger, verrà soppressa qualche ottimizzazione, probabilmente causando il comportamento descritto nella domanda originale? Un esempio di questo sarebbe molto utile. – Govert

0

Solo per aggiungere i miei due centesimi a questo, io recentemente ho scoperto che avevo un confronto di date in una procedura sql che il test chiamava. Le date sono state tutte generate automaticamente prima della procedura di test e i valori sono stati inseriti nel DB, pertanto occasionalmente erano esattamente uguali (quando si utilizza RunTests) causando il ritorno di un null su un join di tabella. Non quello che mi aspettavo. Ovviamente, in modalità di debug, poiché sto procedendo lentamente attraverso di esso, ci sarà una differenza nei tempi generati automaticamente, il che significa che non mi sono mai imbattuto nell'errore. Ho risolto questo con l'inserimento di

Threading.Thread.Sleep (520)

ovunque ci sarebbe sicuramente un ritardo tra le azioni. Problema risolto