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%).
    Cem milhões 
  • 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%).
    Um milhão
  • 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%).
    Cem mil
  • 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).
    Dez mil 

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:

  1. Exceptions and Performance e Exceptions and Performance Redux
  2. 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: ,

Comentários


Brazil Felipe
outubro 2. 2009 16:47
Felipe
Excelente post, claro e objetivo.
Sempre tinha discussão sobre isso por aqui, mas contra fatos, não há argumentos!
vlw!
[]'s

no site


outubro 2. 2009 17:11
Diogo Damiani
Ótimo post Giovanni! Realmente a diferença é gritante.

Fugindo um pouquinho do assunto, surgiu uma dúvida com relação ao código. Porque do método EngolirErro(...)? Pelo que entendi, ele só dispara uma ação para a atribuição da variável quantidadedeVezes. Não poderia simplesmente atribuir a variável diretamente? Alguma vantagem em fazer assim?

Valeu! Até!

http://www.diogodamiani.com.br/http://www.diogodamiani.com.br/


outubro 2. 2009 18:14
Giovanni Bassi
Diogo, acho que você está confundindo o método engolirerro e o método executar. O método engolir erro simplesmente executa a ação dentro de um try catch, e engole o erro. Fiz isso porque o usuário pode digitar um texto que não vai ser conversível pra int, e isso daria um erro, que eu estou ignorando. Baixa o código que tá bem tranquilo de entender.

http://unplugged.giggio.net/http://unplugged.giggio.net/


outubro 2. 2009 18:31
Diogo Damiani
Giovanni, realmente. Eu tinha baixado, mas olhei muito rapidamente. Perguntei sem analisar direito.
Entendi perfeitamente o que você fez! Smile

Abraço!

http://diogodamiani.com.br/http://diogodamiani.com.br/


Brazil Thiago Daher
outubro 6. 2009 18:16
Thiago Daher
Utilizar propriedades validadoras com certeza seria mais rápido. Afinal de contas, com a "propriedade validadora", estaríamos criando somente um bool.

Ao utilizar exceções temos várias outras informações...
O tipo da exceção, que por si só, já pode descrever o que ocorreu;
A mensagem da exceção, que nos dá uma descrição mais detalhada;
A pilha de métodos executados (StackTrace), que ajuda o programador a rastrear "onde" o erro ocorreu.
Isso sem falar nas propriedades de exceções específicas, que podem auxiliar de maneira mais direta ainda a localização e resolução dos problemas (SqlErrorNumber, em SqlException).

Com certeza, criar, serializar e trafegar um bit seria bem mais rápido do que esse volume monstruoso de informações, mas com certeza, bem menos útil na resolução de erros...

Tem casos em que precisamos de utilizar classes mais complexas mesmo. E exceções estão aí para serem utilizadas!! Acho que precisamos mesmo é de bom senso antes de taxar como "pouco performático"...

Essa é minha opinião...

Um abraço a todos.

Thiago Daher
Moderador do CSharp-Br do Yahoo

no site

Comentar


(Vai mostrar seu Gravatar)

  Country flag

biuquote
  • Comentário
  • Pré-visualização
Loading



Quem é Giovanni Bassi

Giovanni Bassi Sou uma pessoa apaixonada por tecnologia e especificamente por .Net. Sou consultor independente especialista em .Net, focado em arquitetura e melhores práticas. Tenho dezenas de artigos publicados na .Net Magazine, revista da qual sou editor técnico. Ministro palestras e cursos de vez em quando, e quando dá tempo eu respiro um pouco. Mais detalhes nesta página.

Busca

Selos

Eu vou ao TechEd Brasil 2010, e você?

MVP

MCPD

MCSD

.Net Magazine

Abaixo ao if!

Calendário

«  julho 2010  »
seteququsedo
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678
Ver detalhamento de posts no calendário

Blogs interessantes

    OPMLDownload OPML file

    Postagens recentes

    Comentários recentes

    Disclaimer / Aviso
    As opiniões colocadas neste blog são minhas e pessoais e não expressam necessariamente as opiniões de meus empregadores, pareceiros e amigos. Da mesma forma, os comentários feitos por leitores do blog não expressam a minha opinião.

    © Copyright 2010 .Net Unplugged
    Log in