Sign in to follow this  

Exceptions and performance

This topic is 4771 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm not having a problem, I'm just curious as to how exception handling in C++ works and what it means in terms of performance. What exactly do the try and catch tags do to the program? Obviously you wouldn't plan on your code throwing an exception during normal operation, so would putting these try and catch statements in your code be better for performance than say, checking for null pointers every time? I'm really not clear on how the whole thing works... Any links/comments?

Share this post


Link to post
Share on other sites
Well, if you're using exceptions outside your "must-be-fast" rendering code (or sth like that) then I think that exceptions won't slow you program. They aren't very slow, but on the other hand they won't be very fast eaither, probably slower than what you can write yourself (hovewer, there are many situations when use of exceptions will save you from writing ugly program that uses [exorcism] goto's [/exorcism]

Quote:
What exactly do the try and catch tags do to the program?


I'm not sure (it's just a hypothesis that surely is wrong) but they actually may do _nothing_ to the program, they may just exist to tell programmer "here I am! be aware of me!" etc. or to point out compiler where exception code starts. But probably I'm totally wrong and I would love to hear somebody who knows exactly what's going under the hood.

Quote:
Obviously you wouldn't plan on your code throwing an exception during normal operation, so would putting these try and catch statements in your code be better for performance than say, checking for null pointers every time?


Sure, exceptions can be used when non-critical thing occurs, but in my opinion using them to check if memory was properly allocated is... well, better to say that I prefer to check for null instead :-)

Share this post


Link to post
Share on other sites
Exceptions are generally used in the layer of software that inerfaces with hardware. People can suddenly unplug a cable when your program is trying to access the digital camera over the firewire or a network cable is unplugged, etc.

People tell you not to use them because of performance because they don't want you to write code like

try
{
while(1)
{
(p++)->blah();
}
}catch(...){/*loop done!*/}

That is indeed slower than a normal loop. Someone else can go into more detail of the assembly code generated for exceptions if they feel like it since you did ask. You seem to have the right idea, though.

Share this post


Link to post
Share on other sites
Execeptions are typically used to check for critical errors within a program. Namely, these are the type that would normally result in you closing down the program or it crashing. So in theory, NULL pointer checks can be done that way, so long as all memory is allocated with new and you're catching the exception thrown when new fails (there is one, right?). Otherwise, you still have to have checks in your code that will throw the exception when things go awry.

That said, here's a synopsis of how exceptions work. When you set up a try/catch block, the code address for the catch part is saved somewhere (not too sure where). Then the code in the try block is executed. If during that execution, something throws an exception, I believe a check is done to determine if there is anywhere in the call stack that has an exception handler implemented for the specific type of the exception thrown. If there is, the program immediately unwinds the stack up to the appropriate exception handler and executes the code in the catch block. Once the stack is unwound, you cannot go directly back to where you were. Execution continues from just after the catch block. Unwinding always occurs to the most deeply nested handler appropriate for the thrown object. Note that dynamic memory is not deallocated when an exception is thrown unless you explicitly do so. This means that you may have to implement handlers at multiple levels whose only purpose is to clean up then throw the exception up to the next level of the call stack.

Exceptions shouldn't be performance eaters if you use them properly. Most of the overhead comes only when an exception is handled because the call stack has to be unwound to the right point. And if you're using them only in cases where the program would have to terminate anyway, you might lose a few cycles as your program terminates. In my opinion, this is better than it crashing or leaking memory (though memory is usually cleaned up by the OS for you at termination).

There are other useful ways to use exception handlers that aren't for critical errors, but the nuances of when to use them are subtle and it's difficult to describe when it should or shouldn't be done. Used as a code flow controller, exceptions can - on the rare occasion - prove to be an extremely elegant solution, but for the most part, you'll end up with hideous spaghetti code.

-Auron

Share this post


Link to post
Share on other sites
"Exceptions are generally used in the layer of software that inerfaces with hardware. "

I agree that this is one use of them but that's hardly the only or the main or even the most important use of them although I don't think you meant that the hardware software interface is their most common use:)

They are used when you can't always return an error code, IMHO this is most useful when calling a constructor since you can't return an error code.

They can also be used for times when you can't handle an error or maybe more correctly you shouldn't handle an error in a particular location.

An out of memory error or null pointer exception are two good cases. You can't always decide whtat to do at times like that so you let teh exception propogate back up your app to an area where you can handle it.

And don't worry about the speed hit of exceptions, like the others mentioned it more than likely won't hurt your app. Alot of game logic is now written in scripting languages:)


Cheers
Chris

Share this post


Link to post
Share on other sites
Standard C++ exceptions roughly correspond to the Standard C setjmp and longjmp functions. Each try has an entry and exit cost (e.g. setjmp to both enter and leave) and when an exception occurs a bunch of stuff happens but that generally doesn't matter because something has gone horribly wrong anyway.

Share this post


Link to post
Share on other sites
I couldn't care less about the performance of exceptions when an exception is thrown. As that's supposed to be... well.. an exceptional case :)

What i'm interested in, though, is knowing what performance impact exceptions have on the "normal" code, ie. the code executed 99.999% of the time.

For example, if i do a single try/catch in my "main" function, is the performance of all the program's sub functions affected or not? Especially if some functions are called millions of times.

I'd also like to know if a function, that is called a lot, throws an exception (ex.: an inlined function in a maths class, checking for an argument validity), how much is performance affected?

Y.

Share this post


Link to post
Share on other sites
My guess is that the overhead is slim to none. If I'm right on this, there should only be some extra stuff don when a try/catch block is created and depending on how it's done (which is the part I don't know), odds are that the address of the handler is stored globally. It shouldn't be getting passed as an invisible parameter to every function called within the try block.

As for throwing exceptions, I don't see it as being a performance issue except for when the exception is actually thrown. As I explained, it will unwind the stack to the scope of the handler. If you aren't throwing the exception, it should never give overhead. If you're using it as a check for passing a negative number to the sqrt() function where you recover afterwards, then I imagine it would create overhead.

But if you are using them like that, then don't. It's not what they're for. Just having a function that is capable of throwing an exception should not create overhead in using that function for the non-exceptional cases.

-Auron

Share this post


Link to post
Share on other sites
Well the use I had in mind was in a client/server app where you can't always trust the data. As an example... when one player requests to give gold to another player the client program sends a packet translating to "I want to give X gold to player S" where S is a number that identifies the player. Since I use this number S as an array index, it's POSSIBLE that it might not be a valid client (null pointer errors, index out of bounds errors... crash). Would it be faster to just check for these problems directly, or would a try/catch statement not affect anything as long as an exception does not occur (rare). I'm not as concerned about a few extra cycles being lost when an exception DOES occur, because like somebody has already stated, things have already went wrong anyways.


Edit: Exactly what I'm most interested in Ysaneya... leave it to somebody else to better state my own question.

Share this post


Link to post
Share on other sites
Quote:
Original post by Koshmaar
better to say that I prefer to check for null instead :-)


really, so you would rather have branching code via an if-statement that executes every time it reaches that statement rather than having no normal branching code at all and only handle the exceptional case when it actually does happen, i know which i prefer [smile]

Quote:
Original post by Auron
Note that dynamic memory is not deallocated when an exception is thrown unless you explicitly do so. This means that you may have to implement handlers at multiple levels whose only purpose is to clean up then throw the exception up to the next level of the call stack.


The problem is you can't always release resources if its not seen in the scope of the catch statement, this is where you need to employ RAII techniques.

Exception Handling in C++ is a little flawed, it was introduced late into the language and wasn't taken very far (as Bjarne's describes in the design of c++), languages such as java do exception handling better. Thats not to say don't use it or its unusable its just to say things like a finally statement would help alot, its still better than doing needless error codes that make you write endless amount of if statements that is completely unneccessarily executated for about 99% of the time (prime examples win32 lib, dx).

Excetion Handling is good for library writers & library users as library users know what should happen in a particular situation in are apps not the library.

Share this post


Link to post
Share on other sites
People worry way too much about the performance concerns of exceptions and not enough about the difficulty in writing exception-safe code. Typically the exception handler prolog to a function is pretty tiny. If you are curious as to what the actual cost is during nonexceptional situations then you should look at the generated assembly code and decide for yourself.

Under no circumstances can I see proper exception handling slowing down or speeding up an actual game to a measurable degree.

Share this post


Link to post
Share on other sites
The performance hit of exceptions is not always negligible (though it usually is), even if it is outside loops. And the impact on performance is there even if no exceptions are ever thrown in your program. Just enabling exceptions has some performance impact even if you have no try-catch statements in your program.

The reason for this is that when an exception is thrown, the stack must be unwound. Here's a scenario. Let's say you have a try block, and inside that block you call a function. It calls another function, which calls another, which calls another. Your call stack (starting at the function with the try block) is now 5 functions deep. If an exception is thrown from the deepest function, the program execution must jump up 4 levels in the call stack (well, 4.5 levels because it also has to pop any variables declared inside the try block). All parameters and local variables must be popped off the stack, and the destructors MUST be called for any of those objects that have destructors.

How does this impact performance when no exceptions are thrown? I'm not aware of the intimate details, but at the very least the compiler must add code to the program to manage its own "stack" of destructors that need to be called in case an exception is thrown. Let's assume that the inner function has a for loop in it with a local variable inside the loop's scope. If that local variable has a destructor, the exception stack will have to be manipulated twice for every iteration of that loop.

Let's assume now that each function is contained within its own try block. Each try block may catch different types of exceptions, so I believe each try block needs its own unwind stack. In this case, each variable in the for loop with a destructor causes 10 stack manipulations (2 for each try block). In a complex program that uses exceptions heavily this can add up, even if no exceptions are ever thrown.

The moral of this story is that exceptions should not generally be used to replace simple success/failure return codes, and they should never be used to implement branching within a single function. They should be used for more serious errors (often errors that are difficult to check for or recover from, or errors you expect to almost never occur), and they should be used sparingly. In other words, don't do something like this:

int func(do)
{
try
{
if(!do_operation1())
throw new MyException("Operation 1 failed", ERROR1);
if(!do_operation2())
throw new MyException("Operation 2 failed", ERROR2);
if(!do_operation3())
throw new MyException("Operation 3 failed", ERROR3);
return SUCCESS;
}
catch(MyException *e)
{
CleanUpSomethingThatNeedsToBeCleanedUp();
LogError(e.GetErrorMessage());
return e.GetErrorCode();
}
}

If you're going to do this, you might as well just use a goto because it accomplishes the same thing and it's faster (the syntax is even cleaner). Sometimes it's ok to use exceptions to clean up error handling logic, but you wouldn't throw and catch in the same function. The inner functions would throw exceptions and the outer functions (sometimes several levels up the call stack) would catch them. It would look more like this:

int func(do)
{
try
{
do_operation1();
do_operation2();
do_operation3();

// You could even add something like this
// This is acceptable here because the try-catch block is already there,
// and it doesn't make sense to duplicate the error-handling code
if(something != something_it_should)
throw new MyException("something didn't come out right");
return SUCCESS;
}
catch(MyException *e)
{
CleanUpSomethingThatNeedsToBeCleanedUp();
LogError(e.GetErrorMessage());
return e.GetErrorCode();
}
}

The gain in code readability isn't significant for a simple function like this, but it can be very significant in a complex function, especially when calling a bunch of COM methods (i.e. very useful when using DirectX). It doesn't matter where you put throw statements because they don't have an impact on performance until they're executed. So you can have them in even your tightest loops. But watch out for how many nested try blocks you have, where your try blocks are, and how many objects with destructors you have that get pushed onto to the stack.

Share this post


Link to post
Share on other sites
Quote:
Original post by snk_kid
Quote:
Original post by Koshmaar
better to say that I prefer to check for null instead :-)


really, so you would rather have branching code via an if-statement that executes every time it reaches that statement rather than having no normal branching code at all and only handle the exceptional case when it actually does happen, i know which i prefer [smile]



Well, you're right but now in my engine code I almost succesfully avoid the need to allocate memory when game's main loop is running - I do it in the startup etc. so in my case, I really don't care what performance loss gives single if statement :->

Hovewer, as somebody earlier stated, every time you enter try block, compiler must do something to allow proper handling exception when it is actually thrown - I don't really know, but that may be as "slow" as using if and - what's more important - I really don't care, microoptimization is the root of all evil etc. ;-)

Share this post


Link to post
Share on other sites
This article gives some insight into the code generated by most win32 compilers.
On most other systems the information needed can (supposedly) be extracted from the call stack, but things need to be managed manually on win32 due to the lack of a standardised calling convention.

Share this post


Link to post
Share on other sites
Quote:
Original post by snk_kid


The problem is you can't always release resources if its not seen in the scope of the catch statement, this is where you need to employ RAII techniques.

Exception Handling in C++ is a little flawed, it was introduced late into the language and wasn't taken very far (as Bjarne's describes in the design of c++), languages such as java do exception handling better.


I actually think that Java's exception handling is worse. Finally clause is brittle and so are required exception specifications.

Share this post


Link to post
Share on other sites
Quote:
Original post by doynax
This article gives some insight into the code generated by most win32 compilers.

Some, but not enough. On Windows, C++ exception handling is built on top of SEH. For the nitty gritties of SEH, see A Crash Course on the Depths of Win32 Structured Exception Handling. The basic mechanism described there applies somewhat as well to other OS's running on X86 based cpus - namely using fs to store the chain of exception handlers.

As to the performance impact of exception handling when no exception is thrown, an exception frame is built on the stack - that's about 4 pushes - and a couple of jumps are added that wouldn't be there otherwise. So, as others have said, it's probably not a good idea for time critical code, but for other code it won't have much of an impact.

Here's another excellent resource: The Exception Model.

Share this post


Link to post
Share on other sites
Quote:

So, as others have said, it's probably not a good idea for time critical code,


Everybody keeps saying that but.. if you have a global try/catch in your WinMain, then all your program is contained in the function calls hierarchy. How can you disable exceptions for the "time critical" code only ?

Y.

Share this post


Link to post
Share on other sites
Quote:
Original post by Ysaneya
What i'm interested in, though, is knowing what performance impact exceptions have on the "normal" code, ie. the code executed 99.999% of the time.

For most code it won't make a difference.

Quote:

For example, if i do a single try/catch in my "main" function, is the performance of all the program's sub functions affected or not? Especially if some functions are called millions of times.

The perform hit is only when entering and exiting the try block, calling a function within a try block stays in that try block - i.e. no overhead.

Quote:

I'd also like to know if a function, that is called a lot, throws an exception (ex.: an inlined function in a maths class, checking for an argument validity), how much is performance affected?
Y.


This the catastrophic case, the performance hit will be large - >100%. The magic words were 'called a lot' and 'inlined', once you have a try-catch block inlining doesn't help nearly as much because you still jump to the try-catch-frame setup and teardown code everytime the function is invoked.
By 'a lot' I assume more than several thousands times per second.

At my last job, I pulled up the VM code, and by just moving the location of a couple of try-catch blocks I doubled the speed of executor (there were 2 try-blocks in the token executor!).

So the general advise is don't worry about the overhead, but when you are working on something that is hit 200,000/sec it matters.


Quote:

Everybody keeps saying that but.. if you have a global try/catch in your WinMain, then all your program is contained in the function calls hierarchy. How can you disable exceptions for the "time critical" code only ?


You can either not put exception handling code in the TC part (unlike the original author of our VM I just mentioned), or you can disable exceptions in the project settings (nothing that use exceptions will compile then). This is typically what you do (along with disabling RTTI) for embedded C++ code.

Share this post


Link to post
Share on other sites
Quote:
Original post by Ysaneya
Quote:

So, as others have said, it's probably not a good idea for time critical code,


Everybody keeps saying that but.. if you have a global try/catch in your WinMain, then all your program is contained in the function calls hierarchy. How can you disable exceptions for the "time critical" code only ?

Y.


I think what people are saying is put the exception code-block around a looping code-block, not inside it - particularly when the loop is computationally intensive

aim to knock out entire loops for speed-gain, not the odd comparison or re-assignment (or exception). Use simple, strong programming structures like exceptions wherever you sensibly can and lose them when a piece of code is used so intensively that its necassary.

an exception or other small overhead elements don't affect the speed of an application significantly - they are constant time operations. The things that really impact if you want to optimise are loops. Taking out a loop reduces an order of n+1 complexity to n. Which will have a very noticeable effect on the speed of your code.

Share this post


Link to post
Share on other sites
Quote:
Original post by Timberl
I think what people are saying is put the exception code-block around a looping code-block, not inside it - particularly when the loop is computationally intensive


In general, you should only put it inside in the case where you need to keep going with other iterations of the loop if one fails. You especially should keep it outside if one failure is likely to cause (or indicate) subsequent failures.

On the other hand, handling exceptions "closer to the source" can make it easier to understand. Personally I try to write in an exception-minimal style: I catch stuff thrown by libraries as soon as I can handle it (in Java, that goes whether the exception is checked or not; and often the handling is "do-nothing", because *it's ok*, even in release - "you tried your best, but I can do without the resource by just skipping this other bit" - I think of the handling for the exception simply being *skipping the rest of the try block*), avoid creating my own situations where something could be thrown or caught, and end up hardly ever having to throw anything explicitly or catch something that was my own fault. I also make use of design patterns intended to be helpful in this regard (especially Null Objects).

Edit: Python is a different beast, however. Even when you don't do it explicitly, exceptions are thrown and caught *all over*, but usually have a fairly short lifetime. For example, Python's for loops are implemented with exactly the kind of exception logic that was labelled as deprecated earlier in the thread (iterable objects implement a "next()" method which is called repeatedly until it raises a "StopIteration" exception). Of course, Python is expected to be slow anyway ;) (And I assume its exception handling is optimized for the common cases.)

Share this post


Link to post
Share on other sites
In the Unreal engine when there is an error, you get a stack trace. I believe this is implemented by having a try-catch in every function that shows up in the stack trace in the error message at the end, and Unreal isn't slow. I also do it this way and it doesn't seem to slow it down.

Share this post


Link to post
Share on other sites
Quote:
Original post by Kibble
In the Unreal engine when there is an error, you get a stack trace. I believe this is implemented by having a try-catch in every function that shows up in the stack trace in the error message at the end, and Unreal isn't slow. I also do it this way and it doesn't seem to slow it down.


No, it just has an automatic object that adds the function name to some static stack somewhere. Your way is just backwards.

Share this post


Link to post
Share on other sites
I don't know that I'd use exceptions for checking input. You should expect that someone will try to send garbage to your function, so I think it might be dangerous to just use whatever you get and hope that it's stopped by an exception.

Share this post


Link to post
Share on other sites
Quote:
Original post by Ysaneya
Everybody keeps saying that but.. if you have a global try/catch in your WinMain, then all your program is contained in the function calls hierarchy. How can you disable exceptions for the "time critical" code only ?


I can't speak to how C++ exception handling deals with it, but I can speak to how SEH deals with it. To get to your question straight away, you do not need to disable exception handling for the entire program to disable exception handling for 'time critical' code. Just don't wrap time critical code in a try-except block (or try catch). Exception handlers are established as a chain aka a linked list. When an exception occurs the OS searches this list looking for a handler that will handle the exception. If there is no immediate handler, the search for a handler will eventually reach the "final handler" - whether there is a global try/catch in WinMain or not. Many compilers employ stub code that wraps the call to 'main' (or WinMain) in an exception block. Jeff Ricther describes this somewhat in Programming Applications (Ch. 6, Ch. 25) with the following psuedo code:


VOID BaseProcessStart(PPROCESS_START_ROUTINE pfnStartAddr) {
_ _try {
ExitThread((pfnStartAddr)());
}
_ _except(UnhandledExceptionFilter(GetExceptionInformation())) {
ExitProcess(GetExceptionCode());
}
// NOTE: We never get here.
}


pfnStartAddr is a value taken from the executable file header. For more detail read Richter's book.

At any rate, the overhead for the final handler is already paid by the time you reach 'time critical' code. The exception frame for it was built on the stack before WinMain was ever called. And if you add another exception frame in WinMain, the overhead is paid for in WinMain, and not in the 'time critical' routine.

Lastly, if an exception reaches the final handler, the program is toast anyway.

Share this post


Link to post
Share on other sites

This topic is 4771 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this