FAQ 33.04 What is a disadvantage of lots of references and pointers?

In some cases, they may degrade the application's performance if it is CPU bound.

In the previous FAQ, member y_ is a Derived object rather than a pointer to an object. In addition to reducing flexibility, embedding an object inside another object like this makes it more expensive to copy a Fred object (the state of the Derived object must be copied, rather than just a pointer to the object). However objects are usually accessed more often than they are copied, so reducing the number of layers of indirection can make the application perform better.

One way that embedding an object inside another (as in y_ in the previous FAQ) can improve performance over using a pointer (as in x_ in the previous FAQ) is by inlining virtual function calls. Virtual function calls can be inlined only when the compiler can statically bind to the function, that is, when it knows the object's exact class. If a function doesn't do very much (a good candidate for inlining), then inline expansion of the function can improve performance significantly. As an extreme example, a simple fetch function (which might occur in a member function that gets a data member's value) might do only 3 CPU cycles worth of work, yet including the overhead of the virtual function call might cost a total of 30 cycles. In this case, it would take 27 + 3 cycles to do 3 cycles worth of work. If this were on a critical path and the operation could be inlined, reducing the overhead of this operation by a factor of 10 could be significant.

In the example that follows, the call x.sample() in main() can be statically bound to Fred::sample(), because the exact class of local object x is known to be Fred. Furthermore, since that member function is defined inline, the call can be inlined. Similarly the call d_.f() in Fred::sample() is known statically to invoke Derived::f(), this time because member d_ is known to be exactly a Derived; since Derived::f() is also inline, the call can be inlined. Thus the entire call graph starting at main()'s x.sample() collapses into nothing: the only code that will be executed is the code that happens to be within Derived::f(). However the call to b_->f() in sample2() cannot be inlined even though b_ apparently points to a Derived, since the compiler has to assume that some other member function might change b_ so that it points to some other derived class (see FAQ 33.03).

 #include <memory> using namespace std; class Base { public:   virtual ~Base() throw();   virtual void f() throw() = 0; }; typedef auto_ptr<Base> BasePtr; Base::~Base() throw() { } class Derived : public Base { public:   virtual void f() throw(); }; inline void Derived::f() throw()                     <-- 1 {                                                      <-- 2 } class Fred { public:   Fred() throw();   virtual void sample() throw();   virtual void sample2() throw(); protected:   Derived d_;   BasePtr b_; }; Fred::Fred() throw() : d_() , b_(new Derived()) { } inline void Fred::sample() throw() { d_.f(); }                                          <-- 3 inline void Fred::sample2() throw() { b_->f(); }                                         <-- 4 int main() {   Fred x;   x.sample();   x.sample2(); } 

(1) This is inline even though it's virtual

(2) If this is short, the inlining can make a significant difference

(3) The call to d_.f() can be inlined

(4) The call to b_->f() cannot be inlined

Not all compilers are guaranteed to perform these optimizations, but many do in practice.



C++ FAQs
C Programming FAQs: Frequently Asked Questions
ISBN: 0201845199
EAN: 2147483647
Year: 2005
Pages: 566
Authors: Steve Summit

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net