Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Exception handling in the same function slows compile times by > 2x, why?

I have a several-thousand-line project with a gigantic main (~800 lines).
The file containing the main function takes 7.94 seconds to compile.

The code is structured like this:

int main(int argc, char *argv[])
{
    int result = 0;
    try
    {
        /* 800 lines of code here */
    }
    catch (std::invalid_argument const &ex)
    {
        std::cerr << ex.what() << std::endl;
        return EINVAL;
    }
    catch (std::runtime_error const &ex)
    {
        std::cerr << ex.what() << std::endl;
        return -1;
    }
    return 0;
}

However, when I simply change it to

void run(int argc, char *argv[])
{
    /* 800 lines of code here */
}

int main(int argc, char *argv[])
{
    int result = 0;
    try
    {
        run(argc, argv);
    }
    catch (std::invalid_argument const &ex)
    {
        std::cerr << ex.what() << std::endl;
        return EINVAL;
    }
    catch (std::runtime_error const &ex)
    {
        std::cerr << ex.what() << std::endl;
        return -1;
    }
    return 0;
}

The compile time reduces to 2.48 seconds!

I can tell the culprit is the exception-handling code, because when I remove the surrounding try/catch, I get the same compile time reduction.

Furthermore, if I mark the run function as __forceinline, the compile time increases to 10.02! But if I do this after taking out the try/catch, then it goes down to a mere 3.27 seconds.

But what gives? What exactly does the compiler have to do that becomes so much more computationally intensive when the code is directly inside the body of the try block?

Notes:

  • I'm compiling in RELEASE mode
  • Microsoft Visual C++ Nov 2013 CTP compiler (native x64)
  • Relevant compiler options: /O2 /Gm- /GS /EHsc (removing /EHsc also speeds up compilation)
like image 316
user541686 Avatar asked Jul 21 '14 00:07

user541686


1 Answers

I suspect the difference has to do with extra cleanup code. C++ objects declared in a function are destroyed upon leaving it, so their destruction code is already at the function epilogue and (I think) stack unwinding - part of the exception handling process - can make use of that code. If you need to destroy all these objects without leaving the function - there's extra destruction code to be generated and managed, which can impact both build time and binary size. Can you say if there's a difference in binary size?

Although frankly I'm surprised either impacts (time/size) is measurable. Are the '800 lines' exceptionally rich in C++ object creation? (perhaps indirectly)

like image 129
Ofek Shilon Avatar answered Sep 23 '22 03:09

Ofek Shilon