Previous section   Next section

Imperfect C++ Practical Solutions for Real-Life Programming
By Matthew Wilson
Table of Contents
Chapter 12.  Optimization


12.3. Empty Base Optimization

The Empty Base Optimisation (EBO) is an optimization that compilers can implement to enable a class derived from an empty base—for example, class EmptyBase {};—to occupy no extra space for the base, which makes sense, since it's empty. In other words, the address of the derived part and base part for a given instance is the same. This can be useful when base classes are used for providing member types, as with the standard library unary_function and binary_function template classes,[2] and also when one is providing policy-based implementation reuse.

[2] It is customary to derive your functors (see sections 20.2 and 34.3) from these in order that certain necessary member types are available to standard library algorithm adaptors [Aust1999, Muss2001].

The canonical form of EBO is shown thus:



class EmptyBase


{};





class Child


  : EmptyBase


{};





// Child should be same size as any empty class, e.g. EmptyBase


STATIC_ASSERT(sizeof(Child) == sizeof(EmptyBase));  // form 1



But this form (which we'll call EBO-1) is scarcely the full picture of the situations in which the optimization can be applied. There are seven forms I can think of. Four of these, involving single inheritance, are shown in Figure 12.1, where the circles represent empty classes, and the squares represent classes with members. P stands for parent, C for child, and G for grandchild.

Figure 12.1. Single inheritance EBO relationships.


The second form (EBO-2) is identical to the first, except that the child has one or more member variables, and is, therefore itself of non-zero size. The third (EBO-3) and fourth (EBO-4) forms determine whether the optimization is carried down to children of the optimized derived class. To be honest, it would be hard to conceive of an implementation that would not carry down the optimization, but we constantly learn that implementation-defined behavior leads to surprises.

The last three forms, shown in Figure 12.2, evaluate the application of the optimization to multiple inheritance situations. This is important, since many modern template techniques rely on being able to inherit from a primary nontrivial type, and to inherit additional mixin[3] classes that merely supply type or policies.

[3] One of the nicest bits of C++ terminology. It stems from the term for cookies, cheesecake pieces, and other goodies mixed into deliciously stodgy gourmet ice creams.

Figure 12.2. Multiple-inheritance EBO relationships.


Form five (EBO-5) is where an empty child derives from two empty base classes. Form six (EBO-6) is the same, except that the child is nonempty. Form seven (EBO-7) involves derivation from one empty base class, and one nonempty class; this is much like the situation with some containers implementations that inherit (private or protected) from an implementation class, and also inherit from an empty allocator mixin class.

Table 12.3 shows the support for a range of Win32 compilers. The results show that most of the compilers support all forms, which is good to know. However, the only forms fully supported by all compilers are the simplistic EBO-1 and EBO-3. Since it's a good bet that most classes for which we would want to take advantage of EBO will have non-zero size themselves, this is not terribly useful.

Table 12.3. Support for EBO forms. # indicates support.

Compiler

EBO-1

EBO-2

EBO-3

EBO-4

EBO-5

EBO-6

EBO-7

Borland (5.6)

#

–Ve –Vl

#

–Ve –Vl

  

–Ve –Vl

CodeWarrior

#

#

#

#

#

 

#

CodePlay

#

#

#

#

#

#

#

Comeau

#

#

#

#

#

#

#

Digital Mars

#

#

#

#

#

#

#

GCC

#

#

#

#

#

#

#

Intel

#

#

#

#

#

 

#

Visual C++

#

#

#

#

#

 

#

Watcom

#

#

#

#

#

 

#


The only compiler that has a problem with any of the single inheritance forms is Borland 5.6, which can be made to behave by specifying the nondefault flags –Ve and –Vl, which stipulate zero-length base classes and "old Borland class layout," respectively. I'm not sure how usable this will be in a production scenario; we can hope that the next version of the Borland compiler will catch up with the rest.

Anyway, I think that's enough Borland bashing. The only case that presents any kind of problems for any of the other compilers is the case where we have two empty bases from which a nonempty base is derived. Although not the most common inheritance layout, the increasing use of policy-based template parameterization mixed with inheritance means that this is a conceivable inheritance relationship.

Imperfection: The support for EBO flavors is not universal, and varies significantly between compilers.


Sure, it's not an imperfection of the C++ language itself, since it's a difference between implementations of an optional optimization. But it does impact the performance of your code, sometimes quite dramatically (see section 32.2.5). So what do we do about it? In one sense, there's very little that can be done. Obviously an important measure is to pick your compiler. Most modern compilers support the optimization in most or all its guises. As for Borland, it's possible that by the time you reach this they will have released their new compiler (to accompany their new C++BuilderX product) and that it implements this optimization.

Second, if you must have broad compilation compatibility, try to use techniques that do not rely on EBO, and learn to live with the space hit when you're using a compiler that does not support it.

There's one slightly bogus trick you can use when the EBO you require is for standard library allocators, which we'll look at in section 32.2.5.


      Previous section   Next section