2009-05-15 14 views
20

So che le eccezioni hanno una penalizzazione delle prestazioni e che è generalmente più efficiente cercare di evitare le eccezioni piuttosto che abbandonare un grosso tentativo/aggirare tutto - ma per quanto riguarda il blocco di prova stesso ? Qual è il costo della semplice dichiarazione di un tentativo/cattura, anche se non genera mai un'eccezione?Costo delle prestazioni di "prova" in C#

+8

Tu sei l'unica persona che può rispondere a questa domanda. Sei l'unica persona che sa che tipo di hardware e software stai utilizzando, sei l'unica persona che sa quali metriche di rendimento sono rilevanti per il tuo cliente e così via. Scrivi alcuni benchmark realistici, provali su hardware realistico e poi saprai la risposta. Chiunque cerchi di rispondere alla domanda per te sta indovinando o descrivendo le caratteristiche delle prestazioni sulle loro macchine, per i loro clienti, non per i tuoi. Ad ogni modo, non sono i dati che ti servono. –

+0

@Eric - Ben detto. –

risposta

26

Il costo delle prestazioni di prova è molto ridotto. Il costo maggiore della gestione delle eccezioni è ottenere la traccia dello stack e altri metadati, e questo è un costo che non viene pagato fino a quando non si deve effettivamente lanciare un'eccezione.

Ma questo può variare in base alla lingua e all'implementazione. Perché non scrivere un semplice ciclo in C# e crearlo da solo?

+1

Sì. Try è effettivamente gratuito perché è fondamentalmente gestito da metadati che vengono realmente controllati solo quando l'eccezione viene effettivamente lanciata. –

5

Un detto comune è che le eccezioni sono costosi quando sono catturati - non torta. Questo perché più della raccolta dei metadati delle eccezioni (come ottenere una traccia stack ecc.) Avviene solo sul lato try-catch (non sul lato del lancio).

Disattivare lo stack è in realtà piuttosto veloce: il CLR risalta lo stack di chiamate e si limita a prestare attenzione ai blocchi che trova; in nessun punto di un blocco try-finally puro il runtime tenta di "completare" un'eccezione (sono i metadati ecc.).

Da quello che ricordo, qualsiasi try-catch con filtri (come "catch (FooException) {}") è altrettanto costoso - anche se non fanno nulla con l'eccezione.

mi permetto di dire che un metodo (lo chiamano CatchesAndRethrows) con il seguente blocco:

try 
{ 
    ThrowsAnException(); 
} 
catch 
{ 
    throw; 
} 

potrebbe comportare una passeggiata pila più veloce in un metodo - come ad esempio:

try 
{ 
    CatchesAndRethrows(); 
} 
catch (Exception ex) // The runtime has already done most of the work. 
{ 
    // Some fancy logic 
} 

alcuni numeri:

With: 0.13905ms 
Without: 0.096ms 
Percent difference: 144% 

Ecco il punto di riferimento mi sono imbattuto (ricordate, modalità di rilascio - correre senza deb ug):

static void Main(string[] args) 
    { 
     Stopwatch withCatch = new Stopwatch(); 
     Stopwatch withoutCatch = new Stopwatch(); 

     int iterations = 20000; 

     for (int i = 0; i < iterations; i++) 
     { 
      if (i % 100 == 0) 
      { 
       Console.Write("{0}%", 100 * i/iterations); 
       Console.CursorLeft = 0; 
       Console.CursorTop = 0; 
      } 

      CatchIt(withCatch, withoutCatch); 
     } 

     Console.WriteLine("With: {0}ms", ((float)(withCatch.ElapsedMilliseconds))/iterations); 
     Console.WriteLine("Without: {0}ms", ((float)(withoutCatch.ElapsedMilliseconds))/iterations); 
     Console.WriteLine("Percent difference: {0}%", 100 * withCatch.ElapsedMilliseconds/withoutCatch.ElapsedMilliseconds); 
     Console.ReadKey(true); 
    } 

    static void CatchIt(Stopwatch withCatch, Stopwatch withoutCatch) 
    { 
     withCatch.Start(); 

     try 
     { 
      FinallyIt(withoutCatch); 
     } 
     catch 
     { 
     } 

     withCatch.Stop(); 
    } 

    static void FinallyIt(Stopwatch withoutCatch) 
    { 
     try 
     { 
      withoutCatch.Start(); 
      ThrowIt(withoutCatch); 
     } 
     finally 
     { 
      withoutCatch.Stop(); 
     } 
    } 

    private static void ThrowIt(Stopwatch withoutCatch) 
    { 
     throw new NotImplementedException(); 
    } 
+1

Penso che l'OP abbia chiesto l'impatto sulle prestazioni quando NON sono state lanciate eccezioni. – Shimmy

7

In realtà, un paio di mesi fa mi è stato la creazione di una web app ASP.NET, e per sbaglio ho avvolto un blocco try/catch con un lunghissimo ciclo. Anche se il ciclo non generava eccezioni, ci voleva troppo tempo per finire. Quando sono tornato indietro e ho visto il try/catch avvolto dal loop, l'ho fatto al contrario, ho avvolto il loop nel blocco try/catch. Le prestazioni sono migliorate MOLTO. Puoi provarlo da solo: fai qualcosa come

int total; 

DateTime startTime = DateTime.Now; 

for(int i = 0; i < 20000; i++) 
{ 
try 
{ 
total += i; 
} 
catch 
{ 
// nothing to catch; 
} 
} 

Console.Write((DateTime.Now - startTime).ToString()); 

E poi prendi il blocco try/catch. Vedrai una grande differenza!

+11

Hmmm. Ho appena provato questo. Net 2.0 (utilizzando un 'Cronometro'). 50000 tentativi di 20000 iterazioni di loop impiegano 4184 ms senza 'try-catch', 4363ms con' try-catch'. Questa è una differenza incredibilmente piccola. Tale differenza sarà ancora meno visibile se ogni iterazione sta effettivamente facendo qualcosa oltre una semplice operazione di addizione. Colpisco risultati simili con e senza debugging. – Brian

4

Per vedere quanto costa, è possibile eseguire il codice di seguito. Prende un semplice array bidimensionale e genera coordinate casuali che non rientrano nell'intervallo. Se la tua eccezione si verifica solo una volta, ovviamente non te ne accorgi. Il mio esempio è fatto per enfatizzare cosa significherà farlo parecchie migliaia di volte, e ciò che cattura un'eccezione rispetto all'implementazione di un semplice test ti salverà.

 const int size = 1000; 
     const int maxSteps = 100000; 

     var randomSeed = (int)(DateTime.UtcNow - new DateTime(1970,1,1,0,0,0).ToLocalTime()).TotalMilliseconds; 
     var random = new Random(randomSeed); 
     var numOutOfRange = 0; 
     var grid = new int[size,size]; 
     var stopwatch = new Stopwatch(); 
     Console.WriteLine("---Start test with exception---"); 
     stopwatch.Reset(); 
     stopwatch.Start(); 
     for (int i = 0; i < maxSteps; i++) 
     { 
      int coord = random.Next(0, size * 2); 
      try 
      { 
       grid[coord, coord] = 1; 
      } 
      catch (IndexOutOfRangeException) 
      { 
       numOutOfRange++; 
      } 
     } 
     stopwatch.Stop(); 
     Console.WriteLine("Time used: " + stopwatch.ElapsedMilliseconds + "ms, Number out of range: " + numOutOfRange); 
     Console.WriteLine("---End test with exception---"); 

     random = new Random(randomSeed); 

     stopwatch.Reset(); 
     Console.WriteLine("---Start test without exception---"); 
     numOutOfRange = 0; 
     stopwatch.Start(); 
     for (int i = 0; i < maxSteps; i++) 
     { 
      int coord = random.Next(0, size * 2); 
      if (coord >= grid.GetLength(0) || coord >= grid.GetLength(1)) 
      { 
       numOutOfRange++; 
       continue; 
      } 
      grid[coord, coord] = 1; 
     } 
     stopwatch.Stop(); 
     Console.WriteLine("Time used: " + stopwatch.ElapsedMilliseconds + "ms, Number out of range: " + numOutOfRange); 
     Console.WriteLine("---End test without exception---"); 
     Console.ReadLine(); 

uscita Esempio di questo codice:

---Start test with exception--- 
Time used: 3228ms, Number out of range: 49795 
---End test with exception--- 
---Start test without exception--- 
Time used: 3ms, Number out of range: 49795 
---End test without exception--- 
Problemi correlati