Hello, I have been long taught that exceptions are considered the bad parts in the design of C++ language, and one should avoid using it since it's hard to write exception-safe code (I have no disagreement on this part) and it impedes compiler optimization. However, when recently I dig into the implementation of exception handling mechanism in LLVM recently, my impression is that using exceptions should instead improve performance (in the common case that no exception is thrown out), compared with the traditional approach of returning an error code in every function that can fail: no error-code-checking logic is executed in the fastpath, and error-handling code are moved from the main binary to the exception table, so the CPU is doing less work, and also instruction cache locality should be improved. Is my understanding correct? So my question is: (1) Is the argument that 'exception hurts compiler optimization, and should not be used in perf-sensitive code' outdated now? Or more generally, what are the pros and cons of using exception vs error code, from a LLVM backend developer's perspective? (2) In current LLVM design, is there still any optimization that is prevented by using exceptions? Or is the current LLVM already able to provide the same optimization quality for code using exceptions, compared with code that does not? Thanks! Haoran -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20200813/fa71b679/attachment.html>
On Thu, Aug 13, 2020 at 2:59 PM Haoran Xu via llvm-dev <llvm-dev at lists.llvm.org> wrote:> > Hello, > > I have been long taught that exceptions are considered the bad parts in the design of C++ language, and one should avoid using it since it's hard to write exception-safe code (I have no disagreement on this part) and it impedes compiler optimization. > > However, when recently I dig into the implementation of exception handling mechanism in LLVM recently, my impression is that using exceptions should instead improve performance (in the common case that no exception is thrown out), compared with the traditional approach of returning an error code in every function that can fail: no error-code-checking logic is executed in the fastpath, and error-handling code are moved from the main binary to the exception table, so the CPU is doing less work, and also instruction cache locality should be improved. Is my understanding correct?(this is probably a bit too broad a topic for this forum/tends to end up in heated discussions, etc, but let's see how we go) I believe one of the main reasons your understanding there might be incorrect: Not every function returns an error code. But essentially every function can throw (yes, you can annotate them - but it's one of those cases where C++ may've got the defaults wrong (like const, etc) and it's impractical/unlikely someone's going to effectively maintain noexcept on most of their codebase (and there are performance problems with noexcept - since it's not UB to throw from such a function, it's guaranteed to call terminate, so you have to generate exception handling code in every noexcept function))> So my question is: > (1) Is the argument that 'exception hurts compiler optimization, and should not be used in perf-sensitive code' outdated now?I don't believe so - having every function call being potentially control flow changing inhibits certain optimizations that would otherwise be possible (certain code motion, reordering, etc can't be done) compared to a relatively smaller number of explicit error-handling calls.> Or more generally, what are the pros and cons of using exception vs error code, from a LLVM backend developer's perspective?Exceptions inhibit code motion, but allow for smaller code/keeping the cold path/handling further away. I think that's basically the mechanical tradeoff (setting aside issues of readability, maintainability, etc - having an explicit keyword/notation on calls that can throw could help a lot from a maintainability perspective, for instance)> (2) In current LLVM design, is there still any optimization that is prevented by using exceptions? Or is the current LLVM already able to provide the same optimization quality for code using exceptions, compared with code that does not?I believe that exceptions do still hinder optimizations when compared to code that doesn't throw or use a return code*. While that may seem like an unfair comparison, it's my understanding that it's part of the excessive cost of exceptions. Not to mention how expensive they can be when they are thrown, compared to explicit error handling. * Note this isn't a case of missed optimization opportunities (well, perhaps in LTO it's a missed opportunity - if we did whole program analysis to prove certain call trees never throw in practice, then we could propagate that information and improve optimizations) - but a limitation of exceptions. - Dave
Sterling Augustine via llvm-dev
2020-Aug-13 22:35 UTC
[llvm-dev] Exceptions and performance
There is a fair amount of dispute and detail here, and real benchmarks can be difficult to write, because you often end up in arguments about whether or not the two styles of coding are equivalent or not. But I agree with Dave--exceptions generally inhibit optimization. One way to think about this is that, generally speaking, the less the compiler can prove about a program, the less aggressive it can be with optimizations. If it doesn't know what the control flow will exactly look like, it can't do certain transformations. Exceptions add a conditional goto from every single function call (without good noexcept hygiene, which practically no program has), to the exception handler or cleanup landing pad (whether the exception handler is written in the code explicitly or not, something has to run the destructors of objects that are going out of scope.) Worse, inlining doesn't really save you, because any function call that gets inlined also will have an implied conditional goto for any function it calls, now to two possible landing pads. Is it safe to move code across this goto? It's comparatively hard to prove things about that. Worse, a compiler generally know if a function call throws or not, or event the type of exception that will get thrown. It definitely knows what the return type and value of a function is. It is very easy for a compiler to reason about the error checking around a function call--the code is all there and explicit. All of this restricts what it can prove about the program. And the less it can prove, the less aggressive it can be. On Thu, Aug 13, 2020 at 3:13 PM David Blaikie via llvm-dev < llvm-dev at lists.llvm.org> wrote:> On Thu, Aug 13, 2020 at 2:59 PM Haoran Xu via llvm-dev > <llvm-dev at lists.llvm.org> wrote: > > > > Hello, > > > > I have been long taught that exceptions are considered the bad parts in > the design of C++ language, and one should avoid using it since it's hard > to write exception-safe code (I have no disagreement on this part) and it > impedes compiler optimization. > > > > However, when recently I dig into the implementation of exception > handling mechanism in LLVM recently, my impression is that using exceptions > should instead improve performance (in the common case that no exception is > thrown out), compared with the traditional approach of returning an error > code in every function that can fail: no error-code-checking logic is > executed in the fastpath, and error-handling code are moved from the main > binary to the exception table, so the CPU is doing less work, and also > instruction cache locality should be improved. Is my understanding correct? > > (this is probably a bit too broad a topic for this forum/tends to end > up in heated discussions, etc, but let's see how we go) > > I believe one of the main reasons your understanding there might be > incorrect: Not every function returns an error code. But essentially > every function can throw (yes, you can annotate them - but it's one of > those cases where C++ may've got the defaults wrong (like const, etc) > and it's impractical/unlikely someone's going to effectively maintain > noexcept on most of their codebase (and there are performance problems > with noexcept - since it's not UB to throw from such a function, it's > guaranteed to call terminate, so you have to generate exception > handling code in every noexcept function)) > > > So my question is: > > (1) Is the argument that 'exception hurts compiler optimization, and > should not be used in perf-sensitive code' outdated now? > > I don't believe so - having every function call being potentially > control flow changing inhibits certain optimizations that would > otherwise be possible (certain code motion, reordering, etc can't be > done) compared to a relatively smaller number of explicit > error-handling calls. > > > Or more generally, what are the pros and cons of using exception vs > error code, from a LLVM backend developer's perspective? > > Exceptions inhibit code motion, but allow for smaller code/keeping the > cold path/handling further away. I think that's basically the > mechanical tradeoff (setting aside issues of readability, > maintainability, etc - having an explicit keyword/notation on calls > that can throw could help a lot from a maintainability perspective, > for instance) > > > (2) In current LLVM design, is there still any optimization that is > prevented by using exceptions? Or is the current LLVM already able to > provide the same optimization quality for code using exceptions, compared > with code that does not? > > I believe that exceptions do still hinder optimizations when compared > to code that doesn't throw or use a return code*. While that may seem > like an unfair comparison, it's my understanding that it's part of the > excessive cost of exceptions. Not to mention how expensive they can be > when they are thrown, compared to explicit error handling. > > * Note this isn't a case of missed optimization opportunities (well, > perhaps in LTO it's a missed opportunity - if we did whole program > analysis to prove certain call trees never throw in practice, then we > could propagate that information and improve optimizations) - but a > limitation of exceptions. > > - Dave > _______________________________________________ > LLVM Developers mailing list > llvm-dev at lists.llvm.org > https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev >-------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20200813/a56a4f4f/attachment.html>