Jez
Jez

Reputation: 29993

Why doesn't C# have lightweight exceptions?

It's often said that you shouldn't use exceptions for regular error handling because of bad performance. My guess is that that bad performance is caused by having to instantiate a new exception object, generate a stack trace, etc. So why not have lightweight exceptions? Code like this is logically sound:

string ageDescription = "Five years old";
try {
    int age = int.Parse(ageDescription);
}
catch (Exception) {
    // Couldn't parse age; handle parse failure
}

And yet we're recommended to use TryParse instead to avoid the overhead of the exception. But if the exception were just a static object that got initialized when the thread started, all the code throwing the exception would need to do is set an error code number and maybe an error string. No stack trace, no new object instantiation. It would be a "lightweight exception", and so the overhead for using exceptions would be greatly reduced. Why don't we have such lightweight exceptions?

Upvotes: 8

Views: 1247

Answers (6)

Dave Markle
Dave Markle

Reputation: 97691

The performance hit isn't just because you're creating a new Exception object. A lot of it has to do with the conditional unwinding of the stack that needs to be done when an exception occurs.

For example, think about the work that would have to be done when you have exception handlers that catch different kinds of exceptions. At each point in the stack, as it's unwound from callee to caller, the language must do a type check to see not only if the exception can be handled, but what the most appropriate handler is. That's a significant amount of overhead in its own right.

If you really want to be lightweight, you should return a result from your functions -- that's what Int32.TryParse() does. No stack unwinding, no type checking, just a simple condition which can easily be optimized for.

EDIT: One somewhat interesting thing to note is that C# was created after Java. Java has a couple of interesting constructs which cause exception handling to be more complicated than what we see in C#, namely checked exceptions and the throws keyword. Kind of interesting reads. I'm (personally) glad that C# didn't include this "feature". My guess is that they bifurcated their exception handlers to boost performance. In the real world, as I understand it, a LOT of developers just end up specifying throws exception in their function declarations.

Upvotes: 4

JulianR
JulianR

Reputation: 16513

You're not recommended to use TryParse instead of Parse because of performance. If there's any chance a parse can fail (because it's user generated input for example) then the failure to parse is not exceptional, it's to be expected. As the name implies, exceptions are for exceptional circumstances. For stuff that should have been caught earlier, but wasn't, so unexpected that you can't really continue.

If a function expects an object but instead null is passed in, then it's up to the designer of the method what the right thing to do is in this case. If the parameter is an optional override for a default, the program ca continue and use the default or ignore the parameter. But otherwise, the program should simply throw a ArgumentNullException.

Performance shouldn't be a consideration at all when deciding to use exceptions or not. It's a matter of intent and purpose. They're not even that slow, sure they're many times slower than adding two integers, but I can still throw 50,000 exceptions per second on my aging Core 2 Duo. If the use of exceptions ever becomes a bottleneck, you're not using them in the right way.

Upvotes: 1

Billy ONeal
Billy ONeal

Reputation: 106530

Because the utility offered by the "heavyweight" exceptions is exceptionally (ha ha) useful. I can't tell you how often I've wanted the ability to dump something like a stack trace in C land without having to ask people to yank out a debugger.

Generating things like the stack trace after the fact (i.e. after the exception has been caught on demand) are infeasible because once the exception has been caught, the stack has been unwound -- the information is gone. You want information about the point of failure; so the data must be collected at the point of failure.

As for the "new object instantiation" -- that is so cheap in comparison to other expensive exception features (unwinding the stack, stack trace, multiple function exit points, etc.) that it isn't worth worrying about.

Upvotes: 2

Honza Brestan
Honza Brestan

Reputation: 10947

The exception object instantiation is the smallest problem in the whole case. The real performance killer is that the control flow must stop executing your program and has to look up the call stack for possible handlers (catch blocks) that can catch the thrown exception, then it has to execute the correct ones (and their finally blocks), rethrow exceptions when told so and then continue executing the program on the right place, i.e. after the last handler. Your idea of "lightweight" exceptions would change nothing of this, it would even slow down creation of threads, because it would have to create and store the exception object, and would prevent exception filtering by type, which is now possible.

By using TryParse, you avoid all this by a simple conditional clause, also you actually write less code and it is much easier to read and reason about.

Exceptions are for exceptional cases and in such scenarios, they provide lots of useful information for logs/debugger.

Upvotes: 5

Servy
Servy

Reputation: 203817

The problem with exceptions isn't just generating the exception itself, that's honestly not even the most time consuming part. When you throw an exception (after it has been created) it needs to unwind the stack going through each scope level, determining if that scope is a try/catch block that would catch this exception, update the exception to indicate it went through that section of the stack, and then tearing down that section of the stack. And then of course there are all of the finally blocks that may need to be executed. Making the Exception itself store less information wouldn't really simplify any of that.

Upvotes: 3

Kirill Bestemyanov
Kirill Bestemyanov

Reputation: 11964

You should use int.TryParse in your case. And it is faster and more readable to test some conditions then to throw and catch exception. Use exceptions for exceptional situations not for regular validation.

Upvotes: 2

Related Questions