Which causes more code-bloat: inline virtual or template class?

Discussion in 'C++' started by RainBow, Aug 25, 2005.

  1. RainBow

    RainBow Guest

    Greetings!!

    I introduced the so-called "thin-template" pattern for controlling the
    code bloat caused due to template usage.

    However, one of the functions in the template happens to be virtual as
    well. To support thin-template, I need to make virtual function as
    inline.

    Now, I know that compiler would generate an out-of-line copy when it
    takes the address of my inline virtual fucntion, thus making _two_
    copies of this function : one out-of-line to be used by VTABLE, and one
    inline for genuine inline substituions.

    Doubts:

    1. Is declaring a virtual function as inline a good design? Does it
    indicate their is some flaw in my design? I have not seen much code
    that makes a virtual function as inline too, and hence this doubt.

    2. As a rule of thumb, who should cause more code bloat : normal
    template or out-of-line function? I wanna know this /cuz if template
    cuase more code bloat, I wanna keep my virtual as inline. ELse I remove
    thin template and make my virtual as non-inline.


    The below code should give some idea as to what I am doing:

    class Base
    {
    public:
    virtual int DoSomething() = 0;
    protected:
    Base();
    };

    class IntermediateBase : public Base
    {
    protected:
    IntermediateBase(void* aSomeParam, void* aArg) :
    iSomeParam(aSomeParam), iArgs(aArg) {}
    virtual int DoSomething() = 0;
    protected:
    void* iSomeParam;
    void* iArgs;
    };


    template <class TYPE, class INPUT>
    class ConcreteClass : public IntermediateBase
    {
    typedef int (TYPE::*MemberFuncPtr)(const INPUT&);
    public:
    inline ConcreteClass(TYPE& aCommandType, INPUT& aArgumentsToCommand,
    MemberFuncPtr aMFP)
    : IntermediateBase( static_cast<TYPE*>(&aCommandType),
    static_cast<INPUT*>(&aArgumentsToCommand) ),
    iMFP(aMFP)
    {
    }

    inline virtual int DoSomething() // VIRTUAL AND LINE - THIS CODE
    SMELLS ROT?
    {
    return ( static_cast<TYPE*>(iSomeParam)->*ConcreteClass::iMFP )(
    *(static_cast<INPUT*>(iArgs)) );
    }
    private:
    MemberFuncPtr iMFP;
    };
     
    RainBow, Aug 25, 2005
    #1
    1. Advertising

  2. RainBow wrote:
    > I introduced the so-called "thin-template" pattern for controlling the
    > code bloat caused due to template usage.


    Could you maybe share your findings how much bloat you manage to control
    and what problems eliminating the bloat helps to avoid? Thanks!

    > [..]


    V
     
    Victor Bazarov, Aug 25, 2005
    #2
    1. Advertising

  3. RainBow wrote:

    > I introduced the so-called "thin-template" pattern
    > for controlling the code bloat caused due to template usage.
    >
    > However,
    > one of the functions in the template happens to be virtual as well.
    > To support thin-template, I need to make virtual function as inline.
    >
    > Now, I know that compiler would generate an out-of-line copy
    > when it takes the address of my inline virtual fucntion,
    > thus making _two_ copies of this function:
    > one out-of-line to be used by VTABLE and
    > one inline for genuine inline substituions.
    >
    > Doubts:
    >
    > 1. Is declaring a virtual function as inline a good design?


    Yes.

    > Does it indicate [that there] is some flaw in my design?
    > I have not seen much code that makes a virtual function as inline too
    > and hence this doubt.


    This is improbable.
    If you apply a virtual function to an object
    in the scope where it was created,
    the compiler knows the actual type of the object
    and a good optimizing compiler will inline the virtual function.

    > 2. As a rule of thumb, who should cause more code bloat:
    > normal template or out-of-line function?
    > I wanna know this because, if template cuase more code bloat,
    > I wanna keep my virtual as inline. Otherwise,
    > I remove thin template and make my virtual as non-inline.
    >
    > The below code should give some idea as to what I am doing:
    >
    > class Base {
    > protected:
    > Base(void);
    > public:
    > virtual int DoSomething(void) = 0;
    > };
    >
    > class IntermediateBase: public Base {
    > protected:
    > void* iSomeParam;
    > void* iArgs;
    > IntermediateBase(void* aSomeParam, void* aArg):
    > iSomeParam(aSomeParam), iArgs(aArg) { }
    > virtual
    > int DoSomething(void) = 0;
    > };
    >
    > template <class TYPE, class INPUT>
    > class ConcreteClass: public IntermediateBase {
    > private:
    > typedef int (TYPE::*MemberFuncPtr)(const INPUT&);
    > MemberFuncPtr iMFP;
    > public:
    > ConcreteClass(TYPE& aCommandType,
    > INPUT& aArgumentsToCommand, MemberFuncPtr aMFP):
    > IntermediateBase(static_cast<TYPE*>(&aCommandType),
    > static_cast<INPUT*>(&aArgumentsToCommand)), iMFP(aMFP) { }
    >
    > virtual
    > int DoSomething(void) { // VIRTUAL AND LINE - THIS CODE SMELLS ROT?
    > return (static_cast<TYPE*>(iSomeParam)->*ConcreteClass::iMFP)
    > (*(static_cast<INPUT*>(iArgs)));
    > }
    > };


    inline functions do *not* cause code bloat.
    They exist simply
    to discourage programmers who are concerned about performance
    from inlining code manually.

    I *always* define inline functions (and operators)
    and I let the optimizing compiler decide
    whether to actually inline them or not.

    inline functions can increase compile time substantially.
    You might consider taking advantage of both
    inline *and* external function definitions:

    > cat file.h

    #ifndef GUARD_FILE_H
    #define GUARD_FILE_H 1

    #ifdef EFINE_INLINE
    inline
    double f(double x) {
    return x*(x + 2.0) + 1.0;
    }
    #else //EFINE_INLINE
    double f(double x);
    #endif//EFINE_INLINE
    #endif//GUARD_FILE_H

    > cat file.cc

    #undef EFINE_INLINE
    #include "file.h"

    double f(double x) {
    return x*(x + 2.0) + 1.0;
    }

    > g++ -DEFINE_INLINE -Wall -ansi -pedantic -O3 -c file.cc
    > nm --demangle file.o

    00000000 T f(double)

    This allows your inline and external function definitions
    to coexist peacefully.
    Use the -DEFINE_INLINE option only after you have finished
    testing and debugging all of your code.
    This will speed up the program development cycle
    and allow you to optimize your code just before deployment.
     
    E. Robert Tisdale, Aug 25, 2005
    #3
  4. RainBow

    RainBow Guest

    Please note: when I say "I introduced" - I did not mean I invented
    it/discovered it. I learnt this pattern when working on Symbian. If you
    google for "thin template", one of the first 5 search results should
    take you to Symbian site.

    Also, I am sorry, but I am _not_ doing this r&d currently - I just want
    a tentative answer for this. I know that the answer depends how often
    are you calling the Execute() call from what all compilation units -
    and I forgot to mention that in my earlier question - its just that
    this inline function will be called from atmost 2 or 3 compilation
    units. So is the case with template instantiation (okay, its
    obvious...). :)

    I will surely post my results in this group as and when I do that
    activity - but right now, I just want a rough, tentative answer so that
    I can finalise my design and proceed.

    Thanks,
    -Viren
     
    RainBow, Aug 25, 2005
    #4
  5. RainBow

    Greg Guest

    RainBow wrote:
    > Greetings!!
    >
    > I introduced the so-called "thin-template" pattern for controlling the
    > code bloat caused due to template usage.
    >
    > However, one of the functions in the template happens to be virtual as
    > well. To support thin-template, I need to make virtual function as
    > inline.
    >
    > Now, I know that compiler would generate an out-of-line copy when it
    > takes the address of my inline virtual fucntion, thus making _two_
    > copies of this function : one out-of-line to be used by VTABLE, and one
    > inline for genuine inline substituions.
    >
    > Doubts:
    >
    > 1. Is declaring a virtual function as inline a good design? Does it
    > indicate their is some flaw in my design? I have not seen much code
    > that makes a virtual function as inline too, and hence this doubt.


    I wouldn't say that declaring a function inline is part of a "design"
    really - I suppose it could be called a "practice". And one that is
    unlikely to make much of a difference to a virtual class method. To
    inline a virtual function call, the exact type of the object involved
    in the call must be unvarying and be known at compile time. Needless to
    say, there are few occasions where a polymorphic reference can be only
    one type, and the compiler can tell what its type must be. After all,
    the whole point of polymorphism is that an object's type is determined
    at runtime, based on the operating state of the program.

    > 2. As a rule of thumb, who should cause more code bloat : normal
    > template or out-of-line function? I wanna know this /cuz if template
    > cuase more code bloat, I wanna keep my virtual as inline. ELse I remove
    > thin template and make my virtual as non-inline.
    >
    >
    > The below code should give some idea as to what I am doing:
    >
    > class Base
    > {
    > public:
    > virtual int DoSomething() = 0;
    > protected:
    > Base();
    > };
    >
    > class IntermediateBase : public Base
    > {
    > protected:
    > IntermediateBase(void* aSomeParam, void* aArg) :
    > iSomeParam(aSomeParam), iArgs(aArg) {}
    > virtual int DoSomething() = 0;
    > protected:
    > void* iSomeParam;
    > void* iArgs;
    > };
    >
    >
    > template <class TYPE, class INPUT>
    > class ConcreteClass : public IntermediateBase
    > {
    > typedef int (TYPE::*MemberFuncPtr)(const INPUT&);
    > public:
    > inline ConcreteClass(TYPE& aCommandType, INPUT& aArgumentsToCommand,
    > MemberFuncPtr aMFP)
    > : IntermediateBase( static_cast<TYPE*>(&aCommandType),
    > static_cast<INPUT*>(&aArgumentsToCommand) ),
    > iMFP(aMFP)
    > {
    > }
    >
    > inline virtual int DoSomething() // VIRTUAL AND LINE - THIS CODE
    > SMELLS ROT?
    > {
    > return ( static_cast<TYPE*>(iSomeParam)->*ConcreteClass::iMFP )(
    > *(static_cast<INPUT*>(iArgs)) );
    > }
    > private:
    > MemberFuncPtr iMFP;
    > };


    Template code "bloat" is measured not by the total amount of code
    generated from instantiating templates (which can be considerable) but
    rather by the amount of duplicated code from instantiating templates
    with interchangeable types (from the template's perspective). For
    instance if a class template accepts a pointer type as its type
    parameter, but does nothing special depending on the type being pointed
    to, then instantiating the template with a series of different pointer
    types will "bloat" the program with essentially duplicated code. Some
    compilers can detect duplicated code and "fold" the code into a single
    set of instructions (leaving stubs for the "folded" routines since
    template functions must each retain a unique address). Of course if the
    compiler folds duplicate code then there is less incentive for the
    programmer to avoid template bloat at the source code level.

    In the code above, there seems to be little opportunity for template
    bloat, since the class template seems quite type-specific. Furthermore
    any optimization seems premature at this point, particularly if it
    complicates the implementation.

    I would instead write the program using the best design available and
    then test the program first for correctness, and then for performance.
    If performance (measured by the speed or the size of the program) turns
    out to be inadequate at that point, then it is a good idea to start
    looking for optimizations. The advantage of this approach is in that
    case that the program turns out to be small and fast enough using the
    optimal design, then no time would have been wasted making unneeded
    optimizations that would have comprised the design. And in the case
    that the program turns out not to b small or fast enough, then any
    optimizations that are made start with a working program - meaning that
    any bugs caused by subsequent changes will be much easier to track
    down.

    Greg
     
    Greg, Aug 25, 2005
    #5
  6. RainBow

    RainBow Guest

    Thank you very much Greg for that beautiful information. Surely, I am
    thinking about optimisation even before a single line of code is
    written - while we are still in the design phase. I think I have been
    too much scared of code size (at binary level of course) of a C++
    program versus a C program. In the past, I was criticised for writing
    too many classes when perhaps a few C functions would have done the
    trick - keeping the program size extremely small (albeit at the cost of
    increasing the maintenance efforts).

    This time, I did not want to take any chances and be careful about the
    estiamted program size right from beginning and hence these (silly??)
    concerns. :) But your posting has opened my eyes and I realise what a
    bad approach I had been taking.

    Thank you very much again.

    Have a great day ahead!!

    --V
     
    RainBow, Aug 25, 2005
    #6
  7. RainBow

    Greg Guest

    RainBow wrote:
    > Thank you very much Greg for that beautiful information. Surely, I am
    > thinking about optimisation even before a single line of code is
    > written - while we are still in the design phase. I think I have been
    > too much scared of code size (at binary level of course) of a C++
    > program versus a C program. In the past, I was criticised for writing
    > too many classes when perhaps a few C functions would have done the
    > trick - keeping the program size extremely small (albeit at the cost of
    > increasing the maintenance efforts).
    >
    > This time, I did not want to take any chances and be careful about the
    > estiamted program size right from beginning and hence these (silly??)
    > concerns. :) But your posting has opened my eyes and I realise what a
    > bad approach I had been taking.
    >
    > Thank you very much again.
    >
    > Have a great day ahead!!
    >
    > --V


    Presumably you could have the linker generate a link map for the
    program you want to make smaller. A link map would tell you how how
    many functions were compiled into the binary and the size of each one.
    Then you would only have to work to make the functions either smaller
    or fewer or both.

    I think that would be a more effective approach than asking the readers
    of this newgroup which functions in your program to target.

    Greg
     
    Greg, Aug 26, 2005
    #7
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Salvador I. Ducros

    STL & reducing code bloat

    Salvador I. Ducros, Jul 28, 2003, in forum: C++
    Replies:
    5
    Views:
    1,586
    Howard Hinnant
    Aug 5, 2003
  2. Steven T. Hatton

    Exceptions and object code bloat

    Steven T. Hatton, Nov 27, 2006, in forum: C++
    Replies:
    5
    Views:
    364
    =?iso-8859-1?q?Kirit_S=E6lensminde?=
    Nov 28, 2006
  3. Tony
    Replies:
    48
    Views:
    1,155
    Mathias Gaunard
    Dec 12, 2006
  4. BGB / cr88192

    hmm: code bloat?...

    BGB / cr88192, Jan 5, 2010, in forum: C Programming
    Replies:
    16
    Views:
    603
    BGB / cr88192
    Jan 6, 2010
  5. Lawrence D'Oliveiro

    Managed-Code Bloat

    Lawrence D'Oliveiro, Jun 6, 2011, in forum: Java
    Replies:
    37
    Views:
    1,030
Loading...

Share This Page