Usando "capturando exceções" para melhorar a legibilidade, Bom ou Ruim?

9

Na seção Quando usar exceção no programador pragmático , o livro escreve que, em vez 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;

, Eles preferem:

 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;

simplesmente porque parece mais limpo. Eu sou tudo para o código mais limpo, no entanto, não é desnecessário captura de exceções um gargalo de desempenho?

Eu posso entender que devemos desistir de otimização minúscula para código mais limpo (pelo menos 99% das vezes), no entanto, pelo que sei, capturar exceções pertence à classe de código que possui um atraso notável no tempo de execução. Por isso, eu queria saber qual é a justificativa de que o segundo trecho de código é preferido sobre o primeiro código?

Ou melhor, qual código você prefere?

Pacerier
fonte
migrou do Code Review, porque é uma questão de melhores práticas não uma revisão de código questão
Winston Ewert
11
IMO, se você espera poder ler essas valetas do soquete, um IOEx é excepcional.
Steven Evers
Relevantes para essa discussão: Usando Throwable por coisas que outras de Exceções
Daniel R Hicks
Você mediu?
@ ThorbjørnRavnAndersen é claro que eu estava lendo o livro e não tem um "caso de teste" real para medir .. mas se fizermos medi-la em um código artificial então, obviamente, há uma penalidade de desempenho, em seguida,
Pacerier

Respostas:

18

As exceções geralmente são mais lentas se forem lançadas. Geralmente, as exceções em situações como essa são raras, portanto não é algo com que você deva se preocupar. Você realmente só deve se preocupar com o desempenho das exceções, se elas estiverem acontecendo o tempo todo e, portanto, são um fator significativo.

O segundo código é melhor porque é muito mais fácil seguir a lógica. A manipulação de erros está bem localizada. A única objeção é que, se você estiver usando exceções, use exceções. Não pegue as exceções e, em seguida, retorne um código de erro.

Winston Ewert
fonte
4
Concordo, odeio ver códigos de erro em um idioma que possui exceções. Na maioria das vezes, você deve capturar, registrar e repetir o processo sem interromper a pilha de chamadas. E na maioria das vezes, não importa se um caso de "exceção" é mais lento. O aplicativo tem um problema, reserve um tempo para lidar com isso corretamente. Contanto que a exceção seja excepcional e não faça parte da operação normal, ou caminho feliz, do aplicativo, geralmente não é um cheiro de código.
CaffGeek #
Aliás, eu queria saber como justificamos a EOFException que o DataStreams usa para detectar um fim de linha (o fim de linha não é excepcional, é), como demonstrado em docs.oracle.com/javase/tutorial/essential/io/datastreams.html ?
Pacerier 12/11/11
@ Pacerier, poderia ser excepcional. Ficar sem entrada na linha pode indicar dados ruins ou algo terrivelmente errado. Eu suspeito que o uso pretendido é analisar vários campos fora de uma linha.
Winston Ewert
@ Downvoter, se você tiver um problema com a resposta, por favor, explique!
Winston Ewert
5

Bem, como também dizem, as exceções devem lidar com casos excepcionais , e como esse é um caso excepcional (isto é, algo que acontece muito pouco), eu diria que isso também se aplica aqui.

Além disso, eu aconselho a não otimizar micro coisas como essa. Concentre-se mais na legibilidade do código , pois você estará gastando muito mais tempo lendo o código do que escrevendo um novo código.

Para realmente garantir que esse seja um gargalo de desempenho, faça alguns perfis, analise e concentre-se nas tarefas mais críticas baseadas nessa análise.

Andreas Johansson
fonte
5

Eu posso entender que devemos desistir de otimização minúscula para código mais limpo (pelo menos 99% das vezes), no entanto, pelo que sei, capturar exceções pertence à classe de código que possui um atraso notável no tempo de execução.

De onde você sabe disso? Como você define "atraso perceptível"?

Esse é exatamente o tipo de mito de desempenho vago (e completamente errado) que leva a otimizações prematuras inúteis.

Lançar e capturar uma exceção pode ser lento em comparação à adição de dois números, mas é completamente insignificante comparado à leitura de dados de um soquete. Você provavelmente poderia lançar e capturar mil exceções ao mesmo tempo em que leu um único pacote de rede. E não estamos falando de milhares de exceções aqui, apenas uma única.

Michael Borgwardt
fonte
Eu venho de .NET tão pardon meu conhecimento de Java, mas eu pessoalmente tenho experimentado capturar exceções em .NET causando atrasos insanas (magnitude de segundos) que foram corrigidos através da remoção de "catching-exceção código" ..
Pacerier
@Pacerier, sério? magnitude de segundos? Eu acho que talvez estivesse pegando muitas exceções? Então eu pude ver isso.
Winston Ewert
3
@Pacerier: Se as exceções estão causando atrasos insanos, claramente o que as causa não é excepcional e, portanto, deve ser tratado de outras maneiras. Nenhum sistema deve levar segundos para processar uma exceção, e acho que a Microsoft é suficientemente competente para evitar isso.
David Thornley # 03
5

Eu concordo com as respostas que você já recebeu dizendo que esse é um caso excepcional, portanto, é razoável usar o tratamento de exceções.

Abordando suas preocupações de desempenho mais diretamente, acho que você precisa manter um senso de escala sobre as coisas. Lançar / capturar uma exceção nessas circunstâncias provavelmente levará uma da ordem de um microssegundo. Conforme o controle de fluxo é lento, muitas vezes mais lento que o normal if, não há dúvida sobre isso.

Ao mesmo tempo, lembre-se de que o que você está fazendo aqui é ler dados de uma rede. A 10 megabits por segundo (lento para uma rede local, mas bastante rápida conforme as conexões com a Internet), está na mesma ordem que o tempo para ler um byte de informação.

Obviamente, essa sobrecarga só ocorre quando você realmente lança a exceção - quando não é lançada, geralmente há pouca ou nenhuma sobrecarga (pelo menos pelo que vi, a sobrecarga mais significativa não é do manipulação de exceção em si, a partir da adição de mais fluxos de controle em potencial, dificultando a otimização do código para o compilador).

Nesse caso, quando uma exceção é lançada, seus dados de gravação no log. Dada a natureza do registro, é muito provável que você libere essa saída assim que você a escreve (para garantir que ela não seja perdida). Gravar em disco é (novamente) uma operação bastante lenta - você precisa de um SSD de classe empresarial razoavelmente rápido para chegar a dezenas de milhares de IOPs / segundo. Um disco usado para o log pode ser facilmente limitado a centenas de IOPs / segundo.

Conclusão: há casos em que a sobrecarga de manipulação de exceção pode ser significativa, mas esse quase certamente não é um deles.

Jerry Coffin
fonte