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:
- 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
- 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<br>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<br>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
Anonymous
November 08, 2006
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.Anonymous
November 08, 2006
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 slowAnonymous
November 08, 2006
The comment has been removedAnonymous
November 08, 2006
So something exceptional happened 1000 times and it only cost you 3 seconds? Seems VERY acceptable to me.Anonymous
November 08, 2006
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!Anonymous
November 08, 2006
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.Anonymous
November 09, 2006
Hi all - A security question for you all that I haven't had time to think about in depth yet but wantedAnonymous
April 09, 2008
So something exceptional happened 1000 times and it only cost you 3 seconds? Seems VERY acceptable to me.Anonymous
April 09, 2008
you are right for a desktop application - but imagine it on www.hotmail.com - for every visitor. For the processors', this will be Visitor times 3 seconds :) David