Well, it's your benchmark that's broken: It does not actually throw, so you only see the non-exception path. This is quite bad as the optimizer can prove that you don't throw, so it can throw away all code that actually handles performing cleanup with an exception in flight.
I think, you should repeat your benchmark, putting a call to exceptionThrower()
or nonthrowingThrower()
into your try{}
block. These two functions should be compiled as a separate translation unit, and only linked together with the benchmark code. That will force the compiler to actually generate exception handling code irrespective of whether you call exceptionThrower()
or nonthrowingThrower()
. (Make sure that you don't switch on link time optimizations, that could spoil the effect.)
This will also allow you to easily compare the performance impacts between the exception and the non-throwing execution paths.
Apart from the benchmark issues, exceptions in C++ are slow. You'll never get hundreds of millions of exceptions thrown within a second. It's more around single digit millions at best, likely less. I expect that any performance differences between different finally
implementations are entirely irrelevant in the throwing case. What you can optimize is the non-throwing path, where your cost is simply the construction/destruction of your finally
implementation object, whatever that is.