Sign in to follow this  
dbzprogrammer

[Confusion]Pointer and Value Passing

Recommended Posts

Ah, once again I'm humbled by my own retardedness and lack of ability to understand. Pray, tell, help me in my time of need GameDev.Net! First off, let's pretend we're passing a small class to a function. Basically, I've been look at the whole concept of optimization, and I've always believed that passing pointers/references of objects to functions would be faster than doing it by value. Mainly because of the copy constructor and all. But someone told me that with the ammount of pointer dereferencing that will go inside the function, it's better to pass a small class by value and have it copied for use. Aparently it will be faster than accessing it with a pointer in the long run? I'm not getting it. References and pointers have always been faster due to lack of CPU overhead, right? Or is there more to it than that? Ay, it's too late and I'm too confused! Help!

Share this post


Link to post
Share on other sites
In the case of the basic types (int, float, etc.) you should pass by value, because that is just as good as passing by pointer/reference, if not better.
For classes, well I have only anecdotal evidence, but it is my experience that when calling a method in a loop, passing by value an object was significantly slower than passing by reference/pointer and dereferencing.

You could always test it out yourself. Write a simple application with a small class and three functions, one that passes by value, one that passes by reference and one that passes by pointer, have a loop for each that calls the function a few million times and record the times taken to complete the loops.

In fact I'm rather bored, I might just do that now.

edit:

I ran this in debug mode (release mode optimises the hell out of it) and got:
Ptr: 4624 Ref: 4609 Val: 4735

I don't make any claims as to the usefullness of this code whatsoever.


#include <iostream>
#include <ctime>

class MyClass
{
private:
int _val;

public:

MyClass(void) : _val(0) {}
MyClass(MyClass const & copy_) : _val(copy_._val) {}

int val(void) const { return _val; }
void val(int val_){ _val = val_; }
};

void funcPtr(MyClass * myClass_)
{
int val;
for(int i = 0; i < 1000; ++i)
{
myClass_->val(i);
for(int j = 0; j < 1000; ++j)
{
val = myClass_->val();
}
}
}

void funcRef(MyClass & myClass_)
{
int val;
for(int i = 0; i < 1000; ++i)
{
myClass_.val(i);
for(int j = 0; j < 1000; ++j)
{
val = myClass_.val();
}
}
}

void funcVal(MyClass myClass_)
{
int val;
for(int i = 0; i < 1000; ++i)
{
myClass_.val(i);
for(int j = 0; j < 1000; ++j)
{
val = myClass_.val();
}
}
}

int main(void)
{
MyClass myClass;
MyClass * myClassPtr = &myClass;
MyClass & myClassRef = myClass;

clock_t ptr, ref, val;
std::cout << "start funcPtr" << std::endl;
clock_t ticks = clock();
for (long i = 0; i < 1000; ++i)
{
funcPtr(myClassPtr);
}
ptr = clock() - ticks;
std::cout << "start funcRef" << std::endl;
ticks = clock();
for (long i = 0; i < 1000; ++i)
{
funcRef(myClassRef);
}
ref = clock() - ticks;
std::cout << "start funcVal" << std::endl;
ticks = clock();
for (long i = 0; i < 1000; ++i)
{
funcVal(myClass);
}
val = clock() - ticks;

std::cout << "Ptr: " << ptr << " Ref: " << ref << " Val: " << val << std::endl;

system("PAUSE");
}




[Edited by - ratbag_mc on October 19, 2006 11:18:20 PM]

Share this post


Link to post
Share on other sites
Alright man, thanks. That makes sense, because all you're doing is effictively making an int on the stack, and calling a function to initialize it, so there wouldn't be much speed difference.

I appreciate it! =)

Share this post


Link to post
Share on other sites
Quote:

I ran this in debug mode

Then those numbers are useless as kind of real-world metric. Your conclusions are correct, but performance measurements done in debug mode don't mean anything. The optimizer removes the majority of your metric in release mode because it's too simple -- for example, inner loops of your test functions just assign to val over and over again.

Quote:

But someone told me that with the ammount of pointer dereferencing that will go inside the function, it's better to pass a small class by value and have it copied for use. Aparently it will be faster than accessing it with a pointer in the long run?

No. The amount of dereferencing you do in C++ doesn't (neccessarily) map 1:1 to the amount of "dereferencing" the compiler will generate. In general, the compiler is much better at making these sorts of optimizations than you, and you shouldn't try to second guess it.

If, for example, you pass a pointer to a Foo, and constantly dereference that pointer to call some method or access some field, the compiler can recognize that and optimize the access appropriately.

When there is a performance benefit to passing by reference versus passing by value (in terms of object size or constructor complexity), do so, unless you can't or shouldn't for semantic reasons.

Share this post


Link to post
Share on other sites
Quote:
Original post by jpetrie
Quote:

I ran this in debug mode

Then those numbers are useless as kind of real-world metric.


I know, which is why I gave the disclaimer and mentioned that release mode optimises it. But it did show that, all things being equal, passing by value is slower. Also, regardless of any speed benefit, passing by reference (better, const reference) is just a good habit to get into.

Share this post


Link to post
Share on other sites
Quote:
Original post by ratbag_mc
Quote:
Original post by jpetrie
Quote:

I ran this in debug mode

Then those numbers are useless as kind of real-world metric.


I know, which is why I gave the disclaimer and mentioned that release mode optimises it. But it did show that, all things being equal, passing by value is slower. Also, regardless of any speed benefit, passing by reference (better, const reference) is just a good habit to get into.


Keep in mind, it does more then just optomize your code. In debug mode a number of things happen, to say for example, help catch out of bounds indexs. These actions dont occur in a release build. Also, bits of code are inserted in a debug build ( to well... help debug ) that arent in release. Even without optimizations, a debug build is pretty much useless for metrics.

Share this post


Link to post
Share on other sites
Quote:
Original post by ratbag_mc
Quote:
Original post by jpetrie
Quote:

I ran this in debug mode

Then those numbers are useless as kind of real-world metric.


I know, which is why I gave the disclaimer and mentioned that release mode optimises it. But it did show that, all things being equal, passing by value is slower.


Hardly. It shows that this is the case for this specific example code in debug mode, on your computer and compiler combination, only.

Here's the results for debug mode, with your code unmodified and debugger attached, on my computer:

Ptr: 79063 Ref: 79031 Val: 78812

Your very same test "shows that, all things being equal, passing by value is faster", directly contradictory of your results! While I didn't run it at this high a multiple more than once, running it with lower multiples never showed Val > Ptr | Ref. Sometimes equivilant, but never greater than.

In general, by-value performance is equivilant or slightly better, in release mode, with trivial (e.g. value copying only) constructors, at least when sizeof(T) <= sizeof(T*), to by-reference/pointer. Nontrivial constructors, sizeof(T) > sizeof(T*)*C (where C is some environment constant > 1), etc. will be the opposite. I can nearly guarantee that any bolder claims are wrong in some circumstances. I wouldn't be suprised if there's some weird quirky corner case in some compiler somewhere that violates these "rules" too.

Your benchmark was flawed (relying on debug mode results), your reasoning was flawed (that you can make a greater-than/less-than comparison based on that close a margin), and as a result your conclusion was quite flawed.

The only useful things your benchmark does are:

1) Demonstrate the two methods for this example are roughly equivilant in debug mode. At least for some compilers.
2) Demonstrate how easy it is to benchmark incorrectly.

Sorry.

[Edited by - MaulingMonkey on October 21, 2006 11:43:17 AM]

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this