0

I have an App where I use a lot of Exceptions.

Recently I heard that Exceptions cost a lot in terms of performance.

My App looks like this:

Try
{
    Function1();
    Function2();
    Function3();
    Function4();
}

catch(Execption ex)
{
    //I return to the user the error (ex) that happen inside the 
    //function that fails
}

Each of my functions 1,2,3 etc look similar to this..

Void Function1()
{
    //Do something...
    //If something fail
    Throw New Exception("Fails because bla bla");

    //Do something...
    //If something fail
    Throw New Exception("Fails because bla bla bla bla");

    //If everything its ok, just continue without throwing anything
}

Is this bad design?
Will this hurt my App performance?

André Snede
  • 9,899
  • 7
  • 43
  • 67
Ricardo Polo Jaramillo
  • 12,110
  • 13
  • 58
  • 83
  • 3
    Don't use exceptions to control program flow. – Mitch Wheat Jan 26 '14 at 23:26
  • The clue is in the word 'Exception'. This is information you can easily find online and at Microsoft!! – Mitch Wheat Jan 26 '14 at 23:27
  • @RicardoPolo Exceptions, are for Exceptional cases, not for ordinary program flow. – André Snede Jan 26 '14 at 23:27
  • What is the probability that some function will fail and throw one of the exceptions? If it's under 2% (arbitrary guestimate) then exceptions are fine. If it's more then exceptions are maybe wrong. – Dialecticus Jan 26 '14 at 23:27
  • http://stackoverflow.com/questions/2737328/why-should-i-not-wrap-every-block-in-try-catch/2737337#2737337 – Mitch Wheat Jan 26 '14 at 23:31
  • Maybe think of it this way: Throwing an exception uses 1kb of RAM and takes 1ms for processing. (Just making up some numbers here.) - If you use these exceptions and they have a noticable impact on your application's performance, you are using exceptions the wrong way. – JimmyB Jan 26 '14 at 23:34
  • http://msdn.microsoft.com/en-us/library/ms229030(v=vs.110).aspx – Mitch Wheat Jan 26 '14 at 23:55

4 Answers4

1

When exceptions are thrown they rebuilt the stack trace among other things. This is pretty costly. If you can, try to prevent a known error condition like this (here C-style):

public static const int INVERSE_DIVISION_BY_ZERO = -1; // any value not possible on correct operation will do

public int Inverse(int a)
{
    if (a != 0)
    {
        return 1 / a;
    }

    return INVERSE_DIVISION_BY_ZERO;
}

Now, this example is ugly I know. It doesn't help much.

In fact from testing first:

if (a != 0)
{
    b = Inverse(a);
}

It is now a matter of testing later:

b = Inverse(a);
if (b != Inverse.INVERSE_DIVISION_BY_ZERO)
{
    // safe to go
}

Does this make sense? No. The example is quite poor. But it shows that there is a minimum amount of testing to do to keep code execution safe. If it's not done before, it has to be done later, but can't really be avoided.

Exceptions should be thrown for error conditions that are exceptional in a sense that you can't control them like no CD in drive and similar. When you already know how the code fails, prevent it!

I want to add this:

Exception handling is a complex design problem not just an implementation problem. Their is a trade-off to take care off, between these two extremes:

  • clean, readable code that throws exceptions on occasion;
  • efficient code that is difficult to read but prevents exceptions and returns C-style error codes that need interpretation.

The common C# practice is to favor clean, readable code over premature optimization, and I share this vision.

But this does not mean that Exceptions should be deliberately thrown. There are cases where intentional throwing makes perfect sense, for example:

enum Gender
{
    Unspecified,
    Male,
    Female
}

// later in the code

switch (gender)
{
    case Gender.Unspecified:
        // handle
        break;
    case Gender.Male:
        // handle
        break;
    case Gender.Female:
        // handle
        break;
    default:
        throw new ArgumentException(string.Format("Unrecognized gender (was {0})", (int)gender));
}

The undefined gender can't be rendered as a string because it is not defined. It will be rendered as an int value, instead.

Now this example is in my opinion clean readable code that is also robust when the Gender enum is modified in future times. At least it will tell the developer that he forgot something...

Another example is this:

// A: high performance, let it throw
for (i = 0; i < values.length; i++)
{
    values[i] = 1 / values[i];
}

// B: slow performance, test first
for (i = 0; i < values.length; i++)
{
    if (values[i] != 0)
    {
        values[i] = 1 / values[i];
    }
}

When values[i] is 0 A will fail while B will ignore that spot in the array. Now I wonder about this:

  • is the array useful with unhandled spots? maybe it should be thrown away at that point;
  • is it really more performant to test instead of throw an exception?
  • what if 0 is so seldom it happens once every 1000 years (really)? That's like saying never, but based on probabilistic estimations.
  • maybe testing in each cycle for something that most probably will not happen is a waste...

If there is solid data to show that error conditions are extremely rare they should be handled with exceptions, because testing will cost much more on the average.

This doesn't mean you don't handle the exceptional state, it just means you handle it in a non-efficient way because it's rare and testing beforehand is costly.

EDIT: added error condition

EDIT 2: added some more thoughts

Community
  • 1
  • 1
pid
  • 11,472
  • 6
  • 34
  • 63
0

You should use return; when you want to stop the execution of a method at some point.

Exceptions and exception catch are used when you want to localize also the reason why something went wrong with a piece of code. By finding out what type of exception occured, you can take appropiate measures.

VasileF
  • 2,856
  • 2
  • 23
  • 36
0

No, throwing exceptions upon -well- exceptional states is good design.

On the other side I have seen code that uses exceptions excessively to basically communicate function results back to the caller. This is bad design, which can easily be seen by the fact that those exceptions are not thrown in exceptional states but rather during regular operation and at a high frequency.

JimmyB
  • 12,101
  • 2
  • 28
  • 44
0

Exceptions are for Exceptional cases.

If you for example want to validate an email address, you write an Email validator.

public bool Validate(string emailAddr)
{
    // Logic
    return false; // validation failed.
}

Then in your main logic you can write:

if(Validate(emailInpunt))
{
    // Do stuff.

}

This way your code becomes very readable.

If you use exceptions it would look like:

try
{
    Validate(emailInpunt);
}
catch
{
     // Something
}

Already in the above example, the code is harder to read, and it will be a bit harder to control the flow, if you want to add different cases, fx. Validate Email, OR check some value that says it should ignore it.

Also, nesting tons of try catches, is harder to read.

try
{
    Validate(emailInpunt);

    try
    {
        DoSomething(otherStuff);
    }
    catch
    {
         // Something
    }

}
catch
{
     // Something
}

And as you mentioned, Exceptions are expensive, because it builds a giant stacktrace, and generally halts the entire program.

Example of proper Exception use

try
{
    SqlConnection con = new SqlConnection(....);
    // Do stuff with the database connection

}
catch(SqlException sex)
{
     // Log this
     throw new UserSomethingException("A connection to the database could not be established");
}

Exceptions as Assertions
Exceptions are also used to tell the developer about a programming error.

Consider this:

public double Calculate(MeaningOfLifeTheUniverseAndEverythingQuestion bigQuestion)
{
     if(bigQuestion == null)
         throw new ArgumentException("MeaningOfLifeTheUniverseAndEverythingQuestion cannot be null!!");

     //  7½ million years of computing
     return 42;
}

In this case, we throw an error, letting the programmer know he has made an error in his code.

André Snede
  • 9,899
  • 7
  • 43
  • 67