Search code examples
c++cperformance

Are there any examples of C code which leads to (significant) performance decrease / increase after being compiled as C++ code?


This question may be asked before.

Are there any examples of C code which leads to (significant) performance decrease / increase after being compiled as C++ code?


UPD1: It's interesting to know about performance increase as well. Hence, added "increase".

UPD2. May be useful: Can code that is valid in both C and C++ produce different behavior when compiled in each language?

UPD3. The "UnspecB/UB/IB aspect" is interesting. However, more interesting is perhaps to limit C code to be a strictly conforming C program (C11, 4p5), which is at the same time an UnspecB/UB/IB-free well-formed C++ program that 1) contains no violations of the rules in Clause 5 through Clause 33 and Annex D (N4917, 4.1.1p2.1), and 2) has the same (abstract) semantics.


Solution

  • Consider this code, which doesn't make a lot of sense, but is easy to imagine more complicated code that does make sense and does something similar.

    union
    {
        long a;
        short b[sizeof(long)/sizeof(short)];
    } foo;
    
    short* ptr;
    
    extern void work();
    
    void func()
    {
       foo.a = 1;                   // 1
       *ptr = 42;                   // 2
       if (foo.a == 1) work();      // 3
    }
    

    Assume that the rest of the code is UB-free and has the same meaning in either language.

    When compiled as C++, func is going to be marginally faster at least with some implementations. Live demo. As you can see, when compiled as C++ it has no conditional jumps, while when compiled as C it does have one.

    The reason is that different things have undefined behaviour in the two languages.

    In C++, if ptr points to foo.b, the program has UB, because it would be illegal to modify one member of a union and then inspect another member. So the compiler assumes that ptr does not point to foo.b and the value of foo.adoes not change at line 2, thus it still has the same value it had at line 1, this there is no need to check it at line 3.

    On the other hand, in C, if ptr points to foo.b, the behaviour is perfectly (implementation-)defined. foo.a can change at line 2, and the compiler has no idea whether it does indeed change or not. Thus there is a need to perform the check at line 3. In reality the value of foo.a won't change, because we know that the program is valid C++ as well as valid C, but the compiler cannot know that.