Está rolando uma discussão no .Net Architects sobre o custo das exceções. Eu sempre soube que elas eram muito caras. O Philip Calçado colocava que não lançar exceções por questões de performance é otimização prematura. Bom, fui checar.
Fiz um código em que criava uma entidade inválida. Em um, a entidade era criada inválida e um valor de status de invalidação era configurado. No outro a entidade não foi criada, e uma exceção foi lançada no construtor.
Vejam as entidades:
class ComExcecao
{
public ComExcecao(bool algumParam)
{
throw new Exception("erro");
}
}
class SemExcecao
{
public SemExcecao(bool algumParam)
{
EhValida = false;
}
public bool EhValida { get; set; }
}
Aqui está o código para testar isso:
static void Main(string[] args)
{
var quantidadeDeVezes = 100000;
Console.WriteLine("Quantas vezes você quer rodar? Padrão == {0}", quantidadeDeVezes);
var vezesInputada = Console.ReadLine();
EngolirErro(()=>quantidadeDeVezes = Convert.ToInt32(vezesInputada));
Console.WriteLine("Rodando {0} iterações...", quantidadeDeVezes);
var totalSemExcecao =
Executar(() =>
{
for (int i = 0; i < quantidadeDeVezes; i++)
{
bool EhValida;
var classe = new SemExcecao(true);
EhValida = classe.EhValida;
}
});
Console.WriteLine("Total sem exceção: {0} milisegundos", totalSemExcecao);
var totalComExcecao =
Executar(() =>
{
for (int i = 0; i < quantidadeDeVezes; i++)
{
bool EhValida;
try
{
new ComExcecao(true);
EhValida = true;
}
catch (Exception)
{
EhValida = false;
}
}
});
Console.WriteLine("Total com exceção: {0} milisegundos", totalComExcecao);
var diferenca = Math.Abs(totalComExcecao - totalSemExcecao);
var maisRapido = totalComExcecao > totalSemExcecao ? "sem exceção" : "com exceção";
var percentualMaisRapido = ObterPercentualMaior(totalComExcecao, totalSemExcecao);
Console.WriteLine("Código {0} é mais rápido em {1} milisegundos ({2}).",
maisRapido,
diferenca,
percentualMaisRapido);
Console.ReadLine();
}
Estou ocultando as funções de auxílio para não poluir (no final eu mostro).
Resultados (código compilado como release, cliquem nas figuras para ampliar):
- Dez milhões de iterações
Total sem exceção: 112 milisegundos (um décimo de um segundo)
Total com exceção: 256.310 milisegundos (256 segundos, ou quase 5 minutos)
Código sem exceção é mais rápido em 256.198 milisegundos (2288.48%).
- Um milhão de iterações:
Total sem exceção: 9 milisegundos
Total com exceção: 25.286 milisegundos
Código sem exceção é mais rápido em 25.277 milisegundos (2809.56%).
- Cem mil iterações:
Total sem exceção: 2 milisegundos
Total com exceção: 2.497 milisegundos
Código sem exceção é mais rápido em 2.495 milisegundos (1248.50%).
- Dez mil iterações:
Total sem exceção: 0 milisegundos
Total com exceção: 252 milisegundos
Código sem exceção é mais rápido em 252 milisegundos (Menor valor == 0).
Minha avaliação dos resultados é que não dá mesmo pra colocar exceções onde código de negócio devia estar ocorrendo. Como eu já havia dito aqui antes, exceções são coisas que não são esperadas (daí o nome “exceção”), como erros, quedas de conexão, etc. Se a magnitude da diferença fosse de 200% até conseguiria entender isso como otimização prematura. Mas uma diferença de quase 3 mil porcento significa que em vez de um servidor, vou ter que colocar 30 pra fazer a mesma coisa. Não faz o menor sentido em praticamente nenhum cenário.
Mais algumas fontes:
- Exceptions and Performance e Exceptions and Performance Redux
- The True Cost of .NET Exceptions e The True Cost of .NET Exceptions -- Solution
Pra quem ficou curioso, segue o código que faz a avaliação do tempo, ele usa uma classezinha pouco utilizada do Framework, a System.Diagnostics.StopWatch:
public static double Executar(Action acao)
{
var stopwatch = new Stopwatch();
stopwatch.Start();
acao();
stopwatch.Stop();
return stopwatch.ElapsedMilliseconds;
}
O código, para quem quiser refazer os testes, está aqui.
Postado na(s) categoria(s)
Polêmicas
pelo
Giovanni Bassi em 2 de outubro de 2009 às 15:09
| Tags:
polêmica,
exception
cb1d3219-36e8-4480-a670-69947c54f0a3|0|.0