Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does the (gcc) compiler optimize away empty-body functions?

Using policy based design, an EncapsulatedAlgorithm:

template< typename Policy>
class EncapsulatedAlgorithm : public Policy
{
    double x = 0; 

    public: 
        using Policy::subCalculate; 

        void calculate()
        {
            Policy::subCalculate(x); 
        }
    protected:
        ~EncapsulatedAlgorithm() = default;
};

may have a policy Policy that performs a sub-calculation. The sub-calculation is not necessary for the algorithm: it can be used in some cases to speed up algorithm convergence. So, to model that, let's say there are three policies.

One that just "logs" something:

struct log
{
    static void subCalculate(double& x)
    {
        std::cout << "Doing the calculation" << endl; 
    }
};

one that calculates:

struct calculate
{
    static void subCalculate(double& x)
    {
        x = x * x; 
    }
};

and one to bring them all and in the darkness bind them :D - that does absolutely nothing:

struct doNothing
{
    static void subCalculate(double& x)
    {
        // Do nothing. 
    }
};

Here is the example program:

typedef EncapsulatedAlgorithm<doNothing> nothingDone; 
typedef EncapsulatedAlgorithm<calculate> calculationDone; 
typedef EncapsulatedAlgorithm<loggedCalculation>  calculationLogged; 

int main(int argc, const char *argv[])
{
    nothingDone n; 
    n.calculate(); 

    calculationDone c; 
    c.calculate();

    calculationLogged l; 
    l.calculate(); 

    return 0;
}

And here is the live example. I tried examining the assembly code produced by gcc with the optimization turned on:

g++ -S -O3 -std=c++11 main.cpp

but I do not know enough about Assembly to interpret the result with certainty - the resulting file was tiny and I was unable to recognize the function calls, because the code of the static functions of all policies was inlined.

What I could see is that when no optimization is set for the, within the main function, there is a call and a subsequent leave related to the 'doNothing::subCalculate'

call    _ZN9doNothing12subCalculateERd
leave

Here are my questions:

  1. Where do I start to learn in order to be able to read what g++ -S spews out?
  2. Is the empty function optimized away or not and where in main.s are those lines?
  3. Is this design O.K.? Usually, implementing a function that does nothing is a bad thing, as the interface is saying something completely different (subCalculate instead of doNothing), but in the case of policies, the policy name clearly states that the function will not do anything. Otherwise I need to do type traits stuff like enable_if, etc, just to exclude a single function call.
like image 421
tmaric Avatar asked Feb 20 '14 16:02

tmaric


2 Answers

I went to http://assembly.ynh.io/, which shows assembly output. I

template< typename Policy>
struct EncapsulatedAlgorithm : public Policy
{
        void calculate(double& x)
        {
            Policy::subCalculate(x); 
        }
};
struct doNothing
{
    static void subCalculate(double& x)
    {
    }
};
void func(double& x) {
   EncapsulatedAlgorithm<doNothing> a;
   a.calculate(x);
}

and got these results:

            .Ltext0:
                .globl  _Z4funcRd 
            _Z4funcRd:
            .LFB2:
                .cfi_startproc    #void func(double& x) {
            .LVL0:
0000 F3             rep           #not sure what this is
0001 C3             ret           #}
                .cfi_endproc
            .LFE2:
            .Letext0:

Well, I only see two opcodes in the assembly there. rep (no idea what that is) and end function. It appears that the G++ compiler can easily optimize out the function bodies.

like image 86
Mooing Duck Avatar answered Oct 29 '22 17:10

Mooing Duck


Where do I start to learn in order to be able to read what g++ -S spews out?

This site's not for recommending reading material. Google "x86 assembly language".

Is the empty function optimized away or not and where in main.s are those lines?

It will have been when the optimiser was enabled, so there won't be any lines in the generated .S. You've already found the call in the unoptimised output....

In fact, even the policy that's meant to do a multiplication may be removed as the compiler should be able to work out you're not using the resultant value. Add code to print the value of x, and seed x from some value that can't be known at compile time (it's often convenient to use argc in a little experimental program like this, then you'll be forcing the compiler to at least leave in the functionally significant code.

Is this design O.K.?

That depends on a lot of things (like whether you want to use templates given the implementation needs to be exposed in the header file, whether you want to deal with having distinct types for every instantiation...), but you're implementing the design correctly.

Usually, implementing a function that does nothing is a bad thing, as the interface is saying something completely different (subCalculate instead of doNothing), but in the case of policies, the policy name clearly states that the function will not do anything. Otherwise I need to do type traits stuff like enable_if, etc, just to exclude a single function call.

You may want to carefully consider your function names... do_any_necessary_calculations(), ensure_exclusivity() instead of lock_mutex(), after_each_value() instead of print_breaks etc..

like image 34
Tony Delroy Avatar answered Oct 29 '22 17:10

Tony Delroy