Skip to main content
gamedev.net
🔒 Locked

CVector class optimization(s)

Started by clapton 6 replies 400+ views
Original Post
clapton
clapton
Hello I have a few questions about making the best of my vector class implementation. Having an efficient CVector class is surley a big step to making fast-working applications. 1) I use MSVC++ 6.0 and I keep getting some errors when I try to define some operators to be inline - funny thing since for instance ''='' works fine with inline definition but other operators (like ''*'', ''/'') does not. ?? Sure, I can put all the operators inside class definition - the question is : am I 100% guaranteed to have these functions inline expanded? 2) Is there a big difference in these two codes : *1
CVector3 CVector3::operator+ (const CVector3 &Vec)
{
    CVector3 result;

    result.x = x + Vec.x;
    result.y = y + Vec.y;
    result.z = z + Vec.z;

    return result;
}
 
*2
CVector3 CVector3::operator+ (const CVector3 &Vec)
{
    return CVector3(x + Vec.x, y + Vec.y, z + Vec.z);
}
 
I guess that there is a pretty big difference since 1* does a lot more (but really?) I am just asking to be sure. 3) I can''t spot the difference between : *A
CVector3 CVector3::operator= (const CVector3 &Vec)
{
     x = Vec.x;
     y = Vec.y;
     z = Vec.z;

    return *this;
}
 
*B
CVector3& CVector3::operator= (const CVector3 &Vec)
{
    x = Vec.x;
    y = Vec.y;
    z = Vec.z;

    return *this;
}
 
I mean - I know about this reference in *B but because it doesn''t affect the way of using ''='' I can''t tell the differenece. Now - which one is more likely to be faster?... Great thanks for all your help/suggestions/blames etc ... See ya'' ---------------------------- ... you got me on my knees, Layla
___Quote: Know where basis goes, know where rest goes.
marijnh
marijnh
In both cases use the second one.

For the + operator the second method will allow (not force) a compiler to eliminate the creation of a temorary object for the return value - it can just create the new vector object once and have that be the return value while the first version requires the object to be present when the code in the function is executed.

It is probably better not to make this function a member (inline CVector3 operator+(const CVector3& a, const CVector3& b); ), but the difference is subtle (more symmetry, allows promotion of other types for the first argument).

For the = operator the second one returns a reference to the current object while the first one creates a copy of the current object, obviously the second one is more efficient.

As for forcing the compiler to inline functions, you can't, but if you turn on optimization it will inline all inline functions that it thinks should be inlined, and the compiler usually knows better than you do what should be inlined.

[edited by - marijnh on May 3, 2004 3:09:23 PM]
Zipster
Zipster
quote:
1) I use MSVC++ 6.0 and I keep getting some errors when I try to define some operators to be inline - funny thing since for instance ''='' works fine with inline definition but other operators (like ''*'', ''/'') does not. ?? Sure, I can put all the operators inside class definition - the question is : am I 100% guaranteed to have these functions inline expanded?

In the end, it''s up to the compiler to decide what gets inlined. But what type of errors do you get?

quote:
2) Is there a big difference in these two codes :
*1
...
*2


They both do the same thing, but *2 is a lot shorter and cleaner.

quote:
3) I can''t spot the difference between :
*A
...
*B

Returning a reference is the convention used for the assignment operator, to maintain compatibility with chaining. While you may never use it, say you have something like this: (myVec = anotherVector) += 3;. Maybe not the best example, but eh It also saves on copy constructors being called all over the place.
JohnBolton
JohnBolton
1) Don''t know why the inline versions don''t work because you didn''t give any details. "inline" is now just a hint to the compiler.

2) Though a pedantic compiler would generate faster code for *2, a modern optimizing compiler is more likely to generate the same code for both (depending on your implementation of CVector3, of course).

3) Same as 2), but it depends on how operator=() is used. I recommend that you return "const CVector3 &", so that the following code doesn''t compile.
CVector3 a, b, c;
( a = b ) = c;


Note: in this code, *A and *B will have very different results.



John Bolton
Page 44 Studios
Current project: NHL Faceoff 2005 PS2
John BoltonLocomotive Games (THQ)Current Project: Destroy All Humans (Wii). IN STORES NOW!
Hybrid666
Hybrid666
its called the RVO (return value optimisation)

for a compiler to do it u cannot declare a local named variable of that type

so for operator +

return CVector(x, y, x);

is good becos the variable isnt named (eg. CVector myVector), hence the compiler can perform the optimisation

Hybrid
Polymorphic OOP
Polymorphic OOP
quote:
Original post by JohnBolton
I recommend that you return "const CVector3 &", so that the following code doesn't compile.
CVector3 a, b, c;
( a = b ) = c;




You should actually try to have your operator = return a non-const reference. This is what built-in types do and is what generic algorithms and types can potentially rely on. With your given example, it seems silly to return a non-cont reference, but that's because you particularly chose an example where it would look like it serves no purpose. There are many times where it is beneficial to have a non-const reference and there is no logical reason why the reference should be const, therefore there isn't a reason to put on a const restriction. Doing so can only restrict you from using a templated algorithm or type which assumes a standard overload of the assignment operator. A good general rule to follow is to avoid putting any more restrictions on overloaded operations than those that are put on built-in types. Always try to stay as close to standard convention as possible, otherwise it can bite you on the ass when you least expect it. Going further, take your example, but replace the vector type with int:

int a, b, c;
( a = b ) = c;

You claimed the vector version was bad. Why then does the int version work? It's because logically, there is no reason for it not to work. The same logic holds true for the vector type. Again, this example is silly because it shows no reason why you'd be doing that operation. A better example might be assigning a vector and passing it by reference to a function in the same statement, or applying a normalize function to it after assignment, etc.

Now, to see when it can cause errors in your code, what happens if you are using a generic library. Inside this library, an algorithm does, for instance, something like this?

template< typename Type >
Type& function( Type& object )
{
Type some_temp_object;
/* code */
return object = some_temp_object;
}


Types which overload the assignment operator properly will work with this algorithm, however, your code won't work because you made operator = return a const reference for no reason. Of course, in the algorithm, the assignment could have been made in an earlier statement and then object could have just been returned, but the person writing the generic algorithm wouldn't know this. He works with the operations as they should work, without unecessary restrictions. He has to assume that the operations work as they are supposed to, modeled after the built-in types in C++.

quote:
Original post by Hybrid666
its called the RVO (return value optimisation)

for a compiler to do it u cannot declare a local named variable of that type

so for operator +

return CVector(x, y, x);

is good becos the variable isnt named (eg. CVector myVector), hence the compiler can perform the optimisation

Hybrid


Then you should also be familiar with the happily titled "named return value optimization." Which performs the same exact optimization when all of the return statements in a program return the same object. Most compilers make use of this optimization as well. In that respect, no matter which version of the functions are used, they'll most-likely generate the same code, unless the default constructor is defined to do something, such as initialize the values of the vector to 0.

[edited by - Polymorphic OOP on May 4, 2004 6:20:35 AM]
clapton
clapton
Hey, thanks for your help!

Everything seems to be pretty clear now. Anyway, you asked what error do I get when using the inline directive. For example, if I define ''*'' as inline member function I get ''unresolved external symbol''. That''s more than weird since for ''*'' I don''t get ANY errors/warnings. Hmm...

Thanx once again!

clapton

-----------------------------------------



... you got me on my knees, Layla
___Quote: Know where basis goes, know where rest goes.
Polymorphic OOP
Polymorphic OOP
quote:
Original post by clapton
Hey, thanks for your help!

Everything seems to be pretty clear now. Anyway, you asked what error do I get when using the inline directive. For example, if I define '*' as inline member function I get 'unresolved external symbol'. That's more than weird since for '*' I don't get ANY errors/warnings. Hmm...


That's because when you inline functions you have to move their definitions to a header file included by any translation units that use it. You can't leave it in the cpp unless the only functions that use it are also in that cpp file.

[edited by - Polymorphic OOP on May 4, 2004 8:20:31 PM]

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.