DEV Community

loading...
Cover image for The real cost of exception handling in C++ with benchmark

The real cost of exception handling in C++ with benchmark

Vishal Chovatiya
Software Developer⌨, Fitness Freak🏋, Hipster🕴, Blogger👨‍💻, Productivity Hacker⌚, Technical Writer✍️, Tech talker👨‍🎤, Leader👨‍🔬, Always a Student👨‍🎓, Incomplete🔍 & Learning Junkie📚.
Updated on ・2 min read

Despite many benefits, most people still do not prefer to use exceptions due to its overhead. So let's understand the overheads with benchmark code:

static void without_exception(benchmark::State &state){
    for (auto _ : state){
        std::vector<uint32_t> v(10000);
        for (uint32_t i = 0; i < 10000; i++) v.at(i) = i;        
    }
}
BENCHMARK(without_exception);//----------------------------------------

static void with_exception(benchmark::State &state){
    for (auto _ : state){
        std::vector<uint32_t> v(10000);
        for (uint32_t i = 0; i < 10000; i++){
            try{
                v.at(i) = i;
            }
            catch (const std::out_of_range &oor){}
        }
    }
}
BENCHMARK(with_exception);//--------------------------------------------

static void throwing_exception(benchmark::State &state){
    for (auto _ : state){
        std::vector<uint32_t> v(10000);
        for (uint32_t i = 1; i < 10001; i++){
            try{
                v.at(i) = i;
            }
            catch (const std::out_of_range &oor){}
        }
    }
}
BENCHMARK(throwing_exception);//-----------------------------------------
Enter fullscreen mode Exit fullscreen mode
  • As you can see above, with_exception & without_exception has only a single difference i.e. exception syntax. But none of them throws any exceptions.
  • While throwing_exception does the same task except it throws an exception of type std::out_of_range in the last iteration.
  • As you can see in below bar graph, the last bar is slightly high as compared to the previous two which shows the cost of throwing an exception.
  • But the cost of using exception is zero here, as the previous two bars are identical.

Slice from 7 best practices for exception handling in Modern C++.

  • I am not considering the optimization here which is the separate case as it trims some of the assembly instructions completely. Also, implementation of compiler & ABI plays a crucial role. But still, it is far better than losing time by setting up a guard(if(error) strategy) and explicitly checking for the presence of error everywhere.
  • While in case of exception, the compiler generates a side table that maps any point that may throw an exception (program counter) to the list of handlers. When an exception is thrown, this list is consulted to pick the right handler (if any) and the stack is unwound.
  • By the way, I am using a Google Benchmark, if you want to explore more.

The real cost of exception handling in C++ with benchmark

  • First and foremost, remember that using try and catch doesn't actually decrease performance unless an exception is thrown.
  • It's "zero cost" exception handling – no instruction related to exception handling is executed until one is thrown.
  • But, at the same time, it contributes to the size of executable due to unwinding routines, which may be important to consider for embedded systems.

TL;DR
No instruction related to exception handling is executed until one is thrown so using try/catch doesn’t actually decrease performance.

What's next?

Still not convinced of using exceptions?

You can try out tweaking same code provided above & check performance online with this tool. Don't forget to consider clang/GCC compilers, disassembly & optimization flags also while playing with it.

Wants to learn about exception handling in more detail?

Design your own tiny ABI for exception handling in C++.

Wants to see what Modern C++ offers in terms of exception safety?

Stay tuned here... :-).

Discussion (1)

Collapse
michelcpp profile image
michel-cpp

"optim = None" lol ??