Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

performance implications of deep inheritance tree in c++

Is there any efficiency disadvantage associated with deep inheritance trees (in c++), i.e, a large set of classes A, B, C, and so on, such that B extends A, C extends B, and so one. One efficiency implication that I can think of is, that when we instantiate the bottom most class, say C, then the constructors of B and A are also called, which will have performance implications.

like image 442
SegFault Avatar asked Nov 05 '11 06:11

SegFault


People also ask

What are some potential efficiency disadvantages of having very shallow inheritance trees?

The only efficiency problem for a shallow inheritance tree that I can see is that one does not take advantage of the benefits on an inheritance hierarchy. So a bunch of classes extending one class becomes disorganized.

What is depth of inheritance tree?

Depth of inheritance, also called depth of inheritance tree (DIT), is defined as “the maximum length from the node to the root of the tree” CK.

What is deep inheritance?

Deep inheritance hierarchies are examples of successful reuse, but are also the source of maintenance problems, due to the complexity inherent in the large number of classes involved.


2 Answers

Let's enumerate the operations we should consider:

Construction/destruction

Each constructor/destructor will call its base class equivalents. However, as James McNellis pointed out, you were obviously going to do that work anyway. You didn't derived from A just because it was there. So the work is going to get done one way or another.

Yes, it will involve a few more function calls. But function call overhead will be nothing compared to the actual work any significantly deep class hierarchy will have to actually do. If you're at the point where function call overhead is actually important for performance, I would strongly suggest that calling constructors at all is probably not what you want to be doing in that code.

Object Size

In general, the overhead for a derived class is nothing. The overhead for virtual members is a pointer or for virtual inheritance.

Member Function Calls, Static

By this, I mean calling non-virtual member functions, or calling virtual member functions with class names (ClassName::FunctionName syntax). Both of these allow the compiler to know at compile time which function to call.

The performance of this is invariant with the size of the hierarchy, since it's compile-time determined.

Member Function Calls, Dynamic

This is calling virtual functions with the full and complete expectation of runtime calls.

Under most sane C++ implementations, this is invariant with the size of the object hierarchy. Most implementations use a v-table for each class. Each object has a v-table pointer as a member. For any particular dynamic call, the compiler accesses the v-table pointer, picks out the method, and calls it. Since the v-table is the same for each class, it won't be any slower for a class that has a deep hierarchy than one with a shallow one.

Virtual inheritance plays a bit with this.

Pointer Casts, Static

This refers to static_cast or any equivalent operation. This means the implicit cast from a derived class to a base class, the explicit use of static_cast or C-style casts, etc.

Note that this technically includes reference casting.

The performance of static casts between classes (up or down) is invariant with the size of the hierarchy. Any pointer offsets will be compile-time generated. This should be true for virtual inheritance as well as non-virtual inheritance, but I'm not 100% certain of that.

Pointer Casts, Dynamic

This obviously refers to the explicit use of dynamic_cast. This is typically used when casting from a base class to a derived one.

The performance of dynamic_cast will likely change for a large hierarchy. But sane implementations should only check the classes between the current class and the requested one. So it's simply linear in the number of classes between the two, not linear in the number of classes in the hierarchy.

Typeof

This means the use of the typeof operator to fetch the std::type_info object associated with an object.

The performance of this will be invariant with the size of the hierarchy. If the class is a virtual one (has virtual functions or virtual base classes), then it will simply pull it out of the vtable. If it's not virtual, then it's compile-time defined.

Conclusion

In short, most operations are invariant with the size of the hierarchy. But even in the cases where it has an impact, it's not a problem.

I'd be more concerned with some design ethic where you felt the need to build such a hierarchy. In my experience, hierarchies like this come from two lines of design.

  1. The Java/C# ideal of having everything derived from a common base class. This is a horrible idea in C++ and should never be used. Each object should derive from what it needs to, and only that. C++ was built on the "pay for what you use" principle, and deriving from a common base works against that. In general, anything you could do with such a common base class is either something you shouldn't be doing period, or something that could be done with function overloading (using operator<< to convert to strings, for example).

  2. Misuse of inheritance. Using inheritance when you should be using containment. Inheritance creates an "is a" relationship between objects. More often than not, "has a" relationships (one object having another as a member) are far more useful and flexible. They make it easier to hide data, and you don't allow the user to pretend one class is another.

Make sure that your design does not fall afoul of one of these principles.

like image 130
Nicol Bolas Avatar answered Nov 15 '22 19:11

Nicol Bolas


There will be but not as bad as the programmer performance implications.

like image 45
Tom Avatar answered Nov 15 '22 19:11

Tom