|
Introduction
One of the important changes to programming style that .NET brought to developers was the use of exceptions as the primary method of error handling. This was not a revolutionary approach, since exceptions has been actively used in various programming languages, but for a generation of programmers trained on traditional Windows and COM APIs it did require change of their habits. No more HRESULT analysis, no more struggling with E_ACCESSDENIED, no more traversing of records obtained through IErrorInfo interface. Just throw, try, catch and finally.
Since the initial release of .NET there’s been a discussion on whether Microsoft has gone too far with abandoning error codes. In this article I will not go into arguments on that matter. Larry Osterman from Microsoft in his blog articles "Exceptions as repackaged error codes" and "Error Code Paradigms" covers some aspects of this discussion. An excellent set of guidelines was compiled by Krzysztof Cwalina. Of course, Microsoft also published various recommendations on exceptions' best practices, including "Error Raising and Handling Guidelines". So I will not try to answer the question "to throw or not to throw?". Honestly, if I knew the definitive answer of such a question, I would not have spent time to write programs just to study exceptions’ behavior. But I don’t. So I felt I had to thoroughly investigate various aspects of using exceptions. And one of the most important aspects is their performance. Exceptions cost. Throwing them without understanding how much they cost may have a negative impact on your application. In this article I will present performance results measured using different scenarios, including throwing exceptions from methods of different complexity, within a single AppDomain and across domains.
Benchmark application
To collect performance measurements I have written a small .NET Windows Forms application. Using this application it is possible to specify a job type, how to throw (or not throw) exception, and select a time interval while the selected job is executed. When the time expires, the application shows the job execution count. Its main screen is shown in figure 1.

Figure 1. Exception Performance benchmark application.
There are five job types that can be executed:
- Empty - As it’s easy to guess, an empty method.
- String processing - A string "The quick brown fox jumps over the lazy dog" is split into words and each word is then uppercased.
- File access - The application reads the contents of every file in the current directory.
- Database - An SQL statement "SELECT * FROM Products" is executed using SQL Server ADO.NET provider and Northwind database.
- Complex - A combination of string (8 times), file (4 times) and database (2 times) operations. This is a simulation of a simple business logic operation, although a very light one, since both file and database operations are read-only.
In addition the application defines five modes of exceptions:
- No exception - Plain job execution.
- Catch without exception - Method is executed inside
try-catch block, but no exception is thrown.
- Throw - A single exception is thrown and caught after the job is executed.
- Rethrow original - An exception is thrown, caught and re-thrown.
- Rethrow new - An exception is thrown, caught, and then a new exception is thrown with the original one set as inner exception.
Since exceptions drag quite a lot of information, in case the method is executed across AppDomains and results in exception, it incurs considerable extra costs related to its serialization. To measure such costs, I have added the possibility to execute selected jobs in a separate AppDomain.
Below I present the results of application execution on my Dell Latitude D800 with Pentium M 1.70 GHz. Every test was executed using 1 second interval.
The perfect world: no exceptions
To begin with our benchmarking we need to gather reference data: how fast is the code when no exceptions are thrown. Table 1 shows this information:
| Mode/operation |
Empty |
String |
File |
Database |
Complex |
| No exception |
17,748,206 |
267,300 |
2,461 |
877 |
239 |
| Catch without exception |
15,415,757 |
261,456 |
2,476 |
871 |
236 |
Table 1. No exceptions thrown (executions per second).
It is too early to make conclusions at this stage, but two things can be observed. First, there is no cost in try-catch block (might sound obvious but I’ve heard arguments that low level code should avoid try-catch blocks for performance reasons). Second, with a difference of 100,000 times between empty and complex operations it does not make much sense to talk about absolute costs of using exceptions. They must be analyzed in the context of operations.
The real world: exceptions added
Now let’s look at how much the use of an exception costs. The results are shown in a table 2:
| Mode/operation |
Empty |
String |
File |
Database |
Complex |
| Throw |
103,456 |
68,952 |
2,236 |
864 |
236 |
| Rethrow original |
53,481 |
41,889 |
2,324 |
852 |
230 |
| Rethrow new |
55,712 |
43,140 |
2,269 |
847 |
232 |
Table 2. Exceptions thrown (executions per second).
Here we can see that exceptions do not present significant performance threat for operations that are more complex than simple calculation algorithms. Only primitive string processing was affected by raised exceptions. As soon as the method involves accessing files or databases (and majority of them do), the cost of exceptions become really marginal.
Another observation is that wrapping an exception in a new one does not really affect the performance compared to rethrowing an original one, so you should not be afraid of doing this just for performance reasons.
The heavy world: sending exceptions across application domains
If you recognize exceptions as the sole method of propagating error information and replace them with numerical traditional error codes, you should be aware of the associated costs of cross-domain exception marshalling. Every exception is a large packet of information that includes even a call stack. When being sent across threads or application domains, this packet is serialized and de-serialized, and of course this comes at a price. Table 3 shows these costs:
| Mode/operation |
Empty |
String |
File |
Database |
Complex |
| No exception |
44,437 |
36,101 |
1,458 |
749 |
175 |
| Throw |
3,073 |
2,942 |
930 |
574 |
160 |
| Rethrow original |
2,950 |
2,881 |
929 |
588 |
158 |
| Rethrow new |
2,882 |
2,875 |
938 |
577 |
158 |
Table 3. Exceptions thrown across domains (executions per second).
As you can see, application domains are performance killers by themselves. An empty method is executed with a speed that is more than 400 times slower than the execution within the same domain! Think twice before splitting your components between different domains. However, sometimes this is an only option, and in such cases exceptions add even more to the performance costs. For simple computational tasks exception handling will use more than 90% of the processing time. Even for file access operations cross-domain exception marshalling slows down the execution to almost half the speed. But more complex business logic operations can still afford to throw exceptions even in multiple domain environments.
Conclusion
Exception handling is much more than its performance aspects. Here, I have not touched such topics as whether exceptions should be thrown only in abnormal situations or they should cover all unsuccessful operational states. Perhaps the only conclusion I can draw from the presented results is that for everything but simple computational algorithms exceptions do not have big impact on performance, at least not big enough to make design decision regarding exceptions based mostly on performance reasons. You should carefully apply the guidelines presented in the referenced articles and in case performance is of high concern implement additional methods that will help avoid unnecessary raising of exceptions (Tester-Doer and Try-Parse patterns in Krzysztof Cwalina’s article). But in general exceptions will fit in most architecture without noticeable performance impact.
References
- Exception Throwing Guidelines by Krzysztof Cwalina
- Exceptions as repackaged error codes by Larry Osterman
- Error Code Paradigms by Larry Osterman
- Error Raising and Handling Guidelines by Microsoft
| You must Sign In to use this message board. |
|
| | Msgs 1 to 25 of 33 (Total in Forum: 33) (Refresh) | FirstPrevNext |
|
 |
|
|
First of all Thank You! Excelent article.
I am so sick of relying on HRESULT's to "catch" critical errors. I find .NET implementation of exceptions a step forward. Look at how much cleaner and easier to debug your code is when using exceptions as oposed to alternatives.
Have a function that returns a bool (for example) to say "Yes I did it" or "No I couldnt do it", but throw an exception if somethign went really wrong. All this performance talk is not relavant in most cases (not to bring down the article, it is very usefull). If an expception was thrown or if you throw one, at that point something is seriously wrong, in which case performance is the least of your concerns. And as stated in the article having a try-catch block has no performance hit whatsoever.
So let me see... critical error handling with no performance hit until a critical error actually occurs... sounds good, I'll take it.
|
| Sign In·View Thread·PermaLink | 5.00/5 (3 votes) |
|
|
|
 |
|
|
Thank you Nick!
Вагиф Абилов MCP (Visual C++) Oslo, Norway
If you're in a war, instead of throwing a hand grenade at the enemy, throw one of those small pumpkins. Maybe it'll make everyone think how stupid war is, and while they are thinking, you can throw a real grenade at them. Jack Handey.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
I agree. We're using exceptions in case of an error. And at that time the performance really doesn't matter. It's informative to see that the try-catch construct has no performance impact. Thanks for the article
company, work and everything else @ netis
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
I think you should not take a possible performance impact in case of a thrown exception as reason to stop throwing exceptions. Every "if" you have to program to check the return code will have a performance impact, too. And I've seen code where programmers forgot to evaluate the return codes or were just too lazy - they thought the method would not fail anyway. I think that the discussions about performance impact are often misunderstood. If exceptions are exceptional they don't have performance impact. If you use exceptions as result objects you will have performance impacts. (E.g. the "lookForTheMatchingEntry" method did not find a matching entry and is called in a loop of 1 million records.) That's why I usally use a method signature like "FindEntry(Criteria criteria, bool throwWhenNotFound)". It let's the caller decide how the method should behave when no matching entry was found: return "null" or throw an exception. There is one thing you have to consider when highest performance matters (and only in those specific methods!): Try-Catch blocks can have a performance impact even if no exception is thrown. To name an example: paint event handlers (there are articles on the web covering how to handle this). I've also found that it's very hard to debug a program where exceptions are used in the normal program flow. The debugger stops on every 2nd step, if you turned stop on exception on and if a exception with a higher severity is thrown you won't even notice it. A good exception handling and loggin concept can make the maintanance of an application much, much easier.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
ptmcomp wrote: I've also found that it's very hard to debug a program where exceptions are used in the normal program flow. The debugger stops on every 2nd step, if you turned stop on exception on and if a exception with a higher severity is thrown you won't even notice it.
That's a very important point, agree!
Вагиф Абилов MCP (Visual C++) Oslo, Norway
If you're in a war, instead of throwing a hand grenade at the enemy, throw one of those small pumpkins. Maybe it'll make everyone think how stupid war is, and while they are thinking, you can throw a real grenade at them. Jack Handey.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
As their name suggests, exceptions should only be used in exceptional situations.
It's academic to know that, for a given processor, exceptions can be thrown/catch with a string (or any other type) 68,952 times a second. If you're throwning an exception more than once or twice a SESSION, you've got more serious problems to worry about than performance.
I think you need to re-read the documents you referenced in your article. It's only useful to know that they are always slower than other flow-control methods. At least one of those documents outlines that exceptions should not be used for normal program flow.
PeterRitchie.com
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Thanks for your posting... I was afraid that everybody who has read this article now thinks that exceptions are bad and should not be used.
What gives me the creeps even more than discussing about exception handling as a valid control flow mechanism is that it still seems to be common thinking that you have to optimise all your code. Why do so many coders want to give their CPU a constant 99% idle task? Exception handling makes your life easier; if it's too slow, get rid of it. But don't ignore it because it slows down a rarely-called method for two nanoseconds.
eb
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Programming is alot like many thinks in life--there's the intended usages and there's the things people do with them. A loaded .38 looks like it would do the job of hammer, and probably will--most of the time.
Herb Sutter's C++ Coding Standards[^] book has a section on premature optimization (item 8) with a corollary Don't pessimize permaturely. He sums up the pitfalls of optimizing everything you can get your hands on. Joe Newcomer[^] is pretty vocal about it here[^] too.
Moving from a C++ view of the world to a .NET (C#) view can actually reduce code speed. For example, in the C++ world it is common to hoist the array length retrieval out of the loop[1]:
const int len = f.Length(); for(int i = 0; i < len; ++i) { f[i]; }
This is actually slower in .NET (C#). The following is the more efficient method in C#:
for(int i = 0; i < f.Length; ++i) { Console.WriteLine(f[i].ToString()); }
1 Eric Gunnerson's C# Compendium, April 18, 2004[^]
PeterRitchie.com
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
I will not argue with you Peter about whether to use exceptions in a normal program flow because I haven't made up my mind about it yet. Yes, the name "exception" hints that it should be used in exceptional situation. On the other hand, everything that is handled is not exceptional anymore. The article did not try to answer this question, it just presented some test results. And it is NOT academical to keep in mind how exceptions affect string processing performance, because often developers need to find out how to propagate error situations from low level code. They should always have a clue about performance implications of certain techniques.
Вагиф Абилов MCP (Visual C++) Oslo, Norway
If you're in a war, instead of throwing a hand grenade at the enemy, throw one of those small pumpkins. Maybe it'll make everyone think how stupid war is, and while they are thinking, you can throw a real grenade at them. Jack Handey.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
The test performed clearly shows a x4 difference in the string comparison with a throw. The file and database tests are high latency and should not have registered at all, yet they show a 10% slow down!
Those results are a lot worse than I would have predicted. The article's conclusion is a bit off mark.
|
| Sign In·View Thread·PermaLink | 1.00/5 (2 votes) |
|
|
|
 |
|
|
So what do you find insane? Of course exceptions cost, and this is why I showed their costs for both primitive and complex operations. But if a string processing function throws an exception, it usually does it because of unexpected failure (null pointer or something similar). What else should it do? Nobody will write a string search function that will throw an exception if it does not find requested string. But if I ask it to find a string in a null pointer, why should I care about the cost of exception? I will need to fix the error anyway.
And when it comes to business logic operations, then exceptions can even be used to return controlled failure results, instead of error codes. For high-level business logic operation the cost will be really insignificant.
Вагиф Абилов MCP (Visual C++) Oslo, Norway
If you're in a war, instead of throwing a hand grenade at the enemy, throw one of those small pumpkins. Maybe it'll make everyone think how stupid war is, and while they are thinking, you can throw a real grenade at them. Jack Handey.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
Thank you. Your observations regarding Parse/TryParse are also very useful.
Вагиф Абилов MCP (Visual C++) Oslo, Norway
If you're in a war, instead of throwing a hand grenade at the enemy, throw one of those small pumpkins. Maybe it'll make everyone think how stupid war is, and while they are thinking, you can throw a real grenade at them. Jack Handey.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
Thanks!
Вагиф Абилов MCP (Visual C++) Oslo, Norway
If you're in a war, instead of throwing a hand grenade at the enemy, throw one of those small pumpkins. Maybe it'll make everyone think how stupid war is, and while they are thinking, you can throw a real grenade at them. Jack Handey.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Nice job on this. Silly assertions about the performance cost of exceptions are often bandied about; it is much better to have nice analysis like this to refer to.
I'd like to see (or write) similar little articles on the real cost of runtime-configurable assertions and logging.
(typo: "Exceptions as repackages error codes" --> "repackaged")
Matt Gerrans
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Thanks Matt (and thanks for the typo correction).
Вагиф Абилов MCP (Visual C++) Oslo, Norway
If you're in a war, instead of throwing a hand grenade at the enemy, throw one of those small pumpkins. Maybe it'll make everyone think how stupid war is, and while they are thinking, you can throw a real grenade at them. Jack Handey.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
(comment is an aside...you did an excellent point on performance)
We should only be throwing exceptions in exceptional cases [sic]!
Error (exception) handling has a few guidelines in my implementation teams [for enterprise development]...
- Don't throw an exception if it is not necessary. Return status / boolean codes for success.
- If you must throw an exception, make it meaningful in the boundary layer it's thrown in. This leads into the next "rule" about exception propogation. Layer-specific exceptions should give enough information for support personell to target their investigation (if warranted) in as specific a manner as required.
- If you do throw an exception, do not, under any release / production mode circumstances, propogate stack traces, "deep information", etc. across application domain boundaries. Log it, throw a boundary crossing transformed exception and move on. This way, they are more generic across application "layers" where an exception message may "bubble up" to the end user. End users do not need to see what support personell and developers do.
- For number 3, the same goes for status / success codes.
- Recover as gracefully as possible across domain boundaries. This means if you must do compensating transaction support, do it. It may not be possible to support fully, but give it your best.
- Severe exceptions must, if possible, be logged and accessible by support and developer personell.
- As much as possible, always have an error handler and escape route ;o) Again, the end user must not see "deep" error message, but *must* have enough to complain about intelligently ]:>
- The best error message seen by an end-user is the one that does not presume the viewer is technologically saavy.
There is a lot of debate on exception and error paradigms. Exception / error handling does not have to be the third rail for developers...just always have a plan...
...plan ahead...straighten up...fly right!
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Thanks Ian. One question. You write: "If you do throw an exception, do not, under any release / production mode circumstances, propogate stack traces". But do we have that control in .NET? .NET includes stack trace information in every exception thrown, so I am not sure how can I stop this info from crossing domain boundaries.
Вагиф Абилов MCP (Visual C++) Oslo, Norway
If you're in a war, instead of throwing a hand grenade at the enemy, throw one of those small pumpkins. Maybe it'll make everyone think how stupid war is, and while they are thinking, you can throw a real grenade at them. Jack Handey.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
We do have that kind of control. It's simple. If you do not include the original exception as the inner exception, the only stack available will be at the point the boundary exception is thrown 
Example:
try { // boundary code in which exceptions may occur } catch (StackOverflowException) { // throw a static exception created earlier for just this reason! throw staticOverflow; } catch (SecurityException se) { // log se inside the boundary
throw new BoundaryException("Access Denied"); // no inner exception! } catch ...
This way, even though a stack trace for the hypothetical BoundaryException shows where it was thrown (acceptable since it's a boundary exception,) the SecurityException stack trace is not, though it is logged inside the boundary.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
I see. However, if the SecurityException is thrown from the boundary code, it will be marshalled to the catch block won't it? What I mean is that even if you discard it and instead throw your own new exception, you will still have to pay the price for catching SecurityException (that comes with its call stack). Am I wrong?
Вагиф Абилов MCP (Visual C++) Oslo, Norway
If you're in a war, instead of throwing a hand grenade at the enemy, throw one of those small pumpkins. Maybe it'll make everyone think how stupid war is, and while they are thinking, you can throw a real grenade at them. Jack Handey.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Yes, this is true. However, I must state again, that the rule about exceptions crossing domain boundaries is more for the enterprise domain boundaries, e.g. layer crossing. The performance penalty is acceptable at the boundary because something Exceptional [sic] occured. Within a domain, then you can disregard the rule, thus you won't be paying the penalty.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
General News Question Answer Joke Rant <
|