Não quero discutir sobre quando e não lançar exceções. Desejo resolver um problema simples. Em 99% do tempo, o argumento para não lançar exceções gira em torno de serem lentos, enquanto o outro lado afirma (com teste de benchmark) que a velocidade não é o problema. Eu li vários blogs, artigos e postagens de um lado ou de outro. Então qual é?
c#
.net
performance
exception
Goran
fonte
fonte
Respostas:
Estou do lado "não lento" - ou mais precisamente "não lento o suficiente para fazer valer a pena evitá-los em uso normal". Eu escrevi dois artigos curtos sobre isso. Existem críticas ao aspecto de benchmark, que são basicamente "na vida real, haveria mais pilha para passar, então você explodiria o cache etc." - mas o uso de códigos de erro para aumentar a pilha também explodir o cache, então não vejo isso como um argumento particularmente bom.
Só para esclarecer: não apoio o uso de exceções onde não são lógicas. Por exemplo,
int.TryParse
é totalmente apropriado para converter dados de um usuário. É apropriado ao ler um arquivo gerado por máquina, onde a falha significa "O arquivo não está no formato que deveria ser, eu realmente não quero tentar lidar com isso, pois não sei o que mais pode estar errado. "Ao usar exceções em "apenas circunstâncias razoáveis", nunca vi um aplicativo cujo desempenho foi significativamente prejudicado por exceções. Basicamente, as exceções não devem ocorrer com freqüência, a menos que você tenha problemas significativos de correção, e se você tiver problemas significativos de correção, o desempenho não será o maior problema que você enfrenta.
fonte
Existe a resposta definitiva para isso do cara que os implementou - Chris Brumme. Ele escreveu um excelente artigo de blog sobre o assunto (aviso - é muito longo) (aviso2 - é muito bem escrito, se você é um técnico, você o lerá até o fim e depois terá que compensar suas horas depois do trabalho :) )
O resumo executivo: eles são lentos. Eles são implementados como exceções do Win32 SEH, portanto, alguns até passarão do limite da CPU do anel 0! Obviamente, no mundo real, você fará muitos outros trabalhos para que a exceção estranha não seja notada, mas se você usá-los para o fluxo do programa, espere que seu aplicativo seja martelado. Este é outro exemplo da máquina de marketing da MS fazendo um desserviço. Lembro-me de um microsoftie nos dizendo como eles incorriam em zero absolutamente zero, o que é algo totalmente diferente.
Chris faz uma citação pertinente:
fonte
Não tenho idéia do que as pessoas estão falando quando dizem que são lentas apenas se forem jogadas.
Edição: Se exceções não são lançadas, isso significa que você está fazendo nova exceção () ou algo parecido. Caso contrário, a exceção fará com que o encadeamento seja suspenso e a pilha percorrida. Isso pode ser bom em situações menores, mas em sites de alto tráfego, depender de exceções como um mecanismo de fluxo de trabalho ou caminho de execução certamente causará problemas de desempenho. As exceções, por si só, não são ruins e são úteis para expressar condições excepcionais
O fluxo de trabalho de exceção em um aplicativo .NET usa exceções de primeira e segunda chance. Para todas as exceções, mesmo se você as estiver capturando e manipulando, o objeto de exceção ainda será criado e a estrutura ainda precisará percorrer a pilha para procurar um manipulador. Se você capturar e repetir novamente, é claro que isso levará mais tempo - você receberá uma exceção de primeira chance, capturá-la, reproduzi-la novamente, causando outra exceção de primeira chance, que não encontra um manipulador, o que causa uma exceção de segunda chance.
Exceções também são objetos no heap - portanto, se você estiver lançando várias exceções, estará causando problemas de desempenho e memória.
Além disso, de acordo com minha cópia de "Testes de desempenho Microsoft .NET Web Applications", escrita pela equipe da ACE:
"O tratamento de exceções é caro. A execução do encadeamento envolvido é suspensa enquanto o CLR recorre pela pilha de chamadas em busca do manipulador de exceções certo e, quando encontrado, o manipulador de exceções e algum número de blocos finalmente devem ter sua chance de executar antes que o processamento regular possa ser executado ".
Minha própria experiência no campo mostrou que a redução de exceções ajudou significativamente o desempenho. Obviamente, há outras coisas que você leva em consideração ao testar o desempenho - por exemplo, se a sua E / S de disco é filmada ou suas consultas são em segundos, esse deve ser seu foco. Mas encontrar e remover exceções deve ser uma parte vital dessa estratégia.
fonte
O argumento que eu entendo não é que lançar exceções seja ruim, elas são lentas por si só. Em vez disso, trata-se de usar a construção throw / catch como uma maneira de primeira classe de controlar a lógica normal do aplicativo, em vez das construções condicionais mais tradicionais.
Freqüentemente, na lógica normal do aplicativo, você executa um loop em que a mesma ação é repetida milhares / milhões de vezes. Nesse caso, com alguns perfis muito simples (consulte a classe Cronômetro), você pode ver por si mesmo que lançando uma exceção em vez de dizer uma declaração if simples pode ser substancialmente mais lenta.
De fato, li uma vez que a equipe .NET da Microsoft introduziu os métodos TryXXXXX no .NET 2.0 para muitos dos tipos básicos de FCL, especificamente porque os clientes estavam reclamando que o desempenho de seus aplicativos era muito lento.
Acontece que, em muitos casos, isso ocorreu porque os clientes estavam tentando a conversão de valores de tipo em um loop e cada tentativa falhou. Uma exceção de conversão foi lançada e capturada por um manipulador de exceções que engoliu a exceção e continuou o loop.
A Microsoft agora recomenda que os métodos TryXXX sejam usados particularmente nessa situação para evitar possíveis problemas de desempenho.
Eu posso estar errado, mas parece que você não tem certeza da veracidade dos "parâmetros de referência" sobre os quais você leu. Solução simples: Experimente você mesmo.
fonte
Meu servidor XMPP ganhou um grande aumento de velocidade (desculpe, não há números reais, puramente observacional) depois de tentar consistentemente impedir que eles aconteçam (como verificar se um soquete está conectado antes de tentar ler mais dados) e me oferecer maneiras de evitá-los (os métodos TryX mencionados). Isso ocorreu com apenas cerca de 50 usuários virtuais ativos (conversando).
fonte
Apenas para adicionar minha própria experiência recente a essa discussão: de acordo com a maior parte do que foi escrito acima, achei as exceções de lançamento extremamente lentas quando feitas repetidamente, mesmo sem o depurador em execução. Acabei de aumentar o desempenho de um programa grande que estou escrevendo em 60%, alterando cerca de cinco linhas de código: alternando para um modelo de código de retorno em vez de lançar exceções. Concedido, o código em questão estava sendo executado milhares de vezes e potencialmente lançava milhares de exceções antes de eu o alterar. Portanto, concordo com a afirmação acima: lance exceções quando algo importante realmente der errado, não como uma maneira de controlar o fluxo de aplicativos em qualquer situação "esperada".
fonte
Se você compará-los para retornar códigos, eles são lentos como o inferno. No entanto, como os cartazes anteriores declararam, você não deseja executar a operação normal do programa, portanto, você obtém o desempenho perfeito quando ocorre um problema e, na grande maioria dos casos, o desempenho não importa mais (pois a exceção implica um obstáculo).
Definitivamente vale a pena usar códigos de erro, as vantagens são vastas na IMO.
fonte
Eu nunca tive nenhum problema de desempenho com exceções. Eu uso muito exceções - nunca uso códigos de retorno, se puder. Eles são uma prática ruim e, na minha opinião, cheiram a código de espaguete.
Eu acho que tudo se resume a como você usa exceções: se você usá-las como códigos de retorno (cada chamada de método na pilha captura e repete), sim, elas serão lentas, porque você sobrecarregará cada captura / lançamento.
Mas se você joga no fundo da pilha e pega no topo (você substitui toda uma cadeia de códigos de retorno por um lançamento / captura), todas as operações caras são feitas uma vez.
No final do dia, eles são um recurso de idioma válido.
Só para provar meu ponto
Execute o código neste link (grande demais para uma resposta).
Resultados no meu computador:
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
Os registros de data e hora são emitidos no início, entre códigos de retorno e exceções, no final. Leva o mesmo tempo em ambos os casos. Observe que você precisa compilar com otimizações.
fonte
Mas o mono lança a exceção 10x mais rápido que o modo independente de .net e o modo independente de .net lança a exceção 60x mais rápido que o modo de depurador .net (As máquinas de teste têm o mesmo modelo de CPU)
fonte
No modo de liberação, a sobrecarga é mínima.
A menos que você esteja usando exceções para controle de fluxo (por exemplo, saídas não locais) de forma recursiva, duvido que você consiga perceber a diferença.
fonte
No Windows CLR, para uma cadeia de chamadas com profundidade 8, lançar uma exceção é 750 vezes mais lento do que verificar e propagar um valor de retorno. (veja abaixo os benchmarks)
Esse alto custo para exceções ocorre porque o Windows CLR se integra a algo chamado Windows Structured Exception Handling . Isso permite que as exceções sejam capturadas e lançadas corretamente em diferentes tempos de execução e idiomas. No entanto, é muito, muito lento.
Exceções no tempo de execução Mono (em qualquer plataforma) são muito mais rápidas, porque não se integra ao SEH. No entanto, há perda de funcionalidade ao passar exceções em vários tempos de execução, porque ele não usa nada como o SEH.
Aqui estão os resultados abreviados do meu benchmark de exceções versus valores de retorno para o Windows CLR.
E aqui está o código ..
fonte
Uma observação rápida aqui sobre o desempenho associado à captura de exceções.
Quando o caminho da execução entra em um bloco 'try', nada de mágico acontece. Não há instruções 'try' e nenhum custo associado à entrada ou saída do bloco try. As informações sobre o bloco try são armazenadas nos metadados do método e esses metadados são usados em tempo de execução sempre que uma exceção é gerada. O mecanismo de execução percorre a pilha procurando a primeira chamada que estava contida em um bloco try. Qualquer sobrecarga associada ao tratamento de exceções ocorre apenas quando as exceções são lançadas.
fonte
Ao escrever classes / funções para outras pessoas, parece difícil dizer quando as exceções são apropriadas. Há algumas partes úteis da BCL que eu tive que abandonar e usar o pinvoke porque elas lançam exceções em vez de retornar erros. Em alguns casos, você pode contornar esse problema, mas para outros, como System.Management and Performance Counters, existem usos em que você precisa executar loops nos quais as exceções são lançadas pela BCL com freqüência.
Se você estiver gravando uma biblioteca e houver uma possibilidade remota de que sua função possa ser usada em um loop e houver um potencial para uma grande quantidade de iterações, use o padrão Try .. ou alguma outra maneira de expor os erros, além das exceções. E mesmo assim, é difícil dizer quanto sua função será chamada se estiver sendo usada por muitos processos em ambiente compartilhado.
No meu próprio código, exceções são lançadas apenas quando as coisas são tão excepcionais que é necessário examinar o rastreamento de pilha e ver o que deu errado e corrigi-lo. Então, eu praticamente reescrevi partes do BCL para usar o tratamento de erros com base no padrão Try .. em vez de exceções.
fonte