Durante una revisión de código con un empleado de Microsoft, encontramos una gran sección de código dentro de un try{}
bloque. 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 debe estar fuera de los bloques try / catch, y que solo se deben verificar las secciones importantes. El empleado de Microsoft agregó y dijo que un próximo libro blanco advierte contra bloques de prueba / captura incorrectos.
Miré a mi alrededor y descubrí que puede afectar las optimizaciones , pero parece que solo se aplica cuando una variable se comparte entre ámbitos.
No estoy preguntando sobre la capacidad de mantenimiento del código, o incluso manejando las excepciones correctas (el código en cuestión necesita ser refactorizado, sin duda). Tampoco me refiero al uso de excepciones para el control de flujo, esto es claramente incorrecto en la mayoría de los casos. Esos son asuntos importantes (algunos son más importantes), pero no es el foco aquí.
¿Cómo afectan los bloques try / catch al rendimiento cuando no se lanzan excepciones ?
fuente
Respuestas:
Revisalo.
Salida:
En milisegundos:
Nuevo código:
Nuevos resultados:
fuente
Después de ver todas las estadísticas con try / catch y sin try / catch, la curiosidad me obligó a mirar hacia atrás. hacia para ver qué se genera para ambos casos. Aquí está el código:
C#:
MSIL:
C#:
MSIL:
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 que las cosas son bastante iguales que para el método sin intentar / atrapar hasta la línea diecisieteIL_0021: leave.s IL_002f
. Si ocurre una excepción, el control salta a la línea; de loIL_0025: ldloc.0
contrario, saltamos a la etiquetaIL_002d: leave.s IL_002f
y la función regresa.Puedo suponer con seguridad que si no se producen excepciones, es la sobrecarga de crear variables locales para contener
soloobjetos de excepción y una instrucción de salto.fuente
No. Si las optimizaciones triviales que impide un bloque try / finally realmente tienen un impacto medible en su programa, probablemente no debería usar .NET en primer lugar.
fuente
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
Dmitriy Zaslavskiy:
fuente
La estructura es diferente en el ejemplo de Ben M . Se extenderá por encima dentro del interior
for
bucle que hará que no sea una buena comparación entre los dos casos.Lo siguiente es más preciso para la comparación donde el código completo para verificar (incluida la declaración de variables) está dentro del bloque Try / Catch:
Cuando ejecuté el código de prueba original de Ben M , noté una diferencia tanto en la configuración de Debug como en la de 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 :
según estas pruebas, creo que podemos decir que Try / Catch sí tener un pequeño impacto en el rendimiento.
EDITAR:
intenté aumentar el valor del bucle de 10000000 a 1000000000, y volví a ejecutarlo en Release para obtener algunas diferencias en el lanzamiento, y el resultado fue este:
Usted ve que el resultado es inconsecuente. ¡En algunos casos, la versión que usa Try / Catch es realmente más rápida!
fuente
try/catch
aquí. Estás cronometrando 12 intentos / capturas en la sección crítica contra bucles de 10M. El ruido del bucle erradicará cualquier influencia que tenga el try / catch. si, en cambio, coloca el try / catch dentro del ciclo cerrado y compara con / sin, terminaría con el costo del try / catch. (sin duda, tal codificación no es una buena práctica en general, pero si desea cronometrar la sobrecarga de una construcción, así es como lo hace). Hoy en día, BenchmarkDotNet es la herramienta de referencia para tiempos de ejecución confiables.Probé el 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 ciclo funciona muy poco (en mi prueba hice un
x++
), puede medir el impacto del manejo de excepciones. El ciclo con manejo de excepciones tardó aproximadamente diez veces más en ejecutarse.Si el ciclo hace un trabajo real (en mi prueba llamé al método Int32.Parse), el manejo de excepciones tiene muy poco impacto para ser medible. Obtuve una diferencia mucho mayor al cambiar el orden de los bucles ...
fuente
intente que los bloques de captura tengan un impacto insignificante en el rendimiento, pero la excepción Lanzar puede ser bastante considerable, probablemente aquí es donde su compañero de trabajo estaba confundido.
fuente
El try / catch TIENE impacto en el rendimiento.
Pero no es un gran impacto. la complejidad try / catch es generalmente O (1), al igual que una asignación simple, excepto cuando se colocan en un bucle. Entonces tienes que usarlos sabiamente.
Aquí hay una referencia sobre el rendimiento try / catch (aunque no explica la complejidad del mismo, pero está implícito). Echa un vistazo a la sección Lanzar menos excepciones
fuente
En teoría, un bloque try / catch no tendrá efecto en el comportamiento del código a menos que realmente ocurra una excepción. Sin embargo, hay algunas circunstancias excepcionales en las que la existencia de un bloque try / catch puede tener un efecto importante, y algunas infrecuentes pero apenas oscuras en las que el efecto puede ser notable. La razón de esto es ese código dado como:
el compilador puede optimizar la declaración1 basándose en el hecho de que la declaración2 está garantizada para ejecutarse antes que la declaración3. Si el compilador puede reconocer que thing1 no tiene efectos secundarios y que thing2 realmente no usa x, puede omitir de manera segura thing1 por completo. Si [como en este caso] thing1 fuera costoso, eso podría ser una optimización importante, aunque los casos en que thing1 es costoso también son los que el compilador sería menos probable que optimice. Supongamos que el código fue cambiado:
Ahora existe una secuencia de eventos donde la instrucción3 podría ejecutarse sin que la instrucción2 se haya ejecutado. Incluso si nada en el código para
thing2
podría arrojar una excepción, sería posible que otro subproceso pudiera usar unInterlocked.CompareExchange
para notar queq
se borró y establecerloThread.ResetAbort
, y luego realizar unaThread.Abort()
instrucción anterior2 en la que escribió su valorx
. Luego,catch
se ejecutaríaThread.ResetAbort()
[a través del delegadoq
], permitiendo que la ejecución continúe con la instrucción3. Tal secuencia de eventos sería, por supuesto, excepcionalmente improbable, pero se requiere un compilador para generar código que funcione de acuerdo con las especificaciones, incluso cuando ocurran tales eventos improbables.En general, es mucho más probable que el compilador note oportunidades de omitir bits de código simples que los complejos, y por lo tanto, sería raro que un try / catch pueda afectar mucho el rendimiento si nunca se lanzan excepciones. Aún así, hay algunas situaciones en las que la existencia de un bloque try / catch puede evitar optimizaciones que, de no ser por el try / catch, hubieran permitido que el código se ejecute más rápido.
fuente
Aunque " Prevenir es mejor que manejar ", en la perspectiva del rendimiento y la eficiencia, podríamos elegir el try-catch en lugar de la prevaricación. Considere el siguiente código:
Aquí está el resultado:
fuente
Consulte la discusión sobre la implementación de try / catch para obtener una explicación de cómo funcionan los bloques try / catch y cómo algunas implementaciones tienen una sobrecarga elevada y otras tienen sobrecarga cero, cuando no se producen excepciones. En particular, creo que la implementación de Windows de 32 bits tiene una alta sobrecarga, y la implementación de 64 bits no.
fuente