No quiero una discusión sobre cuándo y no lanzar excepciones. Deseo resolver un problema simple. El 99% de las veces, el argumento para no arrojar excepciones gira en torno a que son lentos, mientras que el otro lado afirma (con prueba de referencia) que la velocidad no es el problema. He leído numerosos blogs, artículos y publicaciones relacionadas con un lado u otro. Entonces, ¿cuál es?
Algunos enlaces de las respuestas: Skeet , Mariani , Brumme .
c#
.net
performance
exception
Goran
fuente
fuente
Respuestas:
Estoy en el lado "no lento", o más precisamente, "no lo suficientemente lento como para que valga la pena evitarlos en el uso normal". He escrito dos artículos cortos sobre esto. Hay críticas sobre el aspecto de referencia, que en su mayoría se reducen a "en la vida real habría más pila por recorrer, por lo que volaría el caché, etc.", pero usar códigos de error para avanzar en la pila también volar el caché, así que no lo veo como un argumento particularmente bueno.
Solo para dejarlo claro: no apoyo el uso de excepciones donde no son lógicas. Por ejemplo,
int.TryParse
es completamente apropiado para convertir datos de un usuario. Es apropiado cuando se lee un archivo generado por máquina, donde la falla significa "El archivo no está en el formato que debe ser, realmente no quiero tratar de manejar esto ya que no sé qué más podría estar mal". "Al usar excepciones en "solo circunstancias razonables", nunca he visto una aplicación cuyo rendimiento se haya visto significativamente afectado por las excepciones. Básicamente, las excepciones no deberían ocurrir a menudo a menos que tenga problemas de corrección significativos, y si tiene problemas de corrección significativos, entonces el rendimiento no es el mayor problema que enfrenta.
fuente
Existe la respuesta definitiva a esto del tipo que los implementó: Chris Brumme. Escribió un excelente artículo de blog sobre el tema (advertencia: es muy largo) (advertencia2: está muy bien escrito, si eres un experto en tecnología, lo leerás hasta el final y luego tendrás que recuperar tus horas después del trabajo :) )
El resumen ejecutivo: son lentos. Se implementan como excepciones Win32 SEH, por lo que algunos incluso pasarán el límite de CPU del anillo 0. Obviamente, en el mundo real, estará haciendo muchos otros trabajos, por lo que la extraña excepción no se notará en absoluto, pero si los usa para el flujo del programa, espere que su aplicación se vea afectada. Este es otro ejemplo de la máquina de marketing de MS que nos perjudica. Recuerdo que un microsoftie nos contó cómo incurrieron en una sobrecarga absolutamente cero, lo cual es completo.
Chris da una cita pertinente:
fuente
No tengo idea de qué están hablando las personas cuando dicen que son lentas solo si son arrojadas.
EDITAR: si no se lanzan excepciones, entonces eso significa que está haciendo una nueva excepción () o algo así. De lo contrario, la excepción hará que se suspenda el subproceso y se camine la pila. Esto puede estar bien en situaciones más pequeñas, pero en sitios web de alto tráfico, confiar en las excepciones como un mecanismo de flujo de trabajo o ruta de ejecución ciertamente le causará problemas de rendimiento. Las excepciones, per se, no son malas y son útiles para expresar condiciones excepcionales.
El flujo de trabajo de excepción en una aplicación .NET usa excepciones de primera y segunda oportunidad. Para todas las excepciones, incluso si las está capturando y manejando, el objeto de excepción aún se crea y el marco aún debe recorrer la pila para buscar un controlador. Si atrapas y vuelves a lanzar, por supuesto, eso llevará más tiempo: obtendrás una excepción de primera oportunidad, cógela, vuelve a lanzarla, causando otra excepción de primera oportunidad, que luego no encuentra un controlador, lo que causa Una excepción de segunda oportunidad.
Las excepciones también son objetos en el montón, por lo que si está lanzando toneladas de excepciones, entonces está causando problemas de rendimiento y memoria.
Además, de acuerdo con mi copia de "Pruebas de rendimiento de aplicaciones web de Microsoft .NET" escrita por el equipo de ACE:
"El manejo de excepciones es costoso. La ejecución del subproceso involucrado se suspende mientras CLR se repite a través de la pila de llamadas en busca del controlador de excepciones correcto, y cuando se encuentra, el controlador de excepciones y algunos bloques finalmente deben tener la oportunidad de ejecutarse antes de que se pueda realizar el procesamiento regular ".
Mi propia experiencia en el campo demostró que reducir las excepciones ayudó significativamente al rendimiento. Por supuesto, hay otras cosas que debe tener en cuenta al realizar pruebas de rendimiento, por ejemplo, si se dispara su E / S de disco, o si sus consultas están en segundos, entonces ese debería ser su enfoque. Pero encontrar y eliminar excepciones debería ser una parte vital de esa estrategia.
fuente
El argumento, según tengo entendido, no es que arrojar excepciones es malo, son lentas per se. En cambio, se trata de usar la construcción throw / catch como una forma de primera clase de controlar la lógica de aplicación normal, en lugar de las construcciones condicionales más tradicionales.
A menudo, en la lógica de aplicación normal, realiza bucles donde la misma acción se repite miles / millones de veces. En este caso, con un perfil muy simple (vea la clase Cronómetro), puede ver por sí mismo que lanzar una excepción en lugar de decir una declaración simple if puede resultar ser mucho más lenta.
De hecho, una vez leí que el equipo de .NET en Microsoft introdujo los métodos TryXXXXX en .NET 2.0 a muchos de los tipos básicos de FCL específicamente porque los clientes se quejaban de que el rendimiento de sus aplicaciones era muy lento.
Resulta que en muchos casos esto se debió a que los clientes intentaban la conversión de tipos de valores en un bucle y cada intento fallaba. Se lanzó una excepción de conversión y luego fue capturada por un controlador de excepciones que luego se tragó la excepción y continuó el ciclo.
Microsoft ahora recomienda que los métodos TryXXX se utilicen particularmente en esta situación para evitar tales posibles problemas de rendimiento.
Podría estar equivocado, pero parece que no estás seguro de la veracidad de los "puntos de referencia" sobre los que has leído. Solución simple: Pruébelo usted mismo.
fuente
Mi servidor XMPP ganó un gran impulso de velocidad (lo siento, no hay números reales, meramente de observación) después de que intenté evitar que ocurrieran (como comprobar si un socket está conectado antes de intentar leer más datos) y darme formas de evitarlos. (los métodos mencionados de TryX). Eso fue con solo unos 50 usuarios virtuales activos (chateando).
fuente
Solo para agregar mi propia experiencia reciente a esta discusión: en línea con la mayoría de lo que está escrito anteriormente, encontré que lanzar excepciones es extremadamente lento cuando se realiza de forma repetida, incluso sin el depurador en ejecución. Acabo de aumentar el rendimiento de un gran programa que estoy escribiendo en un 60% al cambiar alrededor de cinco líneas de código: cambiar a un modelo de código de retorno en lugar de lanzar excepciones. De acuerdo, el código en cuestión se ejecutaba miles de veces y potencialmente arrojaba miles de excepciones antes de que lo cambiara. Por lo tanto, estoy de acuerdo con la declaración anterior: arroje excepciones cuando algo importante realmente salga mal, no como una forma de controlar el flujo de la aplicación en cualquier situación "esperada".
fuente
Si los compara con los códigos de retorno, son lentos como el infierno. Sin embargo, como afirmaron los pósters anteriores, no desea lanzar el funcionamiento normal del programa, por lo que solo obtiene el impacto cuando se produce un problema y en la gran mayoría de los casos el rendimiento ya no importa (ya que la excepción implica un bloqueo de la carretera de todos modos).
Definitivamente vale la pena usarlos sobre los códigos de error, las ventajas son una gran OMI.
fuente
Nunca he tenido ningún problema de rendimiento con excepciones. Uso muchas excepciones, nunca uso códigos de retorno si puedo. Son una mala práctica y, en mi opinión, huelen a código de espagueti.
Creo que todo se reduce a cómo usas las excepciones: si las usas como códigos de retorno (cada llamada al método en la pila atrapa y vuelve a lanzar), sí, serán lentas, porque tienes sobrecarga cada captura / lanzamiento.
Pero si lanza en la parte inferior de la pila y atrapa en la parte superior (sustituye una cadena completa de códigos de retorno con un solo lanzamiento / captura), todas las operaciones costosas se realizan una vez.
Al final del día, son una característica de idioma válida.
Solo para probar mi punto
Ejecute el código en este enlace (demasiado grande para una respuesta).
Resultados en mi computadora:
marco@sklivvz:~/develop/test$ mono Exceptions.exe | grep PM
10/2/2008 2:53:32 PM
10/2/2008 2:53:42 PM
10/2/2008 2:53:52 PM
Las marcas de tiempo se muestran al principio, entre los códigos de retorno y las excepciones, al final. Se necesita el mismo tiempo en ambos casos. Tenga en cuenta que debe compilar con optimizaciones.
fuente
Pero mono lanza una excepción 10 veces más rápido que el modo independiente .net, y el modo independiente .net lanza una excepción 60 veces más rápido que el modo de depurador .net. (Las máquinas de prueba tienen el mismo modelo de CPU)
fuente
En el modo de lanzamiento, la sobrecarga es mínima.
A menos que vaya a utilizar excepciones para el control de flujo (por ejemplo, salidas no locales) de forma recursiva, dudo que pueda notar la diferencia.
fuente
En el CLR de Windows, para una cadena de llamadas de profundidad 8, lanzar una excepción es 750 veces más lento que verificar y propagar un valor de retorno. (ver abajo para puntos de referencia)
Este alto costo de excepciones se debe a que el CLR de Windows se integra con algo llamado Manejo de excepciones estructuradas de Windows . Esto permite que las excepciones sean capturadas y lanzadas correctamente en diferentes tiempos de ejecución e idiomas. Sin embargo, es muy muy lento.
Las excepciones en el tiempo de ejecución Mono (en cualquier plataforma) son mucho más rápidas, ya que no se integra con SEH. Sin embargo, hay una pérdida de funcionalidad al pasar excepciones a través de múltiples tiempos de ejecución porque no usa nada como SEH.
Aquí hay resultados abreviados de mi punto de referencia de excepciones vs valores de retorno para el CLR de Windows.
Y aquí está el código ...
fuente
Una nota rápida aquí sobre el rendimiento asociado con la captura de excepciones.
Cuando la ruta de ejecución entra en un bloque de "prueba", no ocurre nada mágico. No hay instrucciones de 'prueba', y ningún costo asociado con entrar o salir del bloque de prueba. La información sobre el bloque try se almacena en los metadatos del método, y estos metadatos se usan en tiempo de ejecución cada vez que se genera una excepción. El motor de ejecución camina por la pila buscando la primera llamada contenida en un bloque de prueba. Cualquier sobrecarga asociada con el manejo de excepciones ocurre solo cuando se lanzan excepciones.
fuente
Cuando se escriben clases / funciones para que otros las usen, parece difícil decir cuándo son apropiadas las excepciones. Hay algunas partes útiles de BCL que tuve que abandonar e ir a pinvoke porque arrojan excepciones en lugar de devolver errores. En algunos casos, puede solucionarlo, pero para otros, como System.Management and Performance Counters, hay usos en los que necesita hacer bucles en los que BCL genera excepciones con frecuencia.
Si está escribiendo una biblioteca y existe una posibilidad remota de que su función se pueda usar en un bucle y existe la posibilidad de una gran cantidad de iteraciones, use el patrón Try .. o alguna otra forma de exponer los errores además de las excepciones. E incluso entonces, es difícil decir cuánto se llamará a su función si está siendo utilizada por muchos procesos en un entorno compartido.
En mi propio código, las excepciones solo se producen cuando las cosas son tan excepcionales que es necesario mirar el rastro de la pila y ver qué salió mal y luego solucionarlo. Así que prácticamente reescribí partes de BCL para usar el manejo de errores basado en el patrón Try .. en lugar de excepciones.
fuente