Wednesday, November 30, 2011

PA4 – Implicit Conversions, RVOs and Proxy Objects!


Hello once again! I am trying to keep the pace here with the posts and hopefully you are enjoying them. Today I will talk about Assignment #4 (PA4) – C++ Efficiency. This assignment was a combination of three important topics about Efficiency in C++ programming: Implicit Conversions, Return Value Optimizations and Proxy Objects. I will give you a summary of each one in the following paragraphs:

  • Implicit Conversions: the whole idea behind this trick/optimization is to prevent coders from passing wrong-typed parameters to functions. For example, imagine that you have a set function for a particular member of a class and you do not want the user to pass a value that has a different type, how can you achieve this? Aside from turning up the compiler settings (warnings), there is one trick you could use:

Class Diego {
public:
          void setX( const  float  inX );
private:
          template <typename T> void setX( const T inX );
}

When this trick is applied, anything but a float variable will give a compilation error, therefore successfully preventing wrong-typed values to be passed as parameters. Sexy or what?

  • Return Value Optimizations: I will start by saying this: temporaries kill performance big time! (quoting famous philosopher Ed Keenan). The idea behind RVO is to remove as much temporary variables as possible in order to improve performance. By removing these temporaries we are also removing unnecessary calls to constructors, destructors, copy constructors, etc. Let’s look at the following example to better understand the idea:

With temporaries (No RVO):
Vect2D Vect2D::operator + ( const Vect2D &tmp ) const {
    Vect2D sum;
    sum.x = this->x + tmp.x;
    sum.y = this->y + tmp.y;
    return ( sum );
};

Without temporaries (RVO):
Vect2D Vect2D::operator + ( const Vect2D &tmp ) const {
    return Vect2D( this->x + tmp.x, this->y + tmp.y );
};

You might think this change does not improve performance a lot, but in my assignment I had a stress test in which a bunch of math operations were performed and repeated 5 million times, and just by using RVO I improved the time to 301ms down from 939ms originally. That’s a hell of an improvement in my opinion!

  • Proxy objects: this one is another trick to remove/reduce temporaries and copies from function calls. This method involves (ab)using operator overloading to: replace operations that involve more than two operands, to compare vector length using squared distance instead of square root, etc. I will show you guys an example for your entertainment and pleasure, try running/debugging it so you really understand  what is going on (it took me a while to get it):

struct VaddV {
    const Vect2D &v1;
    const Vect2D &v2;

    VaddV( const Vect2D &t1, const Vect2D &t2 )
        : v1(t1), v2(t2) {};

    operator Vect2D() {
        return Vect2D( v1.x + v2.x, v1.y + v2.y );
    }
};

inline Vect2D::VaddV operator + ( const Vect2D &a1, const Vect2D &a2 ) {
    return Vect2D::VaddV(a1, a2);
};

Try performing an addition operation between three (3) vectors and you will see this baby up and running.

Well my fellow readers, I hope these tricks are useful for you, and if not try using them as opening lines in your next date (not recommended). See you later! Adios!

No comments:

Post a Comment