16

In case of code where you have to do a resource cleanup before exiting a function, is there a major performance difference between these 2 ways of doing it.

  1. Cleaning the resource before every return statement

    void func()
    {
        login();
    
        bool ret = dosomething();
        if(ret == false)
        {
            logout();
            return;
        }
    
        ret = dosomethingelse();
        if(ret == false)
        {
            logout();
            return;
        }
    
        dootherstuff();
    
        logout();
    }
    
  2. Cleaning the resource in a finally block

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

I did some basic tests in sample programs and there doesn't seem to be much of a difference. I so much prefer the finally way of doing this - but I was wondering if it will cause any performance hit in a big project.

user93353
  • 431

5 Answers5

26

As indicated in How slow are Java exceptions? one can see that the slowness of try {} catch {} is within the instatiation of the exception itself.

Creating an exception will fetch the entire call stack from the runtime and this is where the expense is. If you are not creating an exception, this is only very slightly an increase of time.

In the example given in this question there aren't any exceptions, one wouldn't expect any slowdown from creating them - they aren't created. Instead, what is here is a try {} finally {} to handle resource deallocation within the finally block.

So to answer the question, no, there is no real runtime expense within a try {} finally {} structure that doesn't use exceptions (it isn't unheard of, as seen). What is possibly expensive is the maintenance time when one reads the code and sees this not typical code style and the coder has to get their mind around that something else happens in this method after the return before returning to the previous call.


As has been mentioned, maintenance is an argument for both ways of doing this. For the record, after consideration, my preference would be the finally approach.

Consider the maintenance time of teaching someone a new language structure. Seeing try {} finally {} isn't something that one often sees in Java code and thus can be confusing to people. There is a degree of maintenance time for learning a bit more advanced structures in Java than what people are familiar with seeing.

The finally {} block always runs. And this is why you should use it. Consider also the maintenance time of debugging the non-finally approach when someone forgets to include a logout at the proper time, or calls it at the improper time, or forgets to return / exit after calling it so that it is called twice. There are so many possible bugs with this that the use of try {} finally {} makes impossible to have.

When weighing these two costs, it is expensive in maintenance time not to use the try {} finally {} approach. While people can dicker about how many fractional milliseconds or additional jvm instructions the try {} finally {} block is compared to the other version, one must also consider the hours spent in debugging the less than ideal way of addressing resource deallocation.

Write maintainable code first, and preferably in a way that will prevent bugs from being written later.

5

https://stackoverflow.com/questions/299068/how-slow-are-java-exceptions

The accepted answer on this question shows wrapping a function call in a try catch block costs less than 5% over a bare function call. Actually throwing and catching the exception caused runtime to balloon to more than 66x the bare function call. So if you expect the exception design to throw regularly then I would try to avoid it (in properly profiled performance critical code), however if the exceptional situation is rare then it isn't a big deal.

stonemetal
  • 3,381
4

Instead of optimising - think of what your code is doing.

Adding the finally block is a different implementation - it means that you will logout on every exception.

Specifically - if login throws an exception, the behaviour in your second function will be different than your first.

If login and logout are not actually part of the function behaviour then I would suggest that the method should do one thing and one thing well:

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

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

            dootherstuff();

    }

and should be in a function which is already encapsulated in a context which manages login / logout since these seem fundamentally different from what your main code is doing.

Your post is tagged as Java which I'm not super familiar with but in .net I would use a using block for this:

ie:

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

And the login context has a Dispose method which will logout and clean up the resources.

Edit: I didn't actually answer the question!

I have no measurable evidence but I would not expect this to be any more expensive or certainly not significantly enough expensive for it to be worthy of premature optimisation.

I'm going to leave the rest of my answer because although not answering the question, I think it is helpful for the OP.

Michael
  • 2,959
  • 21
  • 13
1

Assumption: You are developing in C# code.

The quick answer is that there is no significant performance hit from using try/finally blocks. There is a performance hit when you throw and catch exceptions.

The longer answer is to see for your self. If you look at the underlying CIL code generated (e.g red-gate reflector then you can see the underlying CIL instructions generated and see the impact that way.

Michael Shaw
  • 10,114
-2

As others already noted, Ttese code will have different results, if either dosomethingelse or dootherstuff throw an exception.

And yes, any function call can throw an exception, at least a StackOVerflowError! While it is rarly possible to handle these correctly (as any called cleanup function will probably have the same issue, in some cases (such as implementing locks for example) it is crucial to even handle that correctly...