typeparam example cref c# exception

example - Costo de rendimiento de ''probar'' en C#



summary example c# (5)

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 bloqueo try? ¿Cuál es el costo de simplemente declarar un try / catch, incluso si nunca arroja una excepción?


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?


En realidad, hace un par de meses estaba creando una aplicación web ASP.NET, y accidentalmente envolví un bloque try / catch con un ciclo 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. Puedes probar esto por tu cuenta: haz algo así 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 saca el bloque try / catch. ¡Verás una gran diferencia!



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


Un dicho común es que las excepciones son costosas cuando son atrapadas, no arrojadas. Esto se debe a que la mayor parte de la recopilación de metadatos de excepción (como obtener un seguimiento de pila, etc.) solo ocurre realmente en el lado de try-catch (no en el lado throw).

Desenrollar la pila es en realidad bastante rápido: la CLR sube la pila de llamadas y solo presta atención a los bloques finales que encuentra; 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 tan caro, incluso si no hacen nada con la excepción.

Me atrevería a decir que un método (llámalo CatchesAndRethrows) con el siguiente bloque:

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

Podría dar como resultado un paseo de pila más rápido 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 - ejecutar sin depurar):

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