usuario try todas tipos programacion por net más manejo las excepciones específicos errores ejemplos definidas comunes como catch capturar asp c# performance try-catch

try - todas las excepciones en c#



¿Los bloques try/catch dañan el rendimiento cuando no se lanzan excepciones? (10)

Después de ver todas las estadísticas con try / catch y sin try / catch, la curiosidad me obligó a mirar hacia atrás para ver qué se genera para ambos casos. Aquí está el código:

DO#:

private static void TestWithoutTryCatch(){ Console.WriteLine("SIN(1) = {0} - No Try/Catch", Math.Sin(1)); }

MSIL:

.method private hidebysig static void TestWithoutTryCatch() cil managed { // Code size 32 (0x20) .maxstack 8 IL_0000: nop IL_0001: ldstr "SIN(1) = {0} - No Try/Catch" IL_0006: ldc.r8 1. IL_000f: call float64 [mscorlib]System.Math::Sin(float64) IL_0014: box [mscorlib]System.Double IL_0019: call void [mscorlib]System.Console::WriteLine(string, object) IL_001e: nop IL_001f: ret } // end of method Program::TestWithoutTryCatch

DO#:

private static void TestWithTryCatch(){ try{ Console.WriteLine("SIN(1) = {0}", Math.Sin(1)); } catch (Exception ex){ Console.WriteLine(ex); } }

MSIL:

.method private hidebysig static void TestWithTryCatch() cil managed { // Code size 49 (0x31) .maxstack 2 .locals init ([0] class [mscorlib]System.Exception ex) IL_0000: nop .try { IL_0001: nop IL_0002: ldstr "SIN(1) = {0}" IL_0007: ldc.r8 1. IL_0010: call float64 [mscorlib]System.Math::Sin(float64) IL_0015: box [mscorlib]System.Double IL_001a: call void [mscorlib]System.Console::WriteLine(string, object) IL_001f: nop IL_0020: nop IL_0021: leave.s IL_002f //JUMP IF NO EXCEPTION } // end .try catch [mscorlib]System.Exception { IL_0023: stloc.0 IL_0024: nop IL_0025: ldloc.0 IL_0026: call void [mscorlib]System.Console::WriteLine(object) IL_002b: nop IL_002c: nop IL_002d: leave.s IL_002f } // end handler IL_002f: nop IL_0030: ret } // end of method Program::TestWithTryCatch

No soy un experto en IL, pero podemos ver que se crea un objeto de excepción local en la cuarta línea .locals init ([0] class [mscorlib]System.Exception ex) después de eso las cosas son muy .locals init ([0] class [mscorlib]System.Exception ex) a las del método sin try / coger hasta la línea diecisiete IL_0021: leave.s IL_002f . Si ocurre una excepción, el control salta a la línea IL_0025: ldloc.0 contrario, IL_0025: ldloc.0 a la etiqueta IL_002d: leave.s IL_002f y la función regresa.

Puedo asumir con seguridad que, si no se producen excepciones, es la sobrecarga de crear variables locales para contener solo objetos de excepción y una instrucción de salto.

Durante una revisión del código con un empleado de Microsoft encontramos una gran sección de código dentro de un bloque try{} . Ella y un representante de TI sugirieron que esto puede tener efectos en el rendimiento del código. De hecho, sugirieron que la mayor parte del código debería estar fuera de los bloques try / catch, y que solo se deberían revisar las secciones importantes. El empleado de Microsoft agregó y dijo que un próximo informe técnico advierte contra los bloques de prueba / captura incorrectos.

Miré a mi alrededor y descubrí que puede afectar a las optimizaciones , pero parece que solo se aplica cuando una variable se comparte entre ámbitos.

No estoy preguntando acerca de la capacidad de mantenimiento del código, ni siquiera de manejar las excepciones correctas (el código en cuestión necesita una re-factorización, sin duda). Tampoco me refiero a usar excepciones para el control de flujo, esto es claramente incorrecto en la mayoría de los casos. Esos son temas importantes (algunos son más importantes), pero no son el enfoque aquí.

¿Cómo afectan los bloques try / catch al rendimiento cuando no se lanzan las excepciones?


El try / catch tiene impacto en el rendimiento.

Pero no es un gran impacto. La complejidad de prueba / captura es generalmente O (1), al igual que una tarea simple, excepto cuando se colocan en un bucle. Así que tienes que usarlos sabiamente.

Here hay una referencia sobre el rendimiento de prueba / captura (aunque no explica su complejidad, pero está implícito). Echa un vistazo a la sección Lanzar menos excepciones .


En teoría, un bloque try / catch no tendrá ningún efecto en el comportamiento del código a menos que realmente ocurra una excepción. Sin embargo, existen algunas circunstancias excepcionales en las que la existencia de un bloque try / catch puede tener un efecto importante, y algunas poco frecuentes pero difíciles de entender en las que el efecto puede ser notable. La razón de esto es que el código dado como:

Action q; double thing1() { double total; for (int i=0; i<1000000; i++) total+=1.0/i; return total;} double thing2() { q=null; return 1.0;} ... x=thing1(); // statement1 x=thing2(x); // statement2 doSomething(x); // statement3

el compilador puede ser capaz de optimizar la sentencia1 basándose en el hecho de que se garantiza que la sentencia2 se ejecute antes que la sentencia3. Si el compilador puede reconocer que thing1 no tiene efectos secundarios y thing2 no usa realmente x, puede omitir thing1 de forma segura. Si [como en este caso] thing1 era caro, podría ser una optimización importante, aunque los casos en que thing1 es caro son también los que el compilador tendría menos probabilidades de optimizar. Supongamos que el código fue cambiado:

x=thing1(); // statement1 try { x=thing2(x); } // statement2 catch { q(); } doSomething(x); // statement3

Ahora existe una secuencia de eventos donde la sentencia3 podría ejecutarse sin haberse ejecutado la sentencia2. Incluso si nada en el código para thing2 pudiera thing2 una excepción, sería posible que otro subproceso pudiera usar un Interlocked.CompareExchange para notar que q estaba borrado y configurarlo en Thread.ResetAbort , y luego realizar un Thread.Abort() antes sentencia2 escribió su valor a x . Luego la catch ejecutaría Thread.ResetAbort() [a través del delegado q ], permitiendo que la ejecución continúe con la sentencia3. Por supuesto, tal secuencia de eventos sería excepcionalmente improbable, pero se requiere que un compilador genere un código que funcione de acuerdo con la especificación incluso cuando ocurren tales eventos improbables.

En general, es mucho más probable que el compilador note oportunidades para omitir fragmentos de código simples que complejos, por lo que sería raro que un intento / captura afecte mucho el rendimiento si nunca se lanzan excepciones. Sin embargo, hay algunas situaciones en las que la existencia de un bloque try / catch puede evitar optimizaciones que, pero para try / catch, habrían permitido que el código se ejecute más rápido.


La estructura es diferente en el ejemplo de Ben M. Se extenderá por encima del bucle interno for que no sea una buena comparación entre los dos casos.

Lo siguiente es más exacto para la comparación donde el código completo para verificar (incluida la declaración de variables) está dentro del bloque Try / Catch:

for (int j = 0; j < 10; j++) { Stopwatch w = new Stopwatch(); w.Start(); try { double d1 = 0; for (int i = 0; i < 10000000; i++) { d1 = Math.Sin(d1); d1 = Math.Sin(d1); } } catch (Exception ex) { Console.WriteLine(ex.ToString()); } finally { //d1 = Math.Sin(d1); } w.Stop(); Console.Write(" try/catch/finally: "); Console.WriteLine(w.ElapsedMilliseconds); w.Reset(); w.Start(); double d2 = 0; for (int i = 0; i < 10000000; i++) { d2 = Math.Sin(d2); d2 = Math.Sin(d2); } w.Stop(); Console.Write("No try/catch/finally: "); Console.WriteLine(w.ElapsedMilliseconds); Console.WriteLine(); }

Cuando ejecuté el código de prueba original de Ben M , noté una diferencia en la configuración de Debug y Releas.

Esta versión, noté una diferencia en la versión de depuración (en realidad más que la otra versión), pero no hubo diferencia en la versión de lanzamiento.

Conclusión :
Basándonos en estas pruebas, creo que podemos decir que Try / Catch tiene un pequeño impacto en el rendimiento.

EDITAR:
Intenté aumentar el valor del bucle de 10000000 a 1000000000, y corrí nuevamente en Release para obtener algunas diferencias en el lanzamiento, y el resultado fue este:

try/catch/finally: 509 No try/catch/finally: 486 try/catch/finally: 479 No try/catch/finally: 511 try/catch/finally: 475 No try/catch/finally: 477 try/catch/finally: 477 No try/catch/finally: 475 try/catch/finally: 475 No try/catch/finally: 476 try/catch/finally: 477 No try/catch/finally: 474 try/catch/finally: 475 No try/catch/finally: 475 try/catch/finally: 476 No try/catch/finally: 476 try/catch/finally: 475 No try/catch/finally: 476 try/catch/finally: 475 No try/catch/finally: 474

Usted ve que el resultado es inconsecuente. En algunos casos, la versión que usa Try / Catch es en realidad más rápida.


No. Si las optimizaciones triviales que un bloque try / finally impide en realidad tienen un impacto medible en su programa, es probable que no deba usar .NET en primer lugar.


Revisalo.

static public void Main(string[] args) { Stopwatch w = new Stopwatch(); double d = 0; w.Start(); for (int i = 0; i < 10000000; i++) { try { d = Math.Sin(1); } catch (Exception ex) { Console.WriteLine(ex.ToString()); } } w.Stop(); Console.WriteLine(w.Elapsed); w.Reset(); w.Start(); for (int i = 0; i < 10000000; i++) { d = Math.Sin(1); } w.Stop(); Console.WriteLine(w.Elapsed); }

Salida:

00:00:00.4269033 // with try/catch 00:00:00.4260383 // without.

En milisegundos:

449 416

Nuevo código:

for (int j = 0; j < 10; j++) { Stopwatch w = new Stopwatch(); double d = 0; w.Start(); for (int i = 0; i < 10000000; i++) { try { d = Math.Sin(d); } catch (Exception ex) { Console.WriteLine(ex.ToString()); } finally { d = Math.Sin(d); } } w.Stop(); Console.Write(" try/catch/finally: "); Console.WriteLine(w.ElapsedMilliseconds); w.Reset(); d = 0; w.Start(); for (int i = 0; i < 10000000; i++) { d = Math.Sin(d); d = Math.Sin(d); } w.Stop(); Console.Write("No try/catch/finally: "); Console.WriteLine(w.ElapsedMilliseconds); Console.WriteLine(); }

Nuevos resultados:

try/catch/finally: 382 No try/catch/finally: 332 try/catch/finally: 375 No try/catch/finally: 332 try/catch/finally: 376 No try/catch/finally: 333 try/catch/finally: 375 No try/catch/finally: 330 try/catch/finally: 373 No try/catch/finally: 329 try/catch/finally: 373 No try/catch/finally: 330 try/catch/finally: 373 No try/catch/finally: 352 try/catch/finally: 374 No try/catch/finally: 331 try/catch/finally: 380 No try/catch/finally: 329 try/catch/finally: 374 No try/catch/finally: 334


Vea la discusión sobre la implementación de prueba / captura para una discusión de cómo funcionan los bloques de prueba / captura, y cómo algunas implementaciones tienen una sobrecarga alta, y algunas tienen una sobrecarga nula, cuando no se producen excepciones. En particular, creo que la implementación de Windows de 32 bits tiene una gran sobrecarga, y la implementación de 64 bits no.


los bloques de prueba de prueba tienen un impacto insignificante en el rendimiento, pero el lanzamiento de la excepción puede ser bastante importante, probablemente aquí es donde su compañero de trabajo estaba confundido.


Explicación bastante completa del modelo de excepción .NET.

Tidbits de rendimiento de Rico Mariani: Costo de excepción: cuándo lanzar y cuándo no

El primer tipo de costo es el costo estático de tener un manejo de excepciones en su código. Las excepciones gestionadas en realidad funcionan comparativamente bien aquí, con lo cual me refiero a que el costo estático puede ser mucho más bajo que en C ++. ¿Por qué es esto? Bueno, el costo estático realmente se incurre en dos tipos de lugares: primero, los sitios reales de try / finally / catch / throw donde hay código para esas construcciones. Segundo, en el código sin cambios, existe el costo oculto asociado con el seguimiento de todos los objetos que deben destruirse en caso de que se produzca una excepción. Hay una cantidad considerable de lógica de limpieza que debe estar presente y la parte engañosa es que incluso el código que no arroja o atrapa o que no tiene un uso abierto de las excepciones todavía tiene la carga de saber cómo limpiar después de sí mismo.

Dmitriy Zaslavskiy:

Según la nota de Chris Brumme: También hay un costo relacionado con el hecho de que el JIT no está realizando algunas optimizaciones en presencia de captura


try..catch impacto real de un try..catch en un circuito cerrado, y es demasiado pequeño por sí solo para ser un problema de rendimiento en cualquier situación normal.

Si el bucle funciona muy poco (en mi prueba hice un x++ ), puede medir el impacto del manejo de excepciones. El bucle con manejo de excepciones tardó aproximadamente diez veces más en ejecutarse.

Si el bucle realiza algún trabajo real (en mi prueba llamé el método Int32.Parse), el manejo de excepciones tiene muy poco impacto para ser medible. Tengo una diferencia mucho mayor al cambiar el orden de los bucles ...