Tenta finalmente caro

14

Em caso de código onde você tem que fazer uma limpeza de recursos antes de sair de uma função, existe uma grande diferença de desempenho entre essas duas maneiras de fazê-lo.

  1. Limpar o recurso antes de cada declaração de retorno

    void func()
    {
        login();
    
        bool ret = dosomething();
        if(ret == false)
        {
            logout();
            return;
        }
    
        ret = dosomethingelse();
        if(ret == false)
        {
            logout();
            return;
        }
    
        dootherstuff();
    
        logout();
    }
    
  2. Limpar o recurso em um bloco finally

    void func()
    {
        login();
    
        try
        {
            bool ret = dosomething();
            if(ret == false)
                return;
    
            ret = dosomethingelse();
            if(ret == false)
                return;
    
            dootherstuff();
    
        }
        finally
        {
            logout();
        }
    }
    

Eu fiz alguns testes básicos em programas de amostra e não parece haver muita diferença. Eu prefiro muito mais a maneira finally de fazer isso - mas eu queria saber se isso causará algum impacto no desempenho de um grande projeto.

    
por user93353 04.09.2013 / 15:09
fonte

5 respostas

22

Como indicado em Quão lentas são as exceções Java? pode-se ver que a lentidão de try {} catch {} está dentro da instação da própria exceção.

Criar uma exceção irá buscar toda a pilha de chamadas do tempo de execução e é aí que está a despesa. Se você não está criando uma exceção, isso é apenas muito ligeiramente um aumento de tempo.

No exemplo dado nesta questão, não há exceções, não se esperaria nenhuma lentidão de criá-las - elas não são criadas. Em vez disso, o que está aqui é um try {} finally {} para lidar com a desalocação de recursos no bloco finally.

Então, para responder a pergunta, não, não há despesa real em tempo de execução dentro de uma estrutura try {} finally {} que não use exceções (não é inédito, como visto). O que é possivelmente caro é o tempo de manutenção quando alguém lê o código e vê este estilo de código não típico e o codificador tem que pensar que algo mais acontece nesse método após o return antes de retornar para a chamada anterior.

Como já foi mencionado, a manutenção é um argumento para ambas maneiras de fazer isso. Para o registro, após a consideração, minha preferência seria a abordagem final.

Considere o tempo de manutenção de ensinar a alguém uma nova estrutura de linguagem. Ver try {} finally {} não é algo que muitas vezes se vê no código Java e, portanto, pode ser confuso para as pessoas. Há um certo tempo de manutenção para aprender estruturas um pouco mais avançadas em Java do que as pessoas estão acostumadas a ver.

O finally {} block sempre é executado. E é por isso que você deveria usá-lo. Considere também o tempo de manutenção da depuração da abordagem não-final quando alguém esquece de incluir um logout no momento adequado ou o chama no momento inadequado ou esquece de retornar / sair após chamá-lo para que seja chamado duas vezes. Existem tantos bugs possíveis com isso que o uso de try {} finally {} torna impossível ter.

Ao pesar esses dois custos, é caro no tempo de manutenção não usar a abordagem try {} finally {} . Embora as pessoas possam hesitar sobre quantos milissegundos fracionários ou instruções adicionais de jvm o bloco try {} finally {} é comparado à outra versão, também é necessário considerar as horas gastas na depuração da maneira menos ideal de abordar a desalocação de recursos.

Escreva primeiro o código de manutenção e, de preferência, de uma forma que impeça que os erros sejam escritos mais tarde.

    
por 04.09.2013 / 16:30
fonte
5

link

A resposta aceita nesta questão mostra que a quebra de uma chamada de função em um bloco try catch custa menos de 5% em uma chamada de função vazia. Na verdade, jogar e capturar a exceção fazia com que o tempo de execução aumentasse para mais de 66x a chamada de função nua. Portanto, se você espera que o design de exceção seja exibido regularmente, tentarei evitá-lo (no código crítico de desempenho com perfil adequado), no entanto, se a situação excepcional for rara, isso não será grande coisa.

    
por 04.09.2013 / 15:49
fonte
4

Em vez de otimizar, pense no que seu código está fazendo.

Adicionar o bloco finally é uma implementação diferente - significa que você fará logout em todas as exceções.

Especificamente - se o login gerar uma exceção, o comportamento da segunda função será diferente do primeiro.

Se o login e logout não são realmente parte do comportamento da função, então eu sugiro que o método faça uma coisa e uma coisa bem:

    void func()
    {
            bool ret = dosomething();
            if(ret == false)
                return;

            ret = dosomethingelse();
            if(ret == false)
                return;

            dootherstuff();

    }

e deve estar em uma função que já está encapsulada em um contexto que gerencia login / logout, uma vez que estes parecem fundamentalmente diferentes do que seu código principal está fazendo.

Sua postagem é marcada como Java, com a qual eu não estou muito familiarizado, mas em .net, eu usaria um bloco de uso para isso:

ou seja:

    using(var loginContext = CreateLoginContext()) 
    {
            // Do all your stuff
    }

E o contexto de login tem um método Dispose que faz o logout e limpa os recursos.

Editar: na verdade, não respondi a pergunta!

Eu não tenho provas mensuráveis, mas eu não esperaria que isso fosse mais caro ou, certamente, não seria suficientemente caro para ser digno de otimização prematura.

Vou deixar o resto da minha resposta porque, apesar de não responder à pergunta, acho que é útil para o OP.

    
por 04.09.2013 / 15:20
fonte
1

Suposição: você está desenvolvendo em código C #.

A resposta rápida é que não há nenhum impacto significativo no desempenho usando blocos try / finally. Há um impacto no desempenho quando você joga e captura exceções.

A resposta mais longa é ver por si mesmo. Se você olhar para o código CIL gerado ( por exemplo, refletor de porta vermelha , então você pode ver as instruções CIL subjacentes geradas e ver o impacto dessa maneira.

    
por 04.09.2013 / 15:15
fonte
-2

Como outros já observaram, o código Ttese terá resultados diferentes, se dosomethingelse ou dootherstuff derem uma exceção.

E sim, qualquer chamada de função pode gerar uma exceção, pelo menos um StackOVerflowError ! Embora seja extremamente possível manipulá-los corretamente (como qualquer função de limpeza chamada provavelmente terá o mesmo problema, em alguns casos (como a implementação de bloqueios, por exemplo), é crucial até mesmo lidar com isso corretamente ...

    
por 17.12.2014 / 09:07
fonte