Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ Exception Throw/Catch Optimizations

It seems to me that if you have some C++ code like this:

int f()
{
  try {
    if( do_it() != success ) {
      throw do_it_failure();
    }
  } catch( const std::exception &e ) {
    show_error( e.what() );
  }
}

The C++ compiler should be able to optimize the throw and catch into almost a simple goto.

However, it seems to me from my experience viewing disassembly and stepping through code that the compilers always jump through the very messy exception handling libraries.

Why do they do that? Is there some language requirement that prevents optimizing? What if it was:

int f()
{
  try { throw std::runtime_error("Boo!"); }
  catch ( const std::exception &e ) { std::cout << e.what() << std::endl; }
}

Why does the compiler not just rewrite that as

int f()
{
  std::cout << "Boo!" << std::endl;
}
like image 386
Zan Lynx Avatar asked Jul 22 '10 17:07

Zan Lynx


3 Answers

I think the accepted answer is quite uninformative if not wrong, so even after so many years I feel the need to offer a proper answer.

Speculating on why the compiler implementors chose to not put effort on any particular feature is just, well... speculation. The fact that exceptions are thrown only in exceptional circumstances is not generally felt as a reason to not optimize the performance of such code. On the contrary, even though it is true that throwing code is not optimized at the expense of non-throwing code, the exception throwing and handling infrastructure is nevertheless optimized very carefully.

Furthermore, that piece of code might feel so contrived that is not worth considering, but this is not true: it may result from inlining and optimizing of much more complex code, and optimizing it could result into simpler code that allows other optimization passes to fire, or the containing function to be further inlined. Optimization passes like these, when correct and efficient to implement, are always worth of at least being considered, no matter how contrived the original piece of code might look. Otherwise, even fundamental passes like dead code elimination would be avoided because "dead code should not be written in the first place". Which is obviously not the case.

Hence I just do not agree with the accepted answer. The fact that exceptions should be thrown exceptionally is not the reason why this code is not optimized.

The reason is purely technical, and is explained in this email from the clang development mailing list: http://lists.llvm.org/pipermail/cfe-dev/2015-March/042035.html

To summarize, the language allows code called inside the catch block to rethrow the exception in any point "without ever having seen" the exception object:

void g() { throw; }

Hence, consider the OP code:

int f()
{
  try { throw std::runtime_error("Boo!"); }
  catch ( const std::exception &e ) { std::cout << e.what() << std::endl; }
}

For what the compiler is concerned, e.what(), or the two invocations of operator<<, may rethrow the exception, hence optimizing away the exception handling code would break the semantics of the program.

Ensuring this is not the case would require "whole-program knowledge", as written in the above email message. Even simpler cases could be optimized, such as:

int func() {
  try {
    throw 42;
  }catch(int x) {
    return x;
  }
}

The above code can be transformed into return 42. There are no technical reasons that impede it.

Still, most common compilers do not do it (godbolt). This time, we can tell from an actual source, the email linked above, that Clang developers (we cannot say anything for other compilers) do not think this optimization to be worth, probably because it would only apply to catch blocks that do not do function calls.

Anyway, the message says nothing about whether they would accept a patch to do so.

like image 93
gigabytes Avatar answered Nov 17 '22 01:11

gigabytes


Because do_it() could throw a different exception, before you throw do_it_failure();

As for your second example, a compiler could do it, but it would have to be treated as a special case, so why bother for such a pathological case?

like image 42
James Curran Avatar answered Nov 17 '22 00:11

James Curran


Why do they do that?

Because C++ exception are for, well, exceptional circumstances and performance under exceptional circumstances doesn't really matter.

C++' exceptions were designed with that in mind, making sure compiler vendors could deliver near-optimal performance in the common case of no exceptions being thrown, at the cost of worse-than-possible performance in the odd case when exceptions are thrown.

From the very beginning users were encouraged to use exceptions only under exceptional circumstances, and implementers were encouraged to optimize the no-exception case (destructor addresses must be stored somewhere in order to call destructors when an exception comes by) at the cost of the exceptional case.
And while implementers could certainly spend resources at also optimizing the odd exceptional case, most users wouldn't like that, since there's always so much more important things that need improvment.

like image 5
sbi Avatar answered Nov 17 '22 00:11

sbi