O custo real das exceptions – 02 – Estamos olhando para o lugar certo?
Publicado em: quinta-feira, 20 de fev de 2025

Sem sombra de dúvidas, o principal argumento que sustenta a ideia de que exceptions são ruins é o custo. Afinal, ninguém quer colar em si uma placa dizendo que “optou pelo caminho mais lento“.

Ao mesmo tempo, as discussões acaloradas transformam o debate em uma rinha onde cada qual adota uma postura que desqualifica toda e qualquer opinião contrária.

Mas afinal, quanto custa uma exception?

Este artigo não pretende fornecer respostas definitivas, mas levantar questionamentos importantes:

Será que estamos ignorando alguma peça fundamental nessa discussão? A aversão às exceptions é justificada por dados concretos ou apenas uma tendência emergente sem embasamento empírico suficiente?

Uma análise superficial pode enganar?

O principal argumento contra exceptions está no seu suposto alto custo computacional. De fato, quando uma exceção é lançada, o runtime do .NET precisa capturar o estado da pilha, criar um objeto de exceção e executar o processo de unwinding da stack até encontrar um manipulador adequado. Sem dúvida, esse overhead é significativamente maior do que o de um simples if/else.

Benchmarks: Comparação com outras operações

Ao analisar benchmarks, observamos que lançar uma exceção é muitas vezes mais custoso do que adotar if’s. Esses comparativos estão na casa dos nanosegundos (ns) ou microssegundos (μs).

No entanto, quando comparado a operações triviais em sistemas modernos, esse impacto pode ser marginal:

  • Consultas a bancos de dados (~2-5ms);
  • Acessos a cache distribuído como Redis (~0.5-1ms);
  • Chamadas a APIs REST (~5-50ms, dependendo da rede e latência);
  • Processamento de mensagens assíncronas em filas distribuídas (~2-5ms, dependendo da carga do sistema).

Em todos os exemplos acima, optei por valores utópicos. No mundo real, dá para multiplicar esses números algumas vezes.

Dado esse cenário, será que o custo das exceptions realmente justifica evitá-las completamente?

  • A Microsoft, ao projetar o novo e moderno ASP.NET Core e o próprio .NET Runtime, não as removeu. Será que deveríamos?
  • Qual é o impacto real na execução das aplicações?

Talvez uma informação adicional possa nos ajudar a refletir sobre:

ValorEm SegundosEm Milisegundos
1 ns : 1 Nanosegundo0,000 000 001 seg0,000 001 ms
1 μs : 1 Microsegundo0,000 001 seg0,001 ms

Em vez de analisar exceptions apenas sob a ótica da performance comparando com o custo de um simples IF, precisamos contextualizar seu uso dentro do fluxo de execução da aplicação, entendendo quando seu custo é relevante e quando não é.

Por exemplo, no vídeo do JUL/2021 – Nick Chapsas – O custo oculto das exceções no .NET que referenciei no post anterior, temos:

  • Medição em nanosegundos | 1 ns = 0,000 001 ms

A ausência de latência foi algo que me chamou a atenção em todos os benchmarks. Afinal, qual a relação entre nanosegundo/microssegundo com milissegundos?

Para entender o impacto da latência na expressividade do custo das exceptions resolvi fazer testes adicionando latência. Assim, rodei o benchmark acima por 45h38m, rodando 120 benchmarks diferentes, por todo esse período.

Quando adicionamos 2 milissegundos à execução, temos números mais expressivos, números que fazem mais sentido à compreensão humana.

2 ns = 1 Nanosegundo = 0,000 000 001 seg = 0,000 001 ms

assim

2ms = 2’000’000 ns

Assim, podemos ter uma noção melhor de quanto custa uma exception em relação à execução de uma aplicação, que, por exemplo, acesse um banco de dados.

Será que estamos fazendo as perguntas certas?

A questão das exceptions não pode ser reduzida a um debate simples sobre custo, precisamos separar custo de valor.

Para isso, precisamos fazer perguntas mais profundas:

  • O custo computacional das exceptions é realmente significativo?
    Em quais cenários?
  • Até que ponto os benchmarks que desconsideram latência são confiáveis?
    Será que eles apresentam uma visão tendenciosa do problema?
  • Estamos realmente buscando otimização de milésimos ou centésimos de milissegundo enquanto ignoramos impactos na arquitetura do software?
  • Quais são os trade-offs reais entre código mais legível e pequenas otimizações de desempenho?

As críticas às exceptions geralmente nascem do uso inadequado. Como uma faca, que pode ser usada tanto para o bem quanto para o mal, seu mau uso pode gerar problemas reais.

Próximos passos

No primeiro post, você viu diversas referências em vídeos que defendem a abolição das exceptions. Neste post, você viu que há uma questão sobre esse argumento central do custo de exceptions, a escala.

A escala em nanossegundos ou microssegundos é injusta, visto que todas as nossas grandes operações acontecem na escala dos milissegundos.

Dizer que lançar exception custa 10 vezes, custa 30 vezes mais, pode ser um argumento verídico, entretanto perde força quando olhamos para a escala dos milissegundos.

Ao trocarmos de escala, a tortura estatística derrete.

Da mesma forma que grandes números são inimagináveis.

Pequenos números também são.

Por isso é tão importante trazer para a escala de milissegundos (ms) em vez de continuar em nanossegundos (ns) ou microssegundos (μs). Mas é preciso muito cuidado para não fazer parecer que não há custo. A questão é sobre qual ponto de referência estamos adotando. Se tomarmos como referência o custo de um if, a exceções são muito mais caras, mas se tomarmos como referência um request http, uma operação maior, esse custo quase desaparece, se torna imperceptível. Só reaparecendo diante de um cenário de múltiplas exceções sendo lançadas na mesma execução, mas aqui o problema não é a exception, mas o tratamento equivocado.

Para essa discussão evoluir, preciso realmente fazer considerações:

  • Para que lançamos exceptions
  • Quando lançamos exceptions
  • Onde lançamos exceptions, onde não lançamos.
  • Por que tratamos?
  • Onde não devemos tratar, devemos deixar estourar.
  • Como tratamos.
  • Onde tratamos.
  • O que é tratar, e o que fazer em cada contexto.

e todos os detalhes para assegurar que, ao apresentar o resultado do benchmark, não alimente a ideia de uso indiscriminado e ilimitado de exceptions.

Por isso, o próximo post falará sobre as boas práticas com exceptions.

O Cloud Native .NET é meu principal projeto.

Onde empenho energia para ajudar, acompanhar, direcionar Desenvolvedores, Líderes Técnicos e jovens Arquitetos na jornada Cloud Native.

Conduzo entregando a maior e mais completa stack de tecnologias do mercado.

Ao trabalhar com desenvolvedores experientes, eu consigo usar seu aprendizado com .NET, banco de dados, e arquitetura para encurtar a jornada.

Ao restringir à desenvolvedores .NET eu consigo usar do contexto de tecnologias e problemas do seu dia-a-dia, coisas que você conhece hoje, como WCF, WebForms, IIS e MVC, por exemplo, para mostrar a comparação entre o que você conhece e o que está sendo apresentado.

É assim que construímos fundamentos sólidos, digerindo a complexidade com didática, tornando o complexo, simples.

É assim que conseguimos tornar uma jornada densa, em um pacote de ~4 meses.

Eu não acredito que um desenvolvedor possa entender uma tecnologia sem compreender seus fundamentos. Ele no máximo consegue ser produtivo, mas isso não faz desse desenvolvedor um bom tomador de decisões técnicas.

É preciso entender os fundamentos para conseguir tomar boas decisões.

0 comentários

Enviar um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Este site utiliza o Akismet para reduzir spam. Saiba como seus dados em comentários são processados.


4x Microsoft MVP

Categorias

Assine

Luiz Carlos Faria

Mensagem do Autor

Espero que goste desse post. Não deixe de comentar e falar o que achou.

Se acha que esse post pode ajudar alguém que você conheça, compartilhe!

 

Lives

Fique de olho nas lives

Fique de olho nas lives no meu canal do Youtube, no Canal .NET e nos Grupos do Facebook e Instagram.

Aceleradores

Existem diversas formas de viabilizar o suporte ao teu projeto. Seja com os treinamentos, consultoria, mentorias em grupo.