Uso de "excepciones de captura" para mejorar la legibilidad, ¿bueno o malo?

9

En la sección Cuándo usar la excepción en El programador pragmático , el libro escribe que en lugar de:

retcode = OK;
     if (socket.read(name) != OK) {
      retcode = BAD_READ;
    }
     else {
      processName(name);
       if (socket.read(address) != OK) {
        retcode = BAD_READ;
      }
       else {
        processAddress(address);
         if (socket.read(telNo) != OK) {
          retcode = BAD_READ;
        }
         else {
          //  etc, etc...
        }
      }
    }
     return retcode;

, ellos prefieren:

 retcode = OK;
     try {
      socket.read(name);
      process(name);
      socket.read(address);
      processAddress(address);
      socket.read(telNo);
      //  etc, etc...
    }
     catch (IOException e) {
      retcode = BAD_READ;
      Logger.log( "Error reading individual: " + e.getMessage());
    }
     return retcode;

simplemente porque se ve más ordenado. Estoy todo para el código más limpio, sin embargo, no es necesaria la captura de excepciones de un cuello de botella?

Puedo entender que deberíamos renunciar a la optimización minúscula para un código más ordenado (al menos el 99% de las veces), sin embargo, por lo que sé, las excepciones de captura pertenecen a la clase de código que tiene un retraso notable en el tiempo de ejecución. Por lo tanto, me preguntaba cuál es la justificación de que se prefiere la segunda parte del código sobre el primer código.

O más bien, ¿qué código preferirías?

Pacerier
fuente
emigrado de revisión de código porque su una pregunta mejores prácticas no es una cuestión de revisión de código
Winston Ewert
1
OMI, si espera poder leer esos valores del socket, entonces un IOEx es excepcional.
Steven Evers el
Relevante para esta discusión: Uso de objetos tirables para otras cosas que no sean excepciones
Daniel R Hicks
¿Has medido?
@ ThorbjørnRavnAndersen, por supuesto, estaba leyendo el libro y no tengo un "caso de prueba" real para medir ... pero si lo medimos en un código artificial, obviamente hay una penalización de rendimiento, entonces
Pacerier

Respuestas:

18

Las excepciones suelen ser más lentas si realmente se lanzan. Por lo general, las excepciones en situaciones como esta son raras, por lo que no es algo de lo que deba preocuparse. Realmente solo debe preocuparse por el desempeño de las excepciones si están sucediendo todo el tiempo y, por lo tanto, son un factor significativo.

El segundo código es mejor porque es mucho más fácil seguir la lógica. El manejo de errores está bien localizado. La única objeción es que si está usando excepciones, entonces use excepciones. No atrape las excepciones y luego devuelva un código de error.

Winston Ewert
fuente
44
De acuerdo, odio ver los códigos de error en un idioma que tiene excepciones. La mayoría de las veces debe atrapar, registrar, volver a lanzar sin romper la pila de llamadas. Y la mayoría de las veces, no importa si un caso de "excepción" es más lento. La aplicación tiene un problema, tómese el tiempo para manejarla correctamente. Siempre y cuando la excepción sea excepcional y no forme parte del funcionamiento normal o la ruta feliz de la aplicación, no suele ser un olor a código.
CaffGeek el
Por cierto, me preguntaba cómo justificamos la EOFException que DataStreams usa para detectar un final de línea (el final de la línea no es excepcional), como se demuestra en docs.oracle.com/javase/tutorial/essential/io/datastreams.html ?
Pacerier
@Pacerier, podría ser excepcional. Quedarse sin entrada en la línea podría indicar datos incorrectos o algo que va terriblemente mal. Sospecho que el uso previsto es analizar varios campos de una línea.
Winston Ewert
@Downvoter, si tiene un problema con la respuesta, ¡explíquelo!
Winston Ewert
5

Bueno, como también dicen, las excepciones deberían manejar los casos excepcionales , y dado que este es un caso excepcional (es decir, algo que ocurre muy poco y en el medio), diría que esto también se aplica aquí.

Además, aconsejaría que no se optimicen las cosas como esta. Céntrese más en la legibilidad del código , ya que pasará mucho más tiempo leyendo código que escribiendo código nuevo.

Para asegurarse realmente de que este es un cuello de botella en el rendimiento, haga algunos perfiles y analice y concéntrese en las tareas más críticas basadas en ese análisis.

Andreas Johansson
fuente
5

Puedo entender que deberíamos renunciar a la optimización minúscula para un código más ordenado (al menos el 99% de las veces), sin embargo, por lo que sé, las excepciones de captura pertenecen a la clase de código que tiene un retraso notable en el tiempo de ejecución.

¿De dónde sabes eso? ¿Cómo define "retraso notable"?

Este es exactamente el tipo de mito de rendimiento vago (y completamente incorrecto) que conduce a optimizaciones prematuras inútiles.

Lanzar y atrapar una excepción puede ser lento en comparación con sumar dos números, pero es completamente insignificante en comparación con la lectura de datos de un socket. Probablemente podría lanzar y atrapar mil excepciones al mismo tiempo que ha leído un solo paquete de red. Y no estamos hablando de miles de excepciones aquí, solo una.

Michael Borgwardt
fuente
Vengo de .NET así que perdón mis conocimientos de Java, pero personalmente he experimentado la captura de excepciones en .NET provocando retrasos dementes (magnitud de segundos) que se fijaron mediante la eliminación de "captura-excepción código" ..
Pacerier
@Pacerier, ¿en serio? magnitud de segundos? Supongo que tal vez estaba atrapando muchas excepciones. Entonces pude ver eso.
Winston Ewert
3
@Pacerier: Si las excepciones están causando retrasos locos, claramente lo que las está causando no es excepcional y, por lo tanto, debe manejarse de otras maneras. Ningún sistema debería tomar segundos para procesar una excepción, y creo que Microsoft es lo suficientemente competente como para evitar eso.
David Thornley el
5

Estoy de acuerdo con las respuestas que ya ha recibido diciendo que este es un caso excepcional, por lo que es razonable utilizar el manejo de excepciones.

Al abordar sus problemas de rendimiento más directamente, creo que debe mantener un sentido de escala sobre las cosas. Lanzar / atrapar una excepción en estas circunstancias es probable que tome uno del orden de un microsegundo. A medida que avanza el control de flujo, eso es lento , muchas veces más lento que las ifdeclaraciones normales , no hay duda al respecto.

Al mismo tiempo, tenga en cuenta que lo que está haciendo aquí es leer datos de una red. A 10 megabits por segundo (lento para una red local, pero bastante rápido a medida que van las conexiones a Internet), eso es en el mismo orden que el tiempo para leer un byte de información.

Por supuesto, esa sobrecarga solo se incurre cuando realmente lanzas la excepción también: cuando no se lanza, generalmente hay poca o ninguna sobrecarga (al menos por lo que he visto, la sobrecarga más significativa no es del manejo de excepciones en sí mismo, como la adición de más flujos potenciales de control, haciendo que el código sea más difícil de optimizar para el compilador).

En este caso, cuando se lanza una excepción, sus datos de escritura en el registro. Dada la naturaleza del registro, es muy probable que elimine esa salida tan pronto como la escriba (para asegurarse de que no se pierda). Escribir en el disco es (de nuevo) una operación bastante lenta: necesita un SSD de clase empresarial bastante rápido para llegar incluso al rango de decenas de miles de IOP / segundo. Un disco utilizado para iniciar sesión podría limitarse fácilmente a algo así como cientos de IOP / segundo.

En pocas palabras: hay casos en los que la sobrecarga de manejo de excepciones puede ser significativa, pero es casi seguro que no es uno de ellos.

Jerry Coffin
fuente