• Advertisement
Sign in to follow this  

can function have too many params?

This topic is 4212 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm not sure what "Beginner" means in this place, but I'll just post this question here because it seems to be pretty basic. I just want to know: Are there problems with defining a method that takes a lot of params? Here are some functions from my game that I am somewhat concerned about:
void AddShotQuad(bool light, WORD alpha, WORD red, WORD green, WORD blue,
		         D3DXVECTOR2 pos, float height,
				  float angle, float radius, float tail_length,
				  float tex_u, float tex_v);

AddPuff(bool light, bool face_camera, float grow, float trns,
						WORD red, WORD green, WORD blue,
						float fade, D3DXVECTOR3 pos, 
                                                float u, float v, float size,
						float size_variance,
                                                float speed,               
                                                float pos_offset)


These may seem random or contrived, but, believe me, I actually use them :) and I want to know if they have too many parameters or not. Thanks! -synth_cat

Share this post


Link to post
Share on other sites
Advertisement
Keep It Simple Stupid.

If you ever go over 4 or 5, you're design probably isn't the best. Don't be fooled by Microsoft's APIs. Less is more. Use default parameters with extreme liberty.

Share this post


Link to post
Share on other sites
In C++, a compiler implemention is suggested to support at least 256 parameters for a function.

Share this post


Link to post
Share on other sites
I dont think it is a problem or a bottleneck of any kind.
An extra parameter usually means pushing an extra variable onto the function stack before doing the jump.
The type of the argument is more important. Sending a huge home-made struct by value would be bad for performance if performance is a big concern for your program.

Share this post


Link to post
Share on other sites
Quote:
Original post by synth_cat
I'm not sure what "Beginner" means in this place, but I'll just post this question here because it seems to be pretty basic.


We're pretty liberal with the definition.

Quote:
I just want to know: Are there problems with defining a method that takes a lot of params?


Technical problems, not really. Problems for the programmer who has to remember the order of the parameters and what they are for, and who will get RSI from all the typing, probably.

Quote:
These may seem random or contrived, but, believe me, I actually use them :) and I want to know if they have too many parameters or not.


It looks like some of the parameters could be placed in an array as constants.

Share this post


Link to post
Share on other sites
Quote:
Original post by SiCrane
In C++, a compiler implemention is suggested to support at least 256 parameters for a function.


Really? I thought the max was 31 (ie 5 bits).

Share this post


Link to post
Share on other sites
One of the things that you can do to help cut down are your parameters is to group your parameters into classes. One that I see right away in your is your WORD red, WORD blue, WORD green. This could easly become a RBG class. Yes this could be a struct also but by making it a class you could add error checking to make sure the red, blue and green values are valid. You could also add alpha to it just to make it more complete and make it a RBGA class. You can look for other groups like this in your code to clean things up.

theTroll

Share this post


Link to post
Share on other sites
Quote:
Original post by LessBread
Really? I thought the max was 31 (ie 5 bits).

It's implementation defined, but the standard suggests that it be at least 256 (Annex B of the C++ Standard).

Share this post


Link to post
Share on other sites
Quote:
Original post by SiCrane
Quote:
Original post by LessBread
Really? I thought the max was 31 (ie 5 bits).

It's implementation defined, but the standard suggests that it be at least 256 (Annex B of the C++ Standard).


Ok. I got the 5 bit limit from the interrupt gate spec for the IA32. Of course, the C++ standard isn't limited to just that chip.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Been a while since I looked at this but as a general rule: under 4 parameters and the compiler will pass the arguments using registers which is much quicker than creating a stack frame that then has to be cleaned up on method exit.

A better way of doing it:

struct RGB
{
WORD red;
WORD green,
WORD blue;
};


struct ParticleParameters
{
bool light;
bool face_camera;
float grow;p
float trns;
RGB Colour;
float fade;
D3DXVECTOR3 pos;
float u;
float v
float size;
float size_variance;
float speed;
float pos_offset;
};

void AddPuff( const ParticleParaters& rParticleParameters );

Generally you wouldn't initialise most of those "variables" every time you use the function anyway - they're constants because you'll use the same combinations again and again (e.g. different set up for fire effect to gently drifting snow). So set the structure up with the values at initialisation time (and for extra points load from an initialisation file) and then just tweak the values that you want to drive in real time.

Have fun.

Share this post


Link to post
Share on other sites
Quote:
Original post by Anonymous Poster
Been a while since I looked at this but as a general rule: under 4 parameters and the compiler will pass the arguments using registers which is much quicker than creating a stack frame that then has to be cleaned up on method exit.

As a general rule, that fails quite spectacularly. It is, however, a nice attempt.

Passing parameters in registers is often not a good idea anyhow, so it is wise of compilers not to default to that behavior.

CM

Share this post


Link to post
Share on other sites
Quote:
Original post by Conner McCloud
Quote:
Original post by Anonymous Poster
Been a while since I looked at this but as a general rule: under 4 parameters and the compiler will pass the arguments using registers which is much quicker than creating a stack frame that then has to be cleaned up on method exit.

As a general rule, that fails quite spectacularly. It is, however, a nice attempt.

Passing parameters in registers is often not a good idea anyhow, so it is wise of compilers not to default to that behavior.

CM


Um, sorry, but I don't think that's correct.

Although the number of parameter passing registers varies by architecture, I would say 4 is a decent number to reasonably expect across the board.

That said, passing parameters in registers is significantly more efficient than putting them on the stack, and no commercial compiler would pass up on the opportunity to take that optimization (for non-debug code, anyways).

Caveat: I'm sure there's some wacky exception out there to this, but I've done optimization and assembly on about 10 different processors, and as far as generalizations go, that's one of the more true ones I've seen.

Oh, also: At least on x86 processors, you don't have to actually create an EBP-based stack frame for optimized code (even with local variables and stack-passed parameters), as the compiler will know how much to offset from ESP, as it knows how many bytes of locals are on the stack at any given moment. I'm pretty sure that's an available VC++ optimization.

Share this post


Link to post
Share on other sites
Quote:
Original post by Anonymous Poster
A better way of doing it:

struct RGB
{
//...
};


struct ParticleParameters
{
//...
};

void AddPuff( const ParticleParameters& particleParameters );

Generally you wouldn't initialise most of those "variables" every time you use the function anyway - they're constants because you'll use the same combinations again and again (e.g. different set up for fire effect to gently drifting snow). So set the structure up with the values at initialisation time (and for extra points load from an initialisation file) and then just tweak the values that you want to drive in real time.


I agree with this 100%.

Well, 99% - I fixed a typo, and got rid of some HN. :)

Share this post


Link to post
Share on other sites
Hmmm.... I also agree with the struct solution. In general, I learned a simple rule is any more than 7 is too much for readability. And only 7 if you must. Reason is that most people can not remember more than 7 things at once. Then if you look at the function prototype and scan through the function, you won't forget what a variable is.

That just has to do with readability though and nothing else.

Share this post


Link to post
Share on other sites
Quote:
Original post by JasonBlochowiak
Um, sorry, but I don't think that's correct.

I do.
Quote:
Original post by JasonBlochowiak
Although the number of parameter passing registers varies by architecture, I would say 4 is a decent number to reasonably expect across the board.

It varies not by architecture, but by compilers. Compilers define how they chose to call functions, not the architecture [with the obvious caveats, of course]. In this case, neither gcc nor VC++'s default calling convention allow for arguments to be passed via registers. VC++ has the fastcall convention, which allows for the first two DWORD or smaller parameters to be passed via ECX and EDX, but that has to be specified by the programmer. It won't do so on its own, and it is still less than the four originally estimated.

I suppose whole-program optimizations might allow for variations on these rules, but even then I imagine the situations where it will are rather limited.

CM

Share this post


Link to post
Share on other sites
Well, thanks for all the replies!

Quote:

Here's an example of a function with too many parameters. Note that most of them have been snipped.

_Wow_ This is probably the first time I've seen code that much worse than my own...

Quote:

Technical problems, not really. Problems for the programmer who has to remember the order of the parameters and what they are for, and who will get RSI from all the typing, probably.

Thanks, Fruny - that's basically what I wanted to know.

Quote:

A better way of doing it:

struct RGB
{
WORD red;
WORD green,
WORD blue;
};


struct ParticleParameters
{
bool light;
bool face_camera;
float grow;p
float trns;
RGB Colour;
float fade;
D3DXVECTOR3 pos;
float u;
float v
float size;
float size_variance;
float speed;
float pos_offset;
};

void AddPuff( const ParticleParaters& rParticleParameters );

I had actually tried doing something like this right before I created this thread. What I did was to create a puff_types vector containing "predefined" types of puffs, and then added a PuffType parameter to the function so that I could remove a bunch of parameters (I currently use this approach with my projectiles.) However, this attempt was pretty much a huge waste of time - it severely limits my flexibility, forces me to spend many tedious hours defining countless puff types, and, to tell the truth, I really want to be able to define every single characteristic of every puff the moment I create it anyway.

I guess I should have asked: Do the params of my functions represent too much memory on the stack?

Thanks!

-synth_cat

Share this post


Link to post
Share on other sites
Quote:
Original post by Conner McCloud
Quote:
Original post by JasonBlochowiak
Um, sorry, but I don't think that's correct.

I do.
Quote:
Original post by JasonBlochowiak
Although the number of parameter passing registers varies by architecture, I would say 4 is a decent number to reasonably expect across the board.

It varies not by architecture, but by compilers. Compilers define how they chose to call functions, not the architecture [with the obvious caveats, of course]. In this case, neither gcc nor VC++'s default calling convention allow for arguments to be passed via registers. VC++ has the fastcall convention, which allows for the first two DWORD or smaller parameters to be passed via ECX and EDX, but that has to be specified by the programmer. It won't do so on its own, and it is still less than the four originally estimated.

I suppose whole-program optimizations might allow for variations on these rules, but even then I imagine the situations where it will are rather limited.

CM


It also varies by processor. x86 doesn't really have a well-defined calling convention other than perhaps "dump everything on the stack, sometimes backwords, sometimes forwards, except when you don't". This is mostly due to the general lack of registers.

x64 tries to do this better by defining (I believe by AMD but could be wrong and there's nothing actually stopping people from doing whatever the hell they want) that the first 4 parameters are passed in registers (rcx, rdx, r8, r9) and the rest on the stack.

ia64 pretty much goes out of it's way to make life hard with special registers for various types of parameters, packed parameters (e.g. floats), wierd rules for register spilling and stack slot reservations, etc. Again, there is nothing really stopping people from making up whatever they want but I can't imagine why anybody would want to. The ia64 does have dedicated instructions for handling (some of) the complexity.

Share this post


Link to post
Share on other sites
Quote:
Original post by synth_cat
I guess I should have asked: Do the params of my functions represent too much memory on the stack?


Not likely. Packing them into structs won't help anyway.

However, packing them into structs *will* help keep things organized, and is very likely to reduce the overall amount of irritating typing - because if some parameters are in some way related to each other, it's likely that they'll be passed around as a set, to several functions. Setting up the struct basically costs you typing only once for the struct, may cost or save typing each time you create a struct (versus individual variables, depending on how you do it, etc.) and saves you typing every time you pass the struct somewhere.

Share this post


Link to post
Share on other sites
Quote:
Original post by Conner McCloud
Quote:
Original post by JasonBlochowiak
Um, sorry, but I don't think that's correct.

I do.
Quote:
Original post by JasonBlochowiak
Although the number of parameter passing registers varies by architecture, I would say 4 is a decent number to reasonably expect across the board.

It varies not by architecture, but by compilers. Compilers define how they chose to call functions, not the architecture [with the obvious caveats, of course]. In this case, neither gcc nor VC++'s default calling convention allow for arguments to be passed via registers. VC++ has the fastcall convention, which allows for the first two DWORD or smaller parameters to be passed via ECX and EDX, but that has to be specified by the programmer. It won't do so on its own, and it is still less than the four originally estimated.

I suppose whole-program optimizations might allow for variations on these rules, but even then I imagine the situations where it will are rather limited.

CM


I was trying to be a bit polite about it, but you're flat out wrong about your original statement that passing parameters in registers is a bad idea, and is something compilers should avoid. Quite to the contrary, on almost all architectures it's a good idea.

The architecture matters quite a bit, and ironically your post shows why: The x86 series has a small number of general purpose registers, and thus dictates how the compilers have to approach parameter passing. On most other modern CPUs, the number of general registers that are available is much larger - hence the number of registers available for parameter passing is larger.

Whole program optimization could hugely affect parameter passing conventions - being able to do register coloring across function call boundaries would mean that the rather staid x86 calling conventions/register reservation could be tossed out the window. Whether or not this is currently being done - I don't know either.

Share this post


Link to post
Share on other sites
specifically on x86 only passing 4 parameters via registers would be completely pointless ... because they would be occupying the locations that need to be read from and written to by the instructions that the function will execute. So the first step of the function would have to be to move them all to the stack.

On any form of risk chip you are obviously right, that using registers is much more efficient a calling cnovention than using the stack, when the number of available registers is still high enough to complete the function's instructions without excesive relocation of values.

See on x86, it is not just the fact that their are almost no registers, it is the fact that each commond often may only operate on a specific register (or more if needed) .. so an instruction may not use AX as a the loop controller, or CH for division just cause it wants to ... it has to move the values into the register that is the fixed source or target for the upcoming instruction.

Basically it would be like movers leaving boxes in the doorway because it is quicker than going upstairs. Problem is, the very next thing you have to do is get the damn boxes out of your way. (x86 only)

Share this post


Link to post
Share on other sites
Quote:
Original post by Xai
See on x86, it is not just the fact that their are almost no registers, it is the fact that each commond often may only operate on a specific register (or more if needed) .. so an instruction may not use AX as a the loop controller, or CH for division just cause it wants to ... it has to move the values into the register that is the fixed source or target for the upcoming instruction.


Well, not really on the loop counter thing. You can use EAX as a loop control counter if you want - just not if you're using REP for controlling a fill or copy. And even there, whether or not it makes sense to use REP MOVSx or REP STOSx has toggled back and forth between different generations of the x86 - on some of the chips, it was more efficient to use the builtin instructions, and on some of the chips, it was faster to use an explicitly coded loop.

Share this post


Link to post
Share on other sites
Quote:
Original post by JasonBlochowiak
I was trying to be a bit polite about it, but you're flat out wrong about your original statement that passing parameters in registers is a bad idea, and is something compilers should avoid. Quite to the contrary, on almost all architectures it's a good idea.

The architecture matters quite a bit, and ironically your post shows why: The x86 series has a small number of general purpose registers, and thus dictates how the compilers have to approach parameter passing. On most other modern CPUs, the number of general registers that are available is much larger - hence the number of registers available for parameter passing is larger.

None of that changes the fact that, as a general rule, compilers do not pass four parameters via registers. They do not do so on the x86, and as that is what the vast majority of programming here [the For Beginers Forum] is targetting, that dictates what the general rule is. Everything is passed via the stack unless explicitly told to do otherwise. Thus, the AP was incorrect to suggest that functions with four parameters are faster than functions with five parameters.

Everything else I've said has been tainted by the fact that, to me, "other architectures" means microcontrolers and the like, not full fledged processors.

CM

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement