C++ try/catch clause impact to real-time performance

phoffric
phoffric used Ask the Experts™
on
A staff SW engineer once told me a few years ago that if no exceptions occur in a C++ try/catch clause, there are still impacts  to real-time performance. He didn't explain the underlying reasons behind his statement. I would like to get an under-the-hood explanation, as well as reading the supportive C++ specs.  Is his statement universally true or platform dependent? Thanks.

(Please assume for this question that the deployed system never has an exception.  One could do a timing test with and without the try/catch, but this discussion was over the phone. Anyway, I am trying to understand the statement without experimentation, but just based on the C++ language specs.)
Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
Fabrice LambertConsulting
Distinguished Expert 2017

Commented:
evilrixSenior Software Engineer (Avast)

Commented:
Yes and no. In theory yes, since the compiler has to synthesise additional code to handle the exception and any stack unwinding. In practice, no since most compilers do an excellent job of optimising code and the impact is negligible unless such code is in a tight loop or is super performance critical.

The old saying that premature optimization is evil really does apply here and you should write code to be optimal, but don't prematurely perform unnecessary optimization without first profiling the code.

In nearly all, but extreme cases, it won't bring you any gains and the spaghetti code you often end up writing to handle your own error framework (such as error return codes) will often be way less optimal and harder to maintain.
David FavorFractional CTO
Distinguished Expert 2018

Commented:
The real questions - does it matter + is there a better way to catch errors.

If you're making 1,000,000s calls/second then this might matter + might not. Only testing will tell the truth.

You must some how catch errors. Code with try/catch blocks, as evilrix said, theoretically is slower + errors must be caught + processed correctly via some mechanism. The reason try/catch blocks have become a common pattern is they're so easy to understand + code becomes far easier (usually) to manage using this mechanism for error processing.

Note: Even real time code speed is limited by something, normally some i/o device.

Describe your exact application + likely you'll receive even better answers.
Python 3 Fundamentals

This course will teach participants about installing and configuring Python, syntax, importing, statements, types, strings, booleans, files, lists, tuples, comprehensions, functions, and classes.

Author

Commented:
>> Describe your exact application + likely you'll receive even better answers.
Ok, before delving further into under-the-hood, let me give you a potential scenario which may be stronger than what I alluded to in the OP.

Initially, there is a try/catch clause because of a potential exception(s) being raised. During the program life cycle, changes are made in that block that eliminates all possibilities of an exception being raised. So, by rights, no harm would be done to remove the try/catch clause. Consider then two scenarios - one, where the try/catch is removed (i.e., no need to replace it with any checks that might cause an exception - I would remove the try/catch for this case); and two, where the try/catch is left  for some reason, perhaps negligence.

In this scenario, we know from code analysis that the block will not have any errors or exceptions in it.

In an interview years ago, when asked whether a try/catch clause that never had to enter the catch section and never raised an exception would run slower. I said "No". I was told that it would run slower. Their application was either real-time, or time-critical embedded C++ systems. (I don't recall the OS, but from experience, I can easily imagine that the original try/block region would be called 1000-2000 times/second.)

I asked this question after searching online for a Yes or a No with an explanation that I could understand. I saw both Yes and No as I looked, and when there was a brief explanation, I didn't understand the under-the-hood explanation. Hence, this question.

Thanks,
Paul
evilrixSenior Software Engineer (Avast)

Commented:
I mean, the answer is maybe. It depends on the compiler and how good it is at code optimization. The advent of noexcept in C++11 will almost certainly help with this. Ultimately, this is implementation specific and the pedant in me would have forced me to ask the interviewer where about in the C++ standard that it is defined that code will be slower. I hate these type of interview questions because, basically, they are written making assumptions. In this case, the assumption is probably true, but it's still an assumption. The correct answer is, I don't know until I profile the code.

So, in summary, yes exception handlers can impact performance, unless code is performance critical it's never going to be a real issue (except in tight loops, maybe), if it is performance critical you should be code profiling anyway and so you'd find the bottle necks, lots of interviews don't actually know what they are talking about outside of their own problem space, premature optimization is evil!
David FavorFractional CTO
Distinguished Expert 2018

Commented:
As evilrix suggested, really depends on your compiler + also compiler options you pass, during compilation.

Just build some test code + test the difference with + without your try/catch blocks... maybe wrapping all try/catch block in a #define, so you can toggle these on/off for testing.

Author

Commented:
>> It depends on the compiler and how good it is at code optimization.

I buy that. Can you help me understand what try/block setup is necessary for one or two implementations that you might be familiar with. Let's assume for the moment that the compiler has no optimization. Just trying to understand the basic setup under the hood. Then if still unclear to me about how optimization might work, we can discuss that later. Thanks.

(BTW, I got an offer despite not getting this question correct according to them. And, the embedded system was extremely real time critical, so maybe their assessment and their own timing tests verified their answer. I did not take the offer because it came in the form of 65 pages.)
evilrixSenior Software Engineer (Avast)

Commented:
Are you asking me to explain what the compiler does with the try/catch during compilation? If so, the answer is that I have no idea. Firstly, it's going to be compiler specific and secondly, I really don't care. I know that sounds glib, but that's the point of using a higher level language, you shouldn't care.

Now, you could say if you don't know then how can you assert the compiler will optimize. The answer is, I'm not asserting it will, I'm asserting that it can. This is why profiling is so critical if your code is performance critical. I write code following my understanding of the C++ standard, do my best to follow generally recognised best practices and profile code when necessary to find and fix any bottlenecks.

If you're really interested in low level optimization, I can highly recommend reading the attached PDF.

optimizing_cpp.pdf

There are more great resources on the site I downloaded this from. It's a little out of date, but mostly still applicable.

https://www.agner.org/optimize/

Author

Commented:
>> Are you asking me to explain what the compiler does with the try/catch during compilation?
Yes, that is what I was referring to in the OP for under-the-hood.

>> it's going to be compiler specific
Ok. But I'd like to understand some basic principles that a compiler must do (one way or another) to set up a Try portion. Does it have to do anything that is dynamic; if so, what?
evilrixSenior Software Engineer (Avast)

Commented:
Well, the problem is that all compilers handle this totally differently. For example, MSVC implements C++ exceptions using SEH (Structured Exception Handling) and so understanding how that works will go some way to answering your question for that compiler. The following is a great blog post to give an overview of how this actually works.

https://accu.org/index.php/journals/245

The following is a reasonably good explanation of how g++ handles things.

https://monoinfinito.wordpress.com/series/exception-handling-in-c/

As you can see, pretty different.

In basic term, the compiler stores the exception state and then starts stack unwinding, looking for a handler in each stack frame, starting with the current one. If it runs out of stack frames and finds no handler  std::terminate is called. How the stack is unwound and how the exception state is stored is up to the compiler, but one can imagine that the program stack is used to accomplish this.

I'm afraid that's the best I can do without Googling and I wouldn't insult you by doing that (unless you want me too), as I know you're just as capable as I am doing that.

I'm really sorry I can't give you a better reply, but I only tend to dig into lower level stuff like this if/when profiling dictates it and it's just never been something I've encountered as a performance bottleneck. The only thing I would add is this: only implement a catch handler at the point you can actually handle the exception usefully. Having handlers that can't really handle the exception and end up either swallowing it and returning error codes or re-throwing are generally a sign of smelly code.

Author

Commented:
I took a very brief look at the "monkey" link.
https://monoinfinito.wordpress.com/series/exception-handling-in-c/

On these articles the process of throwing and catching an exception will be explained with quite a lot of detail, but for the impatient people here is a small brief of all the articles that will follow: how is an exception thrown in gcc/x86:
Since in my OP, we know that no exceptions can be thrown, what I am looking for is simply what dynamic activity has to be done in order to setup the try block. If there is no dynamic activity, then there is no performance hit; if there is dynamic activity, then I would guess that there is a performance hit, however neglible it may be (without optimization). Sorry that I didn't have time today to delve further into your two links, so if they do explain this question, I apologize in advance. Thanks.

(I have to fill out lots of forms today/this weekend for a new job that starts Monday - and no, it is only HPC, and not real-time or time-critical, so this question has nothing to do with that. Curiosity about this old interview question just raised its head again, so I though I'd ask.)
evilrixSenior Software Engineer (Avast)

Commented:
Well, part of the issue is that the compiler can never be 100% sure an exception won't be throw, prior to C++11. For example, what if it's calling an external library? The old throw specifier gives no compile time guarantee against excrptions being thrown and so the runtime way of handling an unexpected exception was to call std:: unexpected whose default behaviour is to call std::terminate. It therefore stands to reason that catch handlers must be implemented.

The C++11 noexcept is only slightly different in behaviour and is capable of compile time evaluation as to whether exceptions may be thrown as long as all child functions are also noexcept.

Functionally, the compiler sets the stack frame of the noexcept function to be considered the top level of stack unwinding. Such a function is still permitted to call throwing functions. If no handler is reached before the stack frame of the noexcept function is unwound to, std:: terminate is called.

So, either way  it seems logical to me that if a catch handler exists and if the compiler cannot, at compile time, determine categorically, that no exception will be called (which I think it can achieve with noexcept), then it will still need to synthesise code to implement your handler.

The only way to categorically prevent handler synthesis is to disable stack unwinding as a compiler setting (most compilers support this ability). The problem is, we really can't know what the compiler is going to do not how it will optimize the handlers and so this is why I keep stressing that profiling the code is the only way to be sure. I know that may sound like a cope-out, but it's really not. L

The general golden rule is that we are not compilers and cannot know what the compiler will do. Write code to be optimal (no unnecessary heap allocation, pass by [const or universal] reference, unwind loops where possible and so on) and leave optimization up to the profiler.

I really cannot stress this enough: premature optimization is evil. It is generally pointless, often leads to spaghetti code and can even decrease performance. By all means maintain code, remove unneeded exception handlers, but don't sweat the micro optimisations until a profiler tells you to.

Author

Commented:
Bear in mind, this is more of a theory-type question, and not how to code in-the-future-type question. I just want to understand why the interviewer said what he said . Now to make things simpler , and consistent with what I talked about in the previous post about maintenance , assume that we are not using C++11 but instead using C++03 thread. (If one could convert older code to C++ 11 code in one fell swoop , then of course that would be done .) And let us assume that the body of the try block has a function call and that function is in another module. I am in total agreement with you that the compiler has no way to know whether or not that function will raise exception. However we know from changes to that function that while it once might have raised an exception, we now know that due to maintenance, we now can see that it no longer can raise an exception.

 In this scenario, it would do no harm to renew the try/catch clause, but for now, let us assume that this clause was left in. I agree that the compiler has to do something about the catch clause, since the compiler doesn't know that an exception will never be thrown. I would like to know what does the compiler have to do to set up the try clause and whether any of that code has to be executed dynamically every time when entering the try block. Different compilers may do different things, but if we can figure out what one compiler does , that will be progress.

>> premature optimization is evil.
 You are preaching to the choir. But off-topic, it might be interesting to note that a scholar from an Ivy League school on our project told me that this is incorrect . However to put things in context , I believe he was talking more in terms of software architecture starting from blank sheet . In that case you do have to consider performance early in the game in setting up hardware and software components. I mean you wouldn't want to purchase a single 386 PC to handle a heavily hit website. But this is off-topic and not something that needs to be discussed further.
evilrixSenior Software Engineer (Avast)

Commented:
Like I've already said, I don't know. It's entirely compiler specific. One way would be to look at the disassembly of a trivial example, but not being able to code in (anything but trivial 6502/Z80) assembler, it's not really something I can assist with. Maybe someone else, with a better knowledge of compiler design can help. It's a shame Infinity08 no longer frequents EE as I'm super confident he'd know (he's a personal friend, so I know just how smart he is, heh).

Sorry I can't be more help.

Author

Commented:
Get oo back! :)
evilrixSenior Software Engineer (Avast)

Commented:
Heh. You saying I'm not good enough for you. :)))

Unfortunately, he no longer has the time nor inclination.

Author

Commented:
Heh, not at all, but two giant brains can fill a universe.

BTW, I wasn't necessarily looking for assembly code, but rather general concepts on what kind of code must be present to set up the try block, and whether this code is called once or every time the try block is entered.
evilrixSenior Software Engineer (Avast)

Commented:
Well, I guess the compiler will always have to synthesise code to handle the catch. At the lowest level the compiler will need to implement code to identify if an exception was thrown and tests to identify which exception was thrown and branches to the appropriate handlers and/or code to handle stack unwinding. This is; however, conjecture.
Top Expert 2016

Commented:
rather general concepts on what kind of code must be present to set up the try block

there are implementations of exception handlers (EH) which push an EH code block onto the stack when a  try clause was entered and pop it off on exit. the address of the most recent EH block was available for each thread and to unwind EH blocks that were present in the call stack all those blocks were linked from newest to oldest. when an exception was fired the last EH block was executed by pointer. for each EH block kinds of exceptions could be identified and processed. a match on an EH case does a cleanup of the stack to the point where the found EH block was pushed. then control transfers to the EH catch handler. No matches on the EH would pass the control to the next EH and so on.

this kind of implementation is some way expensive because of the push and pop that has to be done even if no exception was raised. i would assume that your interviewer had kept this in mind when asking you. a practical excourse on implementing EH could be found at
     codeproject.com/Articles/2126/How-a-C-compiler-implements-exception-handling


better (and younger) implementations were using tables that record begin and end of all try clauses. the concept doesn't need any extra code for try blocks then. if an exception was raised there was a look-up onto the table and further to the EH code block referenced by table entry. i read that windows 64-bit SEH was implemented that way but didn't verify thsi myself.

more detailed information on c++ exception-handling you might get by a 2002 draft of the C++ standard commitee

  www.open-std.org/JTC1/SC22/WG21/docs/TR18015.pdf

it is old but might still be valid especially for some of your questions regarding the c++ performance.

Sara

Author

Commented:
The codeproject.com link started to get a little too complicated for a quick review. Didn't have time since I just started a new job last Monday.


there are implementations of exception handlers (EH) which push an EH code block onto the stack when a  try clause was entered and pop it off on exit.
Please provide a link about this. Only interested in whether there has to be done EH setup; and if so, whether it is dynamic or static setup.

Could you point out the section in the 2002 draft of the C++ standard commitee that addresses this. Again, we can assume that the compiler does not know that an exception will not be thrown; however in our scenario, we know that an exception can not be thrown.

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial