Exceptions are expensive!


Hey! Don't we all use Exceptions in managed code to indicate the caller that something is wrong? That's a really bad habit! - "Hey maaaan, it's the 21st century, passing back HRESULTs is over!" - not yet, you'll see it: did you know that if your caller can systematically force your code to throw an exception (by passing wrong parameters, etc), your application could be DOSed down? It's just a matter of the amount of threads running on the defender's box and the power of the server your code running at. To demonstrate this thingy, I hammered together a small app:


The following is a function that can work in two different ways:



  1. Returning an exception, simulating a function that communicates back to its caller by throwing exceptions on deterministic inputs. MyOwnException is just a class inherited from the ApplicationException class

  2. Returning an integer number, simulating a function that works in an "old-fashioned" HRESULT-way 

static int TesterMethod(bool throwException)
{
    if (throwException)
    {
        throw new MyOwnException();
    }
    else
    {
        return 1;
    }
}



(HRESULT is representing an alternative way of passing back the result of a function as a variable or pointer, it's not neccessarily should be the old-fashioned HRESULT, it could be a structure or class containing information about what happened)


... and here's the function that will drive this baby: it's simple: there's a stopwatch we use to measure time, and we call the function in both ways 1,000 times:

static void Main(string[] args)
{
    Stopwatch stopWatch = Stopwatch.StartNew();
   
    // PHASE I: DOING THE EXCEPTION-TEST
   
    // Reset and start the stopwatch
    stopWatch.Reset();
    stopWatch.Start();
   
    // Call the function 1,000 times, an exception will be
    //  thrown at every call
    for (int i = 0; i < 1000; i++)
    {
        try
        {
            TesterMethod(true);
        }
        catch (MyOwnException)
        {
            // Do something to handle ...
        }
    }
   
    // Stop the watch and show the time elapsed
    stopWatch.Stop();
    Console.WriteLine("Exception: " + stopWatch.Elapsed);

   
    // PHASE II: DOING THE "HRESULT" TEST

    // Reset and start the stopwatch again
    stopWatch.Reset();
    stopWatch.Start();
   
    // Calling the function 1,000 times again, but now in
    //  the HRESULT-way
    int retVal = 0;
    for (int i = 0; i < 1000; i++)
    {
        retVal = TesterMethod(false);
    }
   
    // Stop the stopwatch and display the results
    stopWatch.Stop();
    Console.WriteLine("HRESULT " + stopWatch.Elapsed);



}


Surprise!

Exception: 00:00:03.1247094
HRESULT:   00:00:00.0000287


I hope that you noticed the seconds part, not only the milliseconds part! After some time, I was rethinking the whole thing, because it seemed to be too much of a difference, then I found that I made a mistake when measuring the performance: I was running the code from the Visual Studio IDE, meaning that I was debugging the process, which slows down 1st chance exceptions, so a big part of this difference was due to the operating system informing the debugger about the 1st chance exceptions. Release compiled the project, ran it outside the debugger and got a much better performance for exceptions, but still a huge difference:

Exception: 00:00:00.2039267
HRESULT:   00:00:00.0000058


This is all about performance and security. One can say that these are just milliseconds per one thousand cycles. That's true for one thread, but think about an ASP.net application or a service accepting huge amount of TCP connections, with 25 worker threads per processor. If there's a function inside that can be forced from outside to throw an exception, this could be a big performance impact on your service. So, think twice before throwing an exception.

Comments (9)
  1. eman says:

    Exactly what does that HRESULT mean? What is the call stack? There is a lot of useful information that can be included in exceptions. An HRESULT does not tell me anything useful. If I am lucky, I may be able to look it up.

    Regardless of the numbers, exceptions _when properly used_ are the best way to go. An exception should occur in exceptional circumstances. If they are occurring over and over multiple times, then there is a problem with the code and it needs to be fixed.

  2. dszabo says:

    Yes, that’s right! The 2 problems are (1) the way programmers use Exceptions sometimes, and (2) the way Windows handles exceptions that makes the whole process very slow

  3. Keith Farmer says:

    And that’s where we get such things as bool TryParse(int, out int).

    As eman points out, a blind HRESULT is fairly unrevealing.  Better, I think, to produce a result info struct or enum and return that.

    Exceptions are for the oh-my-god-something’s-wrong situation, not for branching logic that swallows without actually doing anything with the exception.

  4. Jack Bond says:

    So something exceptional happened 1000 times and it only cost you 3 seconds? Seems VERY acceptable to me.

  5. dszabo says:

    Jack Bond: it’s not neccessarily an exceptional thing all the time, especially when it can be reproduced just by passing a wrong parameter. Compare its cost with the other case!

  6. require name says:

    As with all things, exceptions can be misused. While such code does exist, it is very poor. A peer review of the code may identify the issue. If not, a profiler would be able to.

    I could scatter my code with many large loops that do absolutely nothing. Or I could allocate massively large arrays and never use them. However, that does not mean that loops or arrays are bad things. They are just not being used properly.

    As pointed out, an exception should be used for exceptional cases. Well written code should validate passed arguments. By catching the exception, you are declaring that you can handle it. If you are going to throw an exception and catch it and do absolutely nothing about it, then do not throw it in the first place. Finally, catching System.Exception is fairly poor style.

  7. Hi all – A security question for you all that I haven’t had time to think about in depth yet but wanted

  8. So something exceptional happened 1000 times and it only cost you 3 seconds? Seems VERY acceptable to me.

  9. dszabo says:

    you are right for a desktop application – but imagine it on http://www.hotmail.com – for every visitor. For the processors’, this will be Visitor times 3 seconds 🙂

    David

Comments are closed.

Skip to main content