Advertisement

to OOp or not?

Started by October 13, 2005 02:07 PM
7 comments, last by FlowingOoze 19 years ago
hi all, i am creating (again) a math library for my projects and I have some design questions until now I had a vector class like this

class cVector3
{
  GLfloat x,y,z;

  cVector3() : x(0),y(0),z(0) {}

  // useful functions here
  float length();
  void normalize();
  cVector3 cross(cVector3& v) const;
 
  .... etc .....

  // operators
  cVector3 operator + (cVector3& v) const
  {
    return cVector3(x+v.x, y+v.y, z+v.z);
  }
   .... same for subtraction etc
}

Now, obviously I want my vector class to be super-fast and light I've realized that the use of overloaded operators like the one above, although makes the code more readable, is inefficient because it creates a temporary vector to store the result and return it. We are talking about an operation that will be applied thousands of times every frame (but you already know that, don't you?) So should I imlement a function like

void AddVectors(const cVector3& v1, const cVector3& v2, cVector3 &result);

or

class cVector3
{
  //...
  // store to (*this) the sum of v1 and v2
  void AddVectors(const cVector3& v1, const cVector3& v2)
  {
    this->x = v1.x+v2.x;
    this->y = v1.y+v2.y;
    this->z = v1.z+v2.z;
  }
  //...
}

or

class cVector3
{
  //....
  static void AddVectors(const cVector3& v1, 
                         const cVector3& v2, 
                         cVector3 &result);

  //...
}

I think that the last one is more preferable (organization-wise). But, does the member functions of a class add any overhead? if a model consists of 5000 edges, would there be any performance issue? Should I keep the class empty (with only x,y,z) and make every function outside any class (like the first one)? Inheritance is not an issue, I will not derive any class from cVector3 What do you think?
You could stick with operators, but still force the same behavior, by only overloading operators such as +=, -=, etcetera. Or you could implement the normal operators as well, but know (and document) that in sections that need best performance only use the assignment operators. This way you could use the normal ones when style of code was more important than speed, but still choose speed over style when you needed it.

Although I have a suspicion that it isn't quite a critical as you might worry it is. Could be wrong, I'm no expert, and I remember talk of "Named Return Value Optimization" not being commonly implemented, which I belief is the issue at hand here with your code. But there's always the rule of making it work first, and then making it work fast, if there's a speed problem to begin with.
"We should have a great fewer disputes in the world if words were taken for what they are, the signs of our ideas only, and not for things themselves." - John Locke
Advertisement
Keeping functions outside or inside the class shouldn't make a difference, this code
class Foo{public:    int A,B;    void X(){}    void Y()const{}    void X(int a){}    void Y(int a)const{}};


Would be translated into something like this:

class Foo{public:    int A,B;};void Foo_X(Foo&){}void Foo_Y(const Foo&){}void Foo_X(Foo&, int a){}void Foo_Y(const Foo&, int a){}


About the operator overloading, the only way I know of to avoid the temporary is like this:
class Vector3{public:// LHS is calling objectvoid Add(const Vector3& pRHS, Vector3& Result);};Vector3 a,b,c;// c = a + ba.Add(b,c)


In my vector class I have both the operator overloaded and a function taking a reference to store the result, then the user can choose.
Wouldn't defining the operator overloader outside the class work (just as a function)? And using the friend keyword if you have to?

Similar as overloading << for streams.

You shouldn't be too concerned with temporaries. I don't know what compiler you're using, but at least GCC is rather good at doing low-level CSE, so it's likely that no temporaries are actually created. It's best to look at the generated assembly from time to time (be sure that optimizations are turned on).

As for your design decisions, I don't know if my way is any better, but I've taken most of the operators and functions out of the vector class. For example, I've got a operator+(const vector & a,const vector & b) and cross(const vector & a,const vector & b). My vector classes are actually templates with size and type as parameters.
The operators actually instantiate template classes to perform the actual operations. This rather ackward approach is necessary to be able to have hand optimised special cases for certain types of vectors.
Despite this complexity GCC produces very good code.

Anyways, you should decide on a set of operations and types that you want to support and worry about the optimisations only after that. There are plenty of tricks in C++ that can produce optimal or near optimal code.
Quote: Original post by mldaalder
Wouldn't defining the operator overloader outside the class work (just as a function)? And using the friend keyword if you have to?

Similar as overloading << for streams.


well, it works, but you dont gain anything. Still it has to create a temporary vector to hold the result. Except, if you had something else in mind....

Well, I thing that
void cVector::AddVectors(v1,v2,vresult)
is aesthetically better than
void v1.Add(v2,vresult);

What I still want to make sure is that member function do not add "weight" to the class...... if not, then I will implement them as statics, but will also supply operators just for completity.
Advertisement
Quote: Original post by FlowingOoze
You shouldn't be too concerned with temporaries. I don't know what compiler you're using, but at least GCC is rather good at doing low-level CSE, so it's likely that no temporaries are actually created. It's best to look at the generated assembly from time to time (be sure that optimizations are turned on).

As for your design decisions, I don't know if my way is any better, but I've taken most of the operators and functions out of the vector class. For example, I've got a operator+(const vector & a,const vector & b) and cross(const vector & a,const vector & b). My vector classes are actually templates with size and type as parameters.
The operators actually instantiate template classes to perform the actual operations. This rather ackward approach is necessary to be able to have hand optimised special cases for certain types of vectors.
Despite this complexity GCC produces very good code.

Anyways, you should decide on a set of operations and types that you want to support and worry about the optimisations only after that. There are plenty of tricks in C++ that can produce optimal or near optimal code.


I use VC2003. Actually I'm now re-designing a math lib I had written some time ago, so I know wich set of operations I will implement. I have profiled the code and the fps penalty by using operators was significant (about half framerate...) but without optimizations to be honest.....

Quote: Original post by eXXXile
What I still want to make sure is that member function do not add "weight" to the class...... if not, then I will implement them as statics, but will also supply operators just for completity.

Unless you declare them virtual there won't be any difference. You should experiment a bit and look at the produced code.
Quote: Original post by eXXXile
I use VC2003. Actually I'm now re-designing a math lib I had written some time ago, so I know wich set of operations I will implement. I have profiled the code and the fps penalty by using operators was significant (about half framerate...) but without optimizations to be honest.....

Without optimisation the operators will surely create temporaries. There isn't much point doing such tests with optimisations turned off.

This topic is closed to new replies.

Advertisement