Advertisement

C++ Operator Overloading, and why it blows chunks

Started by May 29, 2000 09:39 AM
60 comments, last by MadKeithV 24 years, 6 months ago
You''re right null, I''d glossed over that chapter in my performance book.
The optimisation takes out ONE of the temporaries, but still requires a third parameter for a temporary.

Here''s the (pseudo)code from my book:
void Add( const Vector& __tempResult, const Vector& v1, const Vector& v2 ){__tempResult.Vector::Vector(); // Constructing!__tempResult.m_x = v1.m_x + v2.m_x;.. etc.}which is optimised from:void Add( const Vector& __tempResult, const Vector& v1, const Vector& v2 ){struct Vector retVal;retVal.Vector::Vector();retVal.m_x = v1.m_x + v2.m_x;retVal.m_y = v1.m_y + v2.m_y;__tempResult.Vector::Vector( retVal ); // Constructing!retVal.Vector::~Vector();return;} 


I realise the pseudocode looks a bit odd, but I think it''s reasonably clear.

#pragma DWIM // Do What I Mean!
~ Mad Keith ~
It's only funny 'till someone gets hurt.And then it's just hilarious.Unless it's you.
Another little thing - I''ve been using C++ for a few years now, and never have I learned so much about it as in the last few days!
It really helps to know it''s strengths and weaknesses inside-out.
Now I know NOT to use classes for small, very-often used operations. Granted, I''ve lost type-checking in the process, but since it''s VERY internal code nobody else should ever need to even see the miserable codings I''ve done there.


#pragma DWIM // Do What I Mean!
~ Mad Keith ~
It's only funny 'till someone gets hurt.And then it's just hilarious.Unless it's you.
Advertisement
It is possible to remove one temporary object by a very simple change: instead of doing
vector v1, v2, v3;v1 = v2 + v3;  

, do the following
vector v2, v3;vector v1 = v2 + v3;  

That way, there's no need for an extra temporary object, because the copy constructor is used directly to initialize v1. No need for any static operator+ methods and caching variables...

Another note: operator+ ea are usually defined as friend functions instead of member functions. This makes things easier for type conversion, consider the following example:

There's a string class (that doesn't have a const char* conversion method), with a member operator+(const char*) which adds the const char* to the existing string. This allows us to write:
string a;a + "bla"; 
.
However, it doesn's allow us to write
string a;"bla" + a; 
.
If there's a (implicit) constructor that takes a const char* as single argument, and define a friend function operator+(const string&, const string&), we could use both ways of adding a const char* to a string.

Erik

Edited by - Erik Post on May 30, 2000 9:39:39 AM
Maybe we should have a competition, whoever can write the fastest pure C++ class implementation of operator-based vector addition, and multiplication?


#pragma DWIM // Do What I Mean!
~ Mad Keith ~
It's only funny 'till someone gets hurt.And then it's just hilarious.Unless it's you.
Urrg. I have just finished rewriting my engine in accordance with the OO edicts, using a vector class instead of directly doing the addition /whatever on structure members myself.

What with everyone speaking glowing praises of the new optimizing compilers, I expected it would perform optimizations the same as I would try to do if I was writing things in assembly. Foolish me!
MadKeith - I might just have a go at that.
let us know,

-Mezz
Advertisement
Initialiser lists aren''t likely to be any faster when working with intrinsic types. However, the reason to use them most of the time is for the reasons you''ve discovered here: if you are initialising classes, you don''t want to call a constructor twice. Without using an initialiser list, you could be calling a member''s default constructor, followed by its copy constructor to initialise it in the containing class''s constructor body. Initialiser lists can therefore remove the call to the default constructor, which is often a Good Thing
Don''t be afraid using c++ !

Operator overloading gives you the ability to write easy to read and therefore to understand mathematical code.

If you need to tweak a code fragment just use

+= or -= or *= or /= (you see :-).

Instead of:
a=b+c+d*e*f+g;
write:
a=d;
a*=e;
a*=f;
a+=g;
a+=b;
a+=c;

Such a code segment doesn''t look nicely anymore however there aren''t any temporary objects anymore which should make this code as fast as dedicated c code.

Currently my main concern is the use of inline code. If I want to load my math lib dynamically to use normal floating point operators or ISSE or 3DNow! or Altivec I can''t use inlining - *sigh*.

bjoern
i personally believe the way things have been setup in java which disallows operator overloading. operator overloading is confusing. is it really that bad to use

Vector& Vector::AddVector(Vector& v);

v3 = v1.AddVector(v2);. just as easy to understand.

also C++ classes have to push the "this" pointer onto stack. also it is good to look at the disassembly. it will help you understand what the compiler is doing.
To the vast majority of mankind, nothing is more agreeable than to escape the need for mental exertion... To most people, nothing is more troublesome than the effort of thinking.
I don''t see how the fact that C++ has to use an implicit "this" pointer argument is treated as a mark against it. If you were going to do the equivalent operations in C, you have to pass in a "this" pointer explicitly in the same manner. And methods not requiring a "this" pointer can be made as static methods, so as not to incur any unnecessary overhead. Why do people continue to make this out to be some evil C++ inefficiency thing?

As for the whole operator overloading thing, sorry but I will never go back to using C-style "AddVector" functions ever again if I can avoid it. And using C++, I can avoid it.

In large projects, the amount of general-use code using vector operations far exceeds the amount of code where speed is so critical that the lack of return-value optimizations in operator overloading methods becomes a factor. By many orders of magnitude. And in those few cases where it is a factor, simple things like using += instead of + or other operators where return values are not required is generally enough of a speed gain to make the hit even more negligible. On the even more rare cases where you still need more speed, do the operations explicitly and don''t call those methods.

But let''s be realistic here. We''re talking about a small fraction of a percent of the code that is speed-critical to that degree here. For most of the code for a normal game project, the inefficiencies of return value optimizations on some vector operators has virtually diddlysquat of an effect on the performance of the game. But the decrease in programmer efficiency by being forced to type "AddVector(v1, v2, v3)" instead of the simple "v3 = v1 + v2" thousands of times over will add up to significant losses in person-time, and can also decrease code readability by forcing a formulaic operation requiring only a single line using operators to take 5 lines instead using C-style vector functions.

These days it is just as important to optimize the programmer''s time as it is to optimize the machine''s. And where operator overloading may lack slightly in the latter, it more than makes up for it with it in the former. And the lackings it has aren''t noticable except in a very small amount of code, while the gains are propogated through the entire codebase. In other words, when used properly, it is a *good* thing.

This topic is closed to new replies.

Advertisement