È decisamente costoso

14

In caso di codice in cui è necessario eseguire una pulitura delle risorse prima di uscire da una funzione, c'è una notevole differenza di prestazioni tra questi 2 modi di farlo.

  1. Pulizia della risorsa prima di ogni dichiarazione di ritorno

    void func()
    {
        login();
    
        bool ret = dosomething();
        if(ret == false)
        {
            logout();
            return;
        }
    
        ret = dosomethingelse();
        if(ret == false)
        {
            logout();
            return;
        }
    
        dootherstuff();
    
        logout();
    }
    
  2. Pulizia della risorsa in un blocco finale

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

Ho fatto alcuni test di base in programmi di esempio e non sembra esserci molta differenza. Preferisco di gran lunga il modo finally di farlo, ma mi stavo chiedendo se causerà un qualsiasi impatto sulle prestazioni in un grande progetto.

    
posta user93353 04.09.2013 - 15:09
fonte

5 risposte

22

Come indicato in Quanto lente sono le eccezioni Java? si può vedere che la lentezza di try {} catch {} rientra nell'istanza dell'eccezione stessa.

La creazione di un'eccezione preleva l'intero stack di chiamate dal runtime e questo è dove si trova la spesa. Se non stai creando un'eccezione, questo è solo molto leggermente un aumento di tempo.

Nell'esempio fornito in questa domanda non ci sono eccezioni, non ci si aspetterebbe alcun rallentamento dalla loro creazione - non sono create. Invece, ciò che è qui è un try {} finally {} per gestire deallocazione delle risorse all'interno del blocco finally.

Quindi per rispondere alla domanda, no, non esiste una spesa reale in runtime all'interno di una struttura try {} finally {} che non usi eccezioni (non è inaudita, come visto). Ciò che è probabilmente costoso è il tempo di manutenzione quando si legge il codice e si vede questo stile di codice non tipico e il programmatore deve pensare che qualcosa di simile accade in questo metodo dopo il return prima di ritornare alla precedente chiamata.

Come è stato detto, la manutenzione è un argomento per entrambi modi per farlo. Per la cronaca, dopo la considerazione, la mia preferenza sarebbe l'approccio alla fine.

Considera il tempo di mantenimento di insegnare a qualcuno una nuova struttura linguistica. Vedere try {} finally {} non è qualcosa che si vede spesso nel codice Java e quindi può essere fonte di confusione per le persone. Esiste un certo grado di tempo di manutenzione per l'apprendimento di strutture un po 'più avanzate in Java rispetto a ciò che le persone hanno familiarità con il vedere.

Viene eseguito il blocco finally {} sempre . E questo è il motivo per cui dovresti usarlo. Considera anche il tempo di manutenzione del debug dell'approccio non-finale quando qualcuno dimentica di includere un logout al momento giusto, o lo chiama in un momento improprio, o dimentica di tornare / uscire dopo averlo chiamato in modo che venga chiamato due volte. Ci sono così tanti possibili bug con questo che l'uso di try {} finally {} rende impossibile avere.

Quando si pesa questi due costi, è molto costoso nel tempo di manutenzione non utilizzare l'approccio try {} finally {} . Mentre le persone possono dicker su quanti millisecondi frazionari o istruzioni jvm aggiuntive il blocco try {} finally {} viene confrontato con l'altra versione, è necessario considerare anche le ore trascorse nel debug il meno ideale del modo di affrontare la deallocazione delle risorse.

Scrivi prima il codice gestibile e preferibilmente in un modo che impedisca la scrittura di bug in seguito.

    
risposta data 04.09.2013 - 16:30
fonte
5

link

La risposta accettata su questa domanda mostra che il wrapping di una chiamata di funzione in un blocco try catch costa meno del 5% rispetto a una chiamata di funzione nuda. In effetti lanciando e catturando l'eccezione, il runtime ha generato un numero di mongolfiere superiore a 66 volte la chiamata a funzione nuda. Quindi, se ti aspetti che la progettazione delle eccezioni venga regolarmente eseguita, proverei ad evitarlo (nel codice critico delle prestazioni correttamente profilato), tuttavia se la situazione eccezionale è rara, allora non è un grosso problema.

    
risposta data 04.09.2013 - 15:49
fonte
4

Invece di ottimizzare, pensa a cosa sta facendo il tuo codice.

L'aggiunta del blocco finally è un'implementazione diversa: significa che si effettuerà il logout ad ogni eccezione.

In particolare: se il login genera un'eccezione, il comportamento nella seconda funzione sarà diverso dal primo.

Se il login e il logout non fanno parte del comportamento della funzione, suggerirei che il metodo dovrebbe fare una cosa e una cosa bene:

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

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

            dootherstuff();

    }

e dovrebbe essere in una funzione che è già incapsulata in un contesto che gestisce login / logout poiché questi sembrano fondamentalmente diversi da ciò che sta facendo il tuo codice principale.

Il tuo post è taggato come Java che non conosco molto bene ma in .net userei un blocco using per questo:

es .:

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

E il contesto di login ha un metodo Dispose che eseguirà il logout e ripulirà le risorse.

Modifica: in realtà non ho risposto alla domanda!

Non ho prove misurabili, ma non mi aspetto che questo sia più costoso o certamente non abbastanza costoso perché sia degno di una prematura ottimizzazione.

Lascerò il resto della mia risposta perché, sebbene non risponda alla domanda, penso che sia utile per l'OP.

    
risposta data 04.09.2013 - 15:20
fonte
1

Assunzione: stai sviluppando in codice C #.

La risposta rapida è che non ci sono risultati significativi nelle prestazioni dall'uso dei blocchi try / finally. C'è un calo di prestazioni quando si lancia e si rilevano eccezioni.

La risposta più lunga è vedere per te stesso. Se guardi il codice CIL sottostante generato ( ad esempio un riflettore per cancello rosso allora puoi vedere le istruzioni CIL sottostanti generano e vedono l'impatto in questo modo.

    
risposta data 04.09.2013 - 15:15
fonte
-2

Come altri già notato, il codice Ttese avrà risultati diversi, se dosomethingelse o dootherstuff generano un'eccezione.

E sì, la chiamata di funzione qualsiasi può generare un'eccezione, almeno una StackOVerflowError ! Sebbene sia raramente possibile gestirli correttamente (come ogni funzione di cleanup chiamata probabilmente avrà lo stesso problema, in alcuni casi (come ad esempio l'implementazione di blocchi, ad esempio) è fondamentale gestirla correttamente ...

    
risposta data 17.12.2014 - 09:07
fonte

Leggi altre domande sui tag