Sign in to follow this  
coderWalker

C++ arguments

Recommended Posts

coderWalker    127
I'm curious is the way you pass arguments to functions effects speed.

For example, would A be faster than B?
[b]
A[/b]
[code]//Header
struct vertex {int x,y,z};
//Main Code
setVetex(vertex coordinates);[/code]

[b]B[/b]
[code]//Main Code
setVetex(int x, int y, int z);[/code]

Also the following appears very often in my code.
I want to make the code cleaner (vertex's opposed to 3 ints), however I dont want to make it more complicated and buggy then needed.
[code]
setVertex(x+1,y,z);
setVertex(x-1,y,z);
setVertex(x,y+1,z);
setVertex(x,y+1,z);
setVertex(x,y,z-1);
setVertex(x,y,z+1);
[/code]

How would I do this with the typedef?
Would I have to modify it then remodify it again before the next function?
ex:
[code]
x+=1;
setVertex(x,y,z);
x-=1;
[/code]

Thanks
Walker

Share this post


Link to post
Share on other sites
inavat    317
[quote name='coderWalker' timestamp='1307405446' post='4820307']
I'm curious is the way you pass arguments to functions effects speed.

For example, would A be faster than B?
[b]
A[/b]
[code]//Header
struct vertex {int x,y,z};
//Main Code
setVetex(vertex coordinates);[/code]

[b]B[/b]
[code]//Main Code
setVetex(int x, int y, int z);[/code]
[/quote]

Assuing your setVertex A takes a parameter of type "vertex", then it's quite the opposite: B is probably faster than A. The reason for this is that the compiler will probably pass x,y, and z via register. It will have to pass the "vertex" struct via memory. You could, however, pass a const ref to setVertex instead. This would be as fast as passing the ints because it would use a register.

[code]void setVertex(const vertex& vertexParameter)
{
// now it's not slow
}[/code]

[quote]
Also the following appears very often in my code.
I want to make the code cleaner (vertex's opposed to 3 ints), however I dont want to make it more complicated and buggy then needed.
[code]
setVertex(x+1,y,z);
setVertex(x-1,y,z);
setVertex(x,y+1,z);
setVertex(x,y+1,z);
setVertex(x,y,z-1);
setVertex(x,y,z+1);
[/code]

How would I do this with the typedef?
Would I have to modify it then remodify it again before the next function?
ex:
[code]
x+=1;
setVertex(x,y,z);
x-=1;
[/code]
[/quote]

I might do:
[code]
static const uint NUM_OFFSETS = 6;
static const vertex vertexOffsets[NUM_OFFSETS] = {
vertex(1, 0, 0), vertex(-1, 0, 0),
vertex(1, 1, 0),
vertex(1, -1, 0),
vertex(1, 0, -1),
vertex(1, 0, 1)
};

vertex myVertex(x,y,z);

for (uint i=0; i < NUM_OFFSETS; i++)
{
setVertex( myVertex + vertexOffsets[i] );
}
[/code]

Share this post


Link to post
Share on other sites
sam_hughes    132
[quote name='A Brain in a Vat' timestamp='1307406400' post='4820313']
Assuing your setVertex A takes a parameter of type "vertex", then it's quite the opposite: B is probably faster than A. The reason for this is that the compiler will probably pass x,y, and z via register. It will have to pass the "vertex" struct via memory. You could, however, pass a const ref to setVertex instead. This would be as fast as passing the ints because it would use a register.

[code]void setVertex(const vertex& vertexParameter)
{
// now it's not slow
}[/code]
[/quote]

You are guessing. In fact, what you've said does not even make sense. Passing a const vertex& means you've got a vertex somewhere and you're passing a pointer. Passing a vertex means you are (in x86 under most calling conventions) passing it on the stack. It will not "use a register" in either case. The compiler will not "probably pass x, y, and z via register" in either case. That's for x86 calling conventions. Now let's consider x86-64 calls. In both cases, passing a vertex and passing three ints will be done in registers. A const vertex& will not be.

Now let's do some educated guessing and consider the situation (on x86) that you've imagined where setVertex is defined in a header file, or the same file, or there is some cross-module optimization going on, and the compiler doesn't inline it but creates a special version of the function that you can call using just registers. In such a case, if passing a pointer to a vertex is more optimal than passing the three fields in a register, it will do so and produce the same code for a const vertex& and a vertex. If passing the fields of a vertex in registers is more optimal it will pass them in registers and it's certainly not harder to prove such an optimization if the parameter is a vertex rather than a const vertex&. In either case it's more information for the compiler than passing three int parameters, because how would the compiler know that these three parameters were originally packed contiguously in a struct? It would require more examination of the call sites for it to make such a presumption.

And even if the compiler makes the wrong decision it's not exactly a notable difference because the edge of the stack is practically the same thing as a register anyway.

Share this post


Link to post
Share on other sites
Storyyeller    215
How is the edge of the stack practically the same as registers? It's stored in memory isn't it? Even if the processor prefetches it or whatever, I don't understand how it can be almost the same as registers.

Share this post


Link to post
Share on other sites
sam_hughes    132
[quote name='Storyyeller' timestamp='1307421364' post='4820386']
How is the edge of the stack practically the same as registers? It's stored in memory isn't it? Even if the processor prefetches it or whatever, I don't understand how it can be almost the same as registers.
[/quote]

That's a bit of an exaggeration, they aren't completely equivalent, but the edge of the stack is always in the L1 cache, except just after a thread switch. Edit: At that point it's really up to the CPU designers and the compiler as to how fast it goes.

Share this post


Link to post
Share on other sites
rip-off    10979
Have you profiled your application? My guess is that your bottleneck is elsewhere. I would pass using a structure because it groups together logically related information unless I had ample evidence that this was a poor decision.

Share this post


Link to post
Share on other sites
inavat    317
[quote name='sam_hughes' timestamp='1307420244' post='4820383']
You are guessing. In fact, what you've said does not even make sense. Passing a const vertex& means you've got a vertex somewhere and you're passing a pointer. Passing a vertex means you are (in x86 under most calling conventions) passing it on the stack. It will not "use a register" in either case. The compiler will not "probably pass x, y, and z via register" in either case. That's for x86 calling conventions. Now let's consider x86-64 calls. In both cases, passing a vertex and passing three ints will be done in registers. A const vertex& will not be.

Now let's do some educated guessing and consider the situation (on x86) that you've imagined where setVertex is defined in a header file, or the same file, or there is some cross-module optimization going on, and the compiler doesn't inline it but creates a special version of the function that you can call using just registers. In such a case, if passing a pointer to a vertex is more optimal than passing the three fields in a register, it will do so and produce the same code for a const vertex& and a vertex. If passing the fields of a vertex in registers is more optimal it will pass them in registers and it's certainly not harder to prove such an optimization if the parameter is a vertex rather than a const vertex&. In either case it's more information for the compiler than passing three int parameters, because how would the compiler know that these three parameters were originally packed contiguously in a struct? It would require more examination of the call sites for it to make such a presumption.

And even if the compiler makes the wrong decision it's not exactly a notable difference because the edge of the stack is practically the same thing as a register anyway.
[/quote]

I'm not sure I understand. Are you under the impression that I implied that passing a (vertex) would yield x,y,z being passed in via registers? I didn't say that. I said that passing (int,int,int) would mean x,y,z get passed in via registers. I also said that would be faster than passing (vertex), which would be passed in on the stack (in x86, as you point out). Then I said that passing (const vertex&) was the right way, which seems to be what you are saying as well.

What I got wrong was saying passing (const vertex&) "[color="#1C2837"][size="2"]would be as fast as passing the ints because it would use a register[/size][/color]". Thanks for correcting, I didn't consider the pointer dereference. Given that the compiler is smart enough to optimize the call to use registers (and given that it didn't get inlined), however, I think you're agreeing that passing (const vertex&) is the right way.

Thanks for clearing it up.

Share this post


Link to post
Share on other sites
rip-off    10979
Hidden
All this speculation about whether stuff gets passed this way or that way, its all meaningless. Some functions that take a vertex sized type will be inlined, nothing will be passed. Other functions will be too large to inline, in which case the time spent passing the vertex will be negligible. The only definitive answer is to look at the Release assembly.

Share this post


Link to post
coderWalker    127
I have to change the numbers multiple times so I think I'm going to go with a mixture of the answers.
[code]
//Header
struct vertex {int x,y,z};
setVetex(int x, int y, int z);
//Main Code
vertex thisVertex;
setVetex(thisVertex.x,thisVertex.y,thisVertex.z);
[/code]

Especially considering the following is shorter without passing the struct.
[code]
setVetex(thisVertex.x+1,thisVertex.y,thisVertex.z);
setVetex(thisVertex.x-1,thisVertex.y,thisVertex.z);
setVetex(thisVertex.x,thisVertex.y+1,thisVertex.z);
setVetex(thisVertex.x,thisVertex.y-1,thisVertex.z);
setVetex(thisVertex.x,thisVertex.y,thisVertex.z+1);
setVetex(thisVertex.x,thisVertex.y,thisVertex.z-1);
[/code]
The only problem is that it is a little (well alot) redundant.

Another question would functions be quicker if I declared the arguments as const's where possible?
[code]
//A
setVetex(int x, int y, int z);
//B
setVetex(const int x, const int y, const int z);
[/code]

Share this post


Link to post
Share on other sites
Hiwas    5807
[quote name='coderWalker' timestamp='1307500918' post='4820771']

Another question would functions be quicker if I declared the arguments as const's where possible?
[code]
//A
setVetex(int x, int y, int z);
//B
setVetex(const int x, const int y, const int z);
[/code]
[/quote]

The const "can" be faster but it depends on the use/case involved. For instance, is the function defined in the header or in a cpp? If it is in the header you can ignore pretty much everything since a simple function such as this will inline given just about any compiler. But, even in this case the const "can" be faster based on usage since the non-const version could require the compiler to make copies of the values. Assume that the setVertex is non-const and it is in tight loop, prior to inlining the function the compiler may decide to make copies of the passed in data instead of just using it for the assignments. "Most" compilers should pinhole optimize away the copies since it will notice the fact that there are no changes being made. Of course depending on the complexity of the code involved and the compiler involved, this "may" not be caught and as such case the const version helps the compiler understand the intention and make sure the optimization of avoiding the copies is applied.

Const is a rule for your code to follow and a hint to the compiler, it really doesn't mean anything in the real code. But it "is" a good hint to include when and wherever you can since not all compilers are equal when it comes to doing the best optimizations possible.

Moving back to the prior comments about the speed in general, this is much more arcane than any simple answer can support. I look at the overall class and "not" any individual function in the class. For instance, what happens if/when you decide to change your vector to use SIMD math. SIMD is faster even in the case of 3 component floats but comes with some downfalls. Worse, some of the suggestions suck when you go to SIMD. Given this is the beginners area, I'll stick to the simple case that you should be as const correct as possible when defining your classes of this type.

I could go on for a while about many things involved in this, but it does not have much place in a beginners area.

Share this post


Link to post
Share on other sites
rip-off    10979
Typically const on primitive types will not have a performance impact. Const on reference/pointer types rarely has a performance impact, because C++'s aliasing problem effectively defeats most possible optimisations. Const is mainly a correctness issue. I've only seen const [url="http://www.gamedev.net/topic/230443-initializing-array-member-objects-in-c/page__st__20"]change the outcome of generated code[/url] significantly a few times.

The most flexible solution is to do both. E.g:
[code]
void setVertex(int x, int y, int z);

inline void setVertex(const Vertex &v)
{
setVertex(v.x, v.y, v.z);
}
[/code]
Any modern compiler should trivially inline such a function. If your compiler cannot or will not inline such a function you're probably going to have a nightmare getting any kind of performance from it. TBH I would consider even the above a premature optimisation, I'd just use the setVertex(const Vertex &) as the definitive function until profiling indicated this was causing problems (which I highly doubt it will).

[quote]
Especially considering the following is shorter without passing the struct.
[/quote]
This isn't much longer:
[code]
for(int x = -1 ; x <= 1 ; x += 2) {
for(int y = -1 ; y <= 1 ; y += 2) {
for(int z = -1 ; z <= 1 ; z += 2) {
setVertex(Vertex(v.x + x, v.y + y, v.z + z));
}
}
}
[/code]

Share this post


Link to post
Share on other sites
Hiwas    5807
[quote name='rip-off' timestamp='1307522787' post='4820841']
Typically const on primitive types will not have a performance impact. Const on reference/pointer types rarely has a performance impact, because C++'s aliasing problem effectively defeats most possible optimisations. Const is mainly a correctness issue. I've only seen const [url="http://www.gamedev.net/topic/230443-initializing-array-member-objects-in-c/page__st__20"]change the outcome of generated code[/url] significantly a few times.
[/quote]

As mentioned, I tend to agree but when it "does" make a change to the generated code it can be a serious pain in the ass to track down the problem. As such, I find it best to just 'try" to be mostly const correct given that it is easier to prevent the problems up front. A very specific example of const correctness which shows up regularly:

[code]
class xxx
{
xxx operator +( xxx& rhs );
};
[/code]
Will typically not allow the compiler to apply the return value optimizations. This causes an invisible temp which based on construction/destruction cost can have an extremely negative impact on performance. Simply changing the above as follows gets rid of the temp.


[code]
class xxx
{
const xxx operator +( const xxx& rhs ) const;
};
[/code]
Again, this is not an absolute given that you will or won't get the optimization in either case, it is just a very common issue I've seen over and over with various compilers. Of course it is actually better to use a friend operator for most compilers because the pinhole optimizers seem to correctly optimize them in more cases.

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