2009-05-15 14 views
20

Sé que las excepciones tienen una penalización de rendimiento, y que generalmente es más eficiente intentar y evitar excepciones que dejar caer un gran try/catch en todo, pero ¿qué pasa con el bloque Try? ? ¿Cuál es el costo de simplemente declarar un try/catch, incluso si nunca arroja una excepción?Costo de rendimiento de 'probar' en C#

+8

usted es la única persona que puede responder a esta pregunta. Usted es la única persona que sabe qué tipo de hardware y software está utilizando, usted es la única persona que sabe qué métricas de rendimiento son relevantes para su cliente, y así sucesivamente. Escriba algunos puntos de referencia realistas, pruébelos con hardware realista y luego sabrá la respuesta. Cualquier persona que intente responder la pregunta por usted está adivinando o describiendo las características de rendimiento en sus máquinas, para sus clientes, no para usted. De cualquier manera, esos no son los datos que necesita. –

+0

@Eric - Bien dicho. –

Respuesta

26

El costo de rendimiento de try es muy pequeño. El principal costo del manejo de excepciones es obtener el seguimiento de la pila y otros metadatos, y ese es un costo que no se paga hasta que realmente tenga que lanzar una excepción.

Pero esto variará según el idioma y la implementación. ¿Por qué no escribir un bucle simple en C# y cronometrarlo usted mismo?

+1

Sí. Pruébalo es efectivamente gratis porque básicamente se maneja con metadatos que realmente solo se inspeccionan cuando se lanza la excepción. –

5

Un dicho común es que las excepciones son caros cuando son capturados - sin torcer. Esto es porque más de la recopilación de metadatos de excepción (como obtener un seguimiento de pila, etc.) solo sucede realmente en el lado de try-catch (no en el lado throw).

El desenrollado de la pila es realmente bastante rápido: el CLR sube la pila de llamadas y solo presta atención a los bloques finalmente encontrados; en ningún punto en un bloque try-finally puro el intento intenta "completar" una excepción (sus metadatos, etc.).

Por lo que recuerdo, cualquier intento de captura con filtros (como "catch (FooException) {}") es igual de caro, incluso si no hacen nada con la excepción.

Me atrevería a decir que un método (lo llaman CatchesAndRethrows) con el siguiente bloque:

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

podría resultar en un paseo rápido pila en un método - como por ejemplo:

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

Algunos números:

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

Aquí está el punto de referencia que ejecuté (recuerde, modo de lanzamiento - ejecute sin 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

Creo que OP preguntó sobre el impacto en el rendimiento cuando NO se lanzan excepciones. – Shimmy

7

En realidad, hace un par de meses que estaba creando una aplicación Web ASP.NET, y accidentalmente envuelto un bloque try/catch con un bucle muy largo. Aunque el ciclo no generaba todas las excepciones, tardaba demasiado tiempo en terminar. Cuando volví y vi el try/catch envuelto por el loop, lo hice al revés, envolví el loop en el bloque try/catch. El rendimiento mejoró MUCHO. Puede probar esto por su cuenta: haga algo como

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()); 

Y luego saque el bloque try/catch. ¡Verás una gran diferencia!

+11

Hmmm. Acabo de probar esto en .Net 2.0 (usando un 'Cronómetro'). 50000 pruebas de 20000 iteraciones de bucle tardan 4184ms sin 'try-catch', 4363ms con' try-catch'. Esa es una diferencia increíblemente pequeña. Tal diferencia será aún menos visible si cada iteración realmente está haciendo algo más allá de una simple operación de suma. Llegué a resultados similares con y sin depuración. – Brian

4

Para ver lo que realmente cuesta, puede ejecutar el código a continuación. Se necesita una matriz bidimensional simple y genera coordenadas aleatorias que están fuera de rango. Si su excepción solo ocurre una vez, por supuesto no la notará. Mi ejemplo se hace para enfatizar lo que significará al hacer esto varias miles de veces, y qué capturar una excepción vs implementar una prueba simple te salvará.

 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(); 

Ejemplo de salida de este código:

---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--- 
Cuestiones relacionadas