practices practice management good exceptions custom business best java performance exception

java - practice - Qué tan costosos son las excepciones



java exceptions list (10)

Esta pregunta ya tiene una respuesta aquí:

¿Sabes lo caro que es el lanzamiento y manejo de excepciones en java?

Tuvimos varias discusiones sobre el costo real de las excepciones en nuestro equipo. Algunos los evitan con la mayor frecuencia posible, algunos dicen que la pérdida de rendimiento al usar excepciones está sobrevalorada.

Hoy encontré la siguiente pieza de código en nuestro software:

private void doSomething() { try { doSomethingElse(); } catch(DidNotWorkException e) { log("A Message"); } goOn(); } private void doSomethingElse() { if(isSoAndSo()) { throw new DidNotWorkException(); } goOnAgain(); }

¿Cómo se compara el rendimiento de esto con

private void doSomething() { doSomethingElse(); goOn(); } private void doSomethingElse() { if(isSoAndSo()) { log("A Message"); return; } goOnAgain(); }

No quiero hablar sobre código estético ni nada, ¡solo se trata de un comportamiento en tiempo de ejecución! ¿Tienes experiencias / mediciones reales?


Creo que estás preguntando esto desde un ángulo ligeramente erróneo. Las excepciones están diseñadas para señalar casos excepcionales y como un mecanismo de flujo de programas para esos casos. Entonces la pregunta que debería hacerse es: ¿la "lógica" del código requiere excepciones?

Las excepciones generalmente están diseñadas para funcionar lo suficientemente bien en el uso para el que están destinadas. Si se usan de tal manera que son un cuello de botella, entonces, sobre todo, eso es probablemente una indicación de que solo están siendo utilizados para el punto final "lo incorrecto", es decir, lo que tienes subyacente es un diseño de programa problema en lugar de un problema de rendimiento .

Por el contrario, si la excepción parece estar siendo "utilizada para lo correcto", entonces eso probablemente significa que también funcionará bien.


Creo que si nos atenemos a utilizar excepciones donde se necesitan (condiciones excepcionales), los beneficios superan con creces cualquier penalización de rendimiento que pueda estar pagando. Digo que podría ser porque el costo es realmente una función de la frecuencia con la que se lanzan las excepciones en la aplicación en ejecución.
En el ejemplo que das, parece que la falla no es inesperada o catastrófica, por lo que el método debería estar devolviendo un bool para indicar su estado de éxito en lugar de usar excepciones, haciéndolos parte del flujo de control regular.
En los pocos trabajos de mejora de rendimiento en los que participé, el costo de las excepciones ha sido bastante bajo. Pasaría mucho más tiempo para mejorar la complejidad de las operaciones comunes y repetitivas.


Digamos que la excepción no ocurrirá al intentar ejecutar las declaraciones 1 y 2. ¿Hay CUALQUIER resultado de rendimiento entre esos dos códigos de muestra?

En caso DoSomething() , ¿qué DoSomething() si el método DoSomething() tiene que hacer una gran cantidad de trabajo (un montón de llamadas a otros métodos, etc.)?

1:

try { DoSomething(); } catch (...) { ... }

2:

DoSomething();


Esto es intrínsecamente específico de JVM, por lo que no debes confiar ciegamente en los consejos que se te den, sino que midas en tu situación. No debería ser difícil crear un "lanzar un millón de excepciones e imprimir la diferencia de System.currentTimeMillis" para tener una idea aproximada.

Para el fragmento de código que enumera, yo personalmente requeriría que el autor original documente por qué usó el lanzamiento de excepción aquí ya que no es el "camino de menos sorpresas" que es crucial para mantenerlo más tarde.

(Cada vez que haces algo de forma intrincada, haces que el lector haga un trabajo innecesario para entender por qué lo hiciste así en vez de hacerlo de la manera habitual; ese trabajo debe ser justificado en mi opinión por el autor explicando cuidadosamente por qué fue hecho así porque DEBE haber una razón).

Las excepciones son una herramienta muy, muy útil, pero solo deben usarse cuando sea necesario :)


Gracias por todas las respuestas.

Finalmente, seguí la sugerencia de Thorbjørn y escribí un pequeño programa de prueba, midiendo el rendimiento yo mismo. El resultado es: No hay diferencia entre las dos variantes (en materia de rendimiento).

Aunque no pregunté sobre la estética del código o algo así, es decir, cuál era la intención de las excepciones, etc., la mayoría de ustedes abordó también ese tema. Pero en realidad las cosas no siempre son tan claras ... En el caso que nos ocupa, el código nació hace mucho tiempo cuando la situación en la cual se lanzó la excepción parecía ser excepcional. Hoy la biblioteca se usa de manera diferente, se modificó el comportamiento y el uso de las diferentes aplicaciones, la cobertura de las pruebas no es muy buena, pero el código sigue haciendo su trabajo, un poco demasiado lento (¡¡por eso pedí rendimiento !!). En esa situación, creo que debería haber una buena razón para cambiar de A a B, lo que, en mi opinión, no puede ser "¡No es para lo que se hicieron las excepciones!".

Resultó que el registro ("Un mensaje") es muy caro (en comparación con todo lo demás), así que creo que me desharé de esto.

EDITAR :

El código de prueba es exactamente como el de la publicación original, llamado por un método testPerfomance() en un bucle que está rodeado por System.currentTimeMillis() -calls para obtener el tiempo de ejecución ... pero:

Revisé el código de prueba ahora, cambié de todo lo demás (la declaración de registro) y bucle 100 veces más que antes, y resulta que ahorras 4.7 segundos para un millón de llamadas cuando utilizas B en lugar de A desde la publicación original. Como Ron dijo, fillStackTrace es la parte más cara (+1 para eso) y puedes ahorrar casi lo mismo (4.5 segundos) si la sobreescribes (en el caso de que no la necesites, como yo). En general, sigue siendo una diferencia casi nula en mi caso, ya que el código se llama 1000 veces por hora y las mediciones muestran que puedo ahorrar 4,5 milisegundos en ese momento ...

Por lo tanto, la primera parte de mi primera respuesta fue un poco engañosa, pero lo que dije acerca de equilibrar el costo-beneficio de una refactorización sigue siendo cierto.


La parte lenta de las excepciones es construir el seguimiento de la pila (en el constructor de java.lang.Throwable ), que depende de la profundidad de la pila. Tirar en sí mismo no es lento.

Use excepciones para señalar fallas. El impacto en el rendimiento es insignificante y el seguimiento de la pila ayuda a identificar la causa de la falla.

Si necesita excepciones para el flujo de control (no recomendado), y el perfil muestra que las excepciones son el cuello de botella, entonces cree una subclase de excepción que anule fillInStackTrace() con una implementación vacía. Alternativamente (o adicionalmente) instanciar solo una excepción, guárdela en un campo y siempre arroje la misma instancia.

A continuación se muestran excepciones sin seguimientos de pila al agregar un método simple al micro benchmark ( aunque defectuoso ) en la respuesta aceptada :

public class DidNotWorkException extends Exception { public Throwable fillInStackTrace() { return this; } }

Ejecutarlo utilizando JVM en el modo de servidor (versión 1.6.0_24 en Windows 7) da como resultado:

Exception:99ms Boolean:12ms Exception:92ms Boolean:11ms

La diferencia es lo suficientemente pequeña como para ser ignorable en la práctica.


La parte más lenta de lanzar una excepción es completar el seguimiento de la pila .

Si pre-crea su excepción y la vuelve a usar, el JIT puede optimizarla a " un goto a nivel de máquina ".

Dicho todo esto, a menos que el código de su pregunta esté en un círculo muy cerrado, la diferencia será insignificante.


Las excepciones no son gratis ... así que son caras :-)

El libro Effective Java cubre esto con buen detalle.

  • Ítem ​​39 Use excepciones solo para condiciones excepcionales.
  • Ítem ​​40 Use excepciones para condiciones recuperables

El autor descubrió que las excepciones daban como resultado que el código se ajustara 70 veces más lentamente para su caso de prueba en su máquina con su combinación particular de VM y sistema operativo.


No me he molestado en leer Excepciones, pero haciendo una prueba muy rápida con algún código modificado suyo, llegué a la conclusión de que la circunstancia de la Excepción es bastante más lenta que el caso booleano.

Obtuve los siguientes resultados:

Exception:20891ms Boolean:62ms

De este código:

public class Test { public static void main(String args[]) { Test t = new Test(); t.testException(); t.testBoolean(); } public void testException() { long start = System.currentTimeMillis(); for(long i = 0; i <= 10000000L; ++i) doSomethingException(); System.out.println("Exception:" + (System.currentTimeMillis()-start) + "ms"); } public void testBoolean() { long start = System.currentTimeMillis(); for(long i = 0; i <= 10000000L; ++i) doSomething(); System.out.println("Boolean:" + (System.currentTimeMillis()-start) + "ms"); } private void doSomethingException() { try { doSomethingElseException(); } catch(DidNotWorkException e) { //Msg } } private void doSomethingElseException() throws DidNotWorkException { if(!isSoAndSo()) { throw new DidNotWorkException(); } } private void doSomething() { if(!doSomethingElse()) ;//Msg } private boolean doSomethingElse() { if(!isSoAndSo()) return false; return true; } private boolean isSoAndSo() { return false; } public class DidNotWorkException extends Exception {} }

Tontamente, no leí mi código lo suficientemente bien y anteriormente tenía un error (qué embarazoso), si alguien pudiera verificar este código triple, lo haría mucho más, por si acaso me voy senil.

Mi especificación es:

  • Compilado y ejecutado el 1.5.0_16
  • Sun JVM
  • WinXP SP3
  • Intel Centrino Duo T7200 (2.00 Ghz, 977Mhz)
  • 2.00 GB Ram

En mi opinión, debería observar que los métodos de no excepción no dan el error de registro en doSomethingElse, sino que devuelven un booleano para que el código de llamada pueda manejar un error. Si hay varias áreas en las que esto puede fallar, es posible que sea necesario iniciar sesión en un error o lanzar una excepción.


No tengo mediciones reales, pero lanzar una excepción es más caro.

Ok, este es un enlace sobre el framework .NET, pero creo que lo mismo se aplica a Java también:

excepciones y rendimiento

Dicho eso, no debes dudar en usarlos cuando sea ​​apropiado . Es decir: no los use para controlar el flujo, pero úselos cuando ocurra algo excepcional; algo que no esperabas que sucediera