.NET Offers a “First Chance” to Squelch Performance-killing Hidden Exceptions

rocessing exceptions in any language is expensive. The process of capturing an exception and rolling it into a package that can be processed by code requires a stack walk?basically looking back through the history of what has happened?and that requires a lot of processing time to complete. In .NET software development we sometimes talk about boxing and unboxing of variables and how that can consume a great deal of time in a program?and it can. However, the amount of time necessary to process an exception dwarfs the time necessary to box and unbox a variable. (If you want to know more about boxing and unboxing you can read about it in the article “Boxing and Unboxing of Value Types in C#” on CodeGuru.)

Despite the fact that exceptions are expensive to process they are positively a great thing for debugging applications and making software more reliable. The challenge is when exceptions are used incorrectly; specifically, when they are used to respond to normal conditions instead of exceptional conditions. Generally when this happens the layers of software above the layer generating the exception start “eating” the exception?because nothing is wrong (more on eating exceptions in a moment)?and manually return to normal program flow. This, however, leads to subtle performance issues that are difficult to find. Running a profiler on the code can show where performance is being impacted, however, there’s a quicker and simpler way to find performance problems like these?or rule out exceptions as a potential cause of performance issues.

Using first chance exceptions in the debugger is a way to ferret out these hidden exceptions that your application is generating so that you can go back and prevent them from happening in the first place. The more exceptions you prevent, the faster your application will run. First chance exceptions are exposed via Visual Studio and can be leveraged in any language that compiles to the CLR.

The starting point for learning how to leverage first chance exceptions is to expose a situation where hidden exceptions are causing a performance problem.

Eating Exceptions
Eating an exception?that is, not doing anything special when an exception is raised?is easy to do. This is the most general case of a hidden exception. The code around the code generating the exception simply doesn’t do anything with it. It ignores the exception by working around the inherent problem. Although other types of hidden exceptions exist, they are really just complicated forms of the following basic pattern. A quick try/catch block can eat any exception, as shown in the code below:

try{     int dim2Loop=0;     while (true)     {          sum += dblArray[dim1Loop, dim2Loop];     dim2Loop++;     }}catch (Exception e){     // Trace.WriteLine(e.ToString());}

The code has a try catch block but does nothing with it?except in this case to terminate the inner loop. This loop runs substantially slower because of the exception, out of bounds, that is thrown each time the code is ready to exit.

Anytime there is a catch without any code inside the catch block, the application has eaten the exception. Even if there is code inside the catch block it doesn’t necessarily mean that the code isn’t eating the exception. In the above case, the trace statement is commented out, but even if that weren’t the case, the code would still eat the exception because the trace statement doesn’t log or resolve the original reason for the exception.

Now that you can recognize a pattern for an eaten (and therefore potentially unnecessary) exception, let’s turn on first chance exceptions in the debugger and see where these are in your application.

Using First Chance Exceptions to Find the Problems
First chance exceptions in the debugger are useful because they allow the debugger to process an exception before the code does. So when running the code above with first chance exceptions enabled, the debugger would see the exception prior to the execution of the catch block. This makes the exception instantly visible so you can make a decision as to whether the exception is avoidable or not. Eliminating unnecessary exceptions can dramatically improve overall code performance.

Debugging with First Chance Exceptions
Enabling first chance exceptions in Visual Studio is simple. The process is slightly different for Visual Studio 2003 and Visual Studio 2005, though neither is complex.

Visual Studio 2003

  1. From the Debug menu, select Exceptions (or press Ctrl-Alt E). The dialog shown in Figure 1 appears.
  2. Select the first major grouping, in this case C++ Exceptions, in the list.
  3. In the When the exception is thrown group select the Break into the debugger radio button.
  4. Select the next item in the Exceptions list
  5. Repeat steps 3 and 4 as necessary to select all of the items in the list.
  6. Click the OK button


Figure 1. In Visual Studio 2003: This dialog box in Visual Studio 2003 lets you enable first chance exceptions.
?
Figure 2. In Visual Studio 2005: This dialog box in Visual Studio 2005 lets you enable first chance exceptions.

Visual Studio 2005

  1. From the Debug menu, select Exceptions (or press Ctrl-D, E). The dialog shown in Figure 2 appears.
  2. Check all of the check boxes in the “Thrown” column.
  3. Click the OK button.

Once you’ve enabled first chance exceptions, run your application in the debugger by pressing F5. When an exception occurs you’ll be prompted to take action before the code has a chance to respond. Figure 3 shows the dialog that Visual Studio 2003 raises and Figure 4 shows the popup that Visual Studio 2005 raises.


Figure 3. In Visual Studio 2003: The screen shot shows a first chance exception warning generated from the debugger.
?
Figure 4. In Visual Studio 2005: The screen shot shows a first chance exception warning generated from the debugger.

The VS2003 dialog is very plain but it does allow you to Break into the debugger and examine the situation, including the call stack, variables, etc., as well as a Continue option that hands exception handling over to the code. The Break option is essential; it is the means by which you discover the conditions that caused the exception, thereby allowing you to avoid it.

The VS2005 popup is substantially more friendly. It includes a list of potential solutions for the exception in the Troubleshooting tips listbox and provides mechanisms to copy the exception to the clipboard?if you want to do more research later. Unlike with VS2003, the 2005 debugger doesn’t ask you whether you want to break into the code?it just does. All you have to do is research with the debugging tools and, when ready, hit the Run/Continue key, F5, to resume the application.

Unlike last chance exceptions, first chance exceptions won’t automatically end the program if you don’t deal with them in the debugger. If code handles the exception then that application will continue running even if you didn’t address the exception in the debugger.

Managing an Overload of First Chance Exceptions
You might find, while working your way through the code, that you encounter the exact same first chance exception over and over, making it impossible for you to look for others. If this is the case you can instruct the debugger to ignore that particular exception. This is generally ill-advised; it’s far better to resolve the bug rather than suppress it, but that isn’t always possible.

To suppress the bug, you need to put some more fine-grained control in the debugger. Thus far, I’ve only showed you first chance exception handling at a very gross level. But by expanding the nodes in the Debug Exceptions dialog you can identify the specific exception that keeps getting thrown and reset it so that it will no longer break into the debugger when it occurs. This can help you remain productive in your task of identifying avoidable exceptions in the application, even if there are many of them.

Protecting Against Exceptions
In most cases it’s easy to protect against exceptions. You test for a null value before trying to access a property of the object?or you check to make sure a file exists before reading it. These checks are simple and relatively efficient if done correctly. They are certainly less costly than allowing an exception to be thrown and then dealing with the exception.

There are only a few exceptions that are unavoidable or where checking for the condition is unfeasible. For instance, it’s technically possible to determine before a call whether Windows will have to throw an Out of Memory exception. However, the frequency of this exception is very, very low and the cost in terms of code to check for this situation is fairly high. So in some cases, like this one, it is not cost-effective to write explicit tests for all of the things that might cause an exception.

Restructuring to Avoid Exceptions
Sometimes it’s not an issue of testing for the thing that may cause the exception; it’s a matter of restructuring the code in a way that the chances of an exception are minimized. In the above example, it would be better to use a for statement that limits the size of the array indexer to the actual size of the array. This automatically terminates the loop when the end of the array is encountered.

Restructuring a trivial example like the one given above is, well, trivial. However, restructuring larger sections of code may be more difficult. A few tips that may help you restructure your application code to avoid exceptions are:

  • Review the call stack when the error is occurring. The call stack can show you at a glance how the execution got down into a particular method and can allow you to quickly see the structure of the existing code that may need to change.
  • Identify the root cause. While the actual exception may be the result of a missing file, the root cause may be the failure of a previous process or bad input from the user. Identify the root cause and put error handling and exception avoidance code there.
  • Add breakpoints. Breakpoints can help you see where things first went awry or illuminate subtle nuances of the code execution, such as the state of other variables, which may not be available once the exception occurs.

Extracting Exceptions
While hidden exceptions are a huge performance drain it doesn’t mean that they have to suck the life out of your application. By knowing how to identify them and using the tools you already have you can quickly remove them and regain performance your application has lost.

Share the Post:
Share on facebook
Share on twitter
Share on linkedin

Related Posts