views:

536

answers:

10

Is it possible to change this code, with a return value and an exception:

public Foo Bar(Bar b)
{
   if(b.Success)
   {
      return b;
   }
   else
   {
      throw n.Exception;
   }
}

to this, which throws separate exceptions for success and failure

public Foo Bar(Bar b)
{
   throw b.Success ? new BarException(b) : new FooException();
}

try
{
   Bar(b)
}
catch(BarException bex)
{
   return ex.Bar;
}
catch(FooException fex)
{
   Console.WriteLine(fex.Message);
}
+10  A: 

Throwing an exception is definitely more expensive than returning a value. But in terms of raw cost it's hard to say how much more expensive an exception is.

When deciding on a return value vs. an exception you should always consider the following rule.

Only use exceptions for exceptional circumstances

They shouldn't ever be used for general control flow.

JaredPar
There are times when exceptions make sense for general control flow - control flow for which the language has fewer alternatives. For example, consider a recursive descent parser which is investigating a parse path speculatively - when it gives up, it'll want to return from a very deep call tree, and the easiest way to do that without laborious design of every routine is to throw an exception.
Barry Kelly
I dont agree with "Only use exceptions for exceptional circumstances" That rule is far to vague and subjective to be effective.Throwing a "InvalidParameterException" is hardly an exceptional circumstance.
Alan
@Alan: Passing an invalid argument to a method *should* be an exceptional circumstance.
Jon Skeet
I'd say that everything that prevents a program from continuing normally is an exceptional circumstance.
JulianR
+1  A: 

In some recent real work performance analysis, we found that tons of exceptions on lower end machines had a critical effect on the application performance, so much so that we are devoting a few weeks to go through and adjust the code to not throw so many.

When I say a critical effect, it was in the ballpark of spiking the dual core CPU up to 95% on the single CPU core the application was using.

Tom Anderson
What is the negative vote for?
Tom Anderson
+2  A: 

I'm having trouble at the moment finding any documents to support it, but keep in mind that when you throw an exception, C# has to generate a stack trace from the point where you called it. Stack traces (and reflection in general) are far from free.

Mark Rushakoff
Technically gathering the stack trace is not that expensive. It certainly costs something if data from high up the stack (stack growing down) needs to be pulled into the cache to grab each pushed return address, but that's the only cost that needs to be paid until someone actually asks for the stack trace data. You can lazily do the reflection, and never pay for it if no-one asks.
Barry Kelly
+1  A: 

You should prefer Exceptions over error codes, and for error conditions, but don't use exceptions for normal program flow.

Exceptions are extremely heavy duty compared to normal work flow, I've seen a huge order of magnitude decrease in application performance using a try-catch-block.

Alan
+2  A: 

Using error returns will be much more expensive than exceptions - as soon as a piece of code forgets to check the error return, or fails to propagate it.

Still, be sure to not use exceptions for control flow - use them only to indicate that something has gone wrong.

John Saunders
+3  A: 

You can find a lot of useful information about this in the answers to this question, including one answer with 45 up-votes How slow are .net exceptions?

DOK
Which appears to be incorrect...
Hmmm, which part of my answer is incorrect, angryboy? The URL, the answers to that question, the fact that Jon had 45 up-votes (now 49), or my assertion that there is a "lot of useful information" there?
DOK
I did not say that any part of you answer was incorrect DOK. If I had intended that, then I would have said "*this* is incorrect". Rather, I was responding to your reference: "...including one answer with 45 up-votes", with: "Which appears to be incorrect.", as in, the content of the answer that you are referencing appears to be incorrect.
RBarryYoung
Thanks for the clarification. It would be really helpful if you amplified on that in an answer to the other question. If Jon's high-scoring answer is incorrect, you would be doing everyone a real service to explain there how it is incorrect (beyond your brief comment there). This is such an important issue, and you obviously have in-depth experience with it.
DOK
+3  A: 

Exception have two costs: warm-up to page in the exception infrastructure - if not in to memory then into the CPU cache - and per-throw cost to gather exception stack, search for exception handler, possibly call exception filters, unwind the stack, calling finalization blocks - all operations that the runtime, by design, does not optimize for.

Thus, measuring the cost of throwing exceptions can be misleading. If you write a loop that iteratively throws and catches an exception, without a lot of work between the throw site and the catch site, the cost won't look that large. However, that's because it's amortizing the warm-up cost of exceptions, and that cost is harder to measure.

Certainly, exceptions don't cost anything like they seem to if one's main experience is exceptions thrown by programs under the debugger. But they do cost, and it's advisable to design libraries in particular such that exceptions can be avoided where necessary for optimization.

Barry Kelly
It's the catching that incurs the cost to gather the stack, search for handlers, etc. not the throwing.
Scott Dorman
Gathering the stack can be relatively trivially made quite cheap. Usually only a few dozen words need be copied from memory that's in a contiguous block almost never more than 1MB in size. The search for handlers is similarly cheap, but calling filter routines can cause pulling in lots of other code, and if the exception dispatching and stack unwinding logic isn't optimized for frequent exception throwing, setting up the stack frame etc. for each step of the whole operation can add up.
Barry Kelly
@Scott: I don't see that it makes any point to differentiate between throwing and catching. They're inextricably linked - you can't have one without the other, so it makes no difference what you call it.
Jon Skeet
+1  A: 

Throwing an exception is a relatively inexpensive operation. It's catching them that incurrs the cost because of the stack walks that must occur to find the catch handlers, execute the code in the catch handlers, find the finally blocks, execute the code in the finally blocks, and then return to the original caller.

It is strongly recommended that you don't use exceptions for control flow. Using return codes to indicate errors gets expensive from a "time and materials" perspective as it will eventually incur maintenance costs.

All of that aside, your two examples don't match nor are they even valid. Since you are return b, which is of type Bar, your first method should probably be:

public Bar Foo(Bar b)
{
   if(b.Success)
   {
      return b;
   }
   else
   {
      throw n.Exception;
   }
}

which could be rewritten as:

public Bar Foo(Bar b)
{
   if (!b.Success)
      throw n.Exception;

   return b;
}
Scott Dorman
+1  A: 

Generally I've avoided this because of the expensive nature of catching the exception. It may not be too bad if it's not something that happens often.

However, why not just return null? Then it becomes this:

public Foo Bar(Bar b)
{
   if(b.Success)
   {
      return b;
   }
   else
   {
      return null;
   }
}

And then whenever you call Bar() you just check to ensure that the returned value is not null before using the value. This is a far less expensive operation. And I figure it's a good practice because this is the technique Microsoft has used all over the place in many of .NET's built-in functions.

Steve Wortham
This is a very bad practice, because of the chance someone will forget to check for null. It also litters the code with checks for something that's meant to happen rarely, obscuring what the code is actually meant to do.
John Saunders
"This is a very bad practice, because of the chance someone will forget to check for null." - Couldn't the same be said for throwing an exception? If you throw an exception you need to remember to catch it.
Steve Wortham
I guess my point is that in either case this will come back to bite you if you don't write the code that surrounds the function call properly. The difference is that my method is much, much faster than throwing and catching an exception.
Steve Wortham
+6  A: 

Using the code below, testing revealed that the the call+return with no exceptions took about 1.6 microseconds per iteration, whereas exceptions (throw plus catch) added about 4000 microseconds each.(!)

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        DateTime start = DateTime.Now;
        bool PreCheck = chkPrecheck.Checked;
        bool NoThrow = chkNoThrow.Checked;
        int divisor = (chkZero.Checked ? 0 : 1);
        int Iterations =  Convert.ToInt32(txtIterations.Text);
        int i = 0;
        ExceptionTest x = new ExceptionTest();
        int r = -2;
        int stat = 0;

        for(i=0; i < Iterations; i++)
        {
            try
            {
                r = x.TryDivide(divisor, PreCheck, NoThrow);
            }
            catch
            {
                stat = -3;
            }

        }

        DateTime stop = DateTime.Now;
        TimeSpan elapsed = stop - start;
        txtTime.Text = elapsed.TotalMilliseconds.ToString();

        txtReturn.Text = r.ToString();
        txtStatus.Text = stat.ToString();

    }
}



class ExceptionTest
{
    public int TryDivide(int quotient, bool precheck, bool nothrow)
    {
        if (precheck)
        {
            if (quotient == 0)
            {
                if (nothrow)
                {
                    return -9;
                }
                else
                {
                    throw new DivideByZeroException();
                }

            }
        }
        else
        {
            try
            {
                int a;
                a = 1 / quotient;
                return a;
            }
            catch
            {
                if (nothrow)
                {
                    return -9;
                }
                else
                {
                    throw;
                }
            }
        }
        return -1;
    }
}

So yes, Exceptions are VERY expensive.

And before someone says it, YES, I tested this in Release mode and not just Debug mode. Try the code yourself and see if you get significantly different results.

RBarryYoung
+1: for actually testing it instead of just pontificating. Why do programmers argue about stupid stuff that they could just measure? Makes me angry, grrrr...
Thanks for posting these results. I knew it'd be a big difference. But that's a REALLY big difference. Good stuff.
Steve Wortham