About the order of input parameters
Asked Answered
N

4

19

For a function/method contains many input parameters, does it make a difference if passing-in in different orders? If does, in what aspects (readability, efficiency, ...)? I am more curious about how should I do for my own functions/methods?

It seems to me that:

  1. Parameters passing by references/pointers often come before parameters passing by values. For example:

    void* memset( void* dest, int ch, std::size_t count ); 
    
  2. Destination parameters often come before source parameters. For example:

    void* memcpy( void* dest, const void* src, std::size_t count );
    
  3. Except for some hard constraints, i.e., parameters with default values must come last. For example:

    size_type find( const basic_string& str, size_type pos = 0 ) const;
    
  4. They are functional equivalent (achieve the same goal) no matter what order they pass in.

Nureyev answered 2/1, 2014 at 10:39 Comment(9)
Also note that parameters with default values must come last - that may constrain choices.Cytolysin
Do you mean generally, like how you should do with your own functions? In that case it really doesn't matter, as long as the call matches the declaration.Viva
@JoachimPileborg Yes, I am mainly curious about my own functions.Nureyev
@RogerRowland Good point, I will add this to question to let others focus more on the order except for these hard-constraints.Nureyev
In 99% of all cases, optimizing for readability, convention and consistency should trump any assumed differences regarding efficiency.Walston
You got closed requests because your question is subjective. Some people order arguments as input first, input/output next, output only last with the justification of a data flow mindset. Others do the exact opposite, with the justification of that that is how a=b+c looks. Which way is right, which way is wrong? Neither. They are subjective conventions. What's wrong is asking the question "which way is right, which way is wrong?"Fresno
@DavidHammen Thanks for the explanation. Actually, I know this question is sort-of subjective. But, on the other hand, as mentioned in TonyD's answer, it does matter in terms of code-efficiency.Nureyev
Per my observation, C library functions put destination before source, as in memcpy while C++ STL put destination after source, such as std::copy. I guess the difference is only a matter of style, the same as Intel syntax vs AT&T.Tensive
@DavidHammen What is wrong is saying that asking a question is wrong! (You should never be afraid to ask a question and learn something.) And a question might be subjective and stackoverflow not the right place to ask it, but asking it is not wrong. Maybe there is no right way, but maybe one is considered better (in a more widely accepted way). And looking at other languages (Pascal, C#, bash-command line) source, destination order seems more widespread.Woolfell
T
12

There are a few reasons it can matter - listed below. The C++ Standard itself doesn't mandate any particular behaviours in this space, so there's no portable way to reason about performance impact, and even if something's demonstrably (slightly) faster in one executable, a change anywhere in the program, or to the compiler options or version, might remove or even reverse the earlier benefit. In practice it's extremely rare to hear people talk about parameter ordering being of any significance in their performance tuning. If you really care you'd best examine your own compiler's output and/or benchmark resultant code.

Exceptions

The order of evaluation of expressions passed to function parameters is unspecified, and it's quite possible that it could be affected by changes to the order they appear in the source code, with some combinations working better in the CPU execution pipeline, or raising an exception earlier that short-circuits some other parameter preparation. This could be a significant performance factor if some of the parameters are temporary objects (e.g. results of expressions) that are expensive to allocate/construct and destruct/deallocate. Again, any change to the program could remove or reverse a benefit or penalty observed earlier, so if you care about this you should create a named temporary for parameters you want evaluated first before making the function call.

Registers vs cache (stack memory)

Some parameters may be passed in registers, while others are pushed on to the stack - which effectively means entering at least the fastest of the CPU caches, and implies their handling may be slower.

If the function ends up accessing all the parameters anyway, and the choice is between putting parameter X in a register and Y on the stack or vice versa, it doesn't matter much how they're passed, but given the function may have conditions affecting which variables are actually used (if statements, switches, loops that may or may not be entered, early returns or breaks etc.), it's potentially faster if a variable that's not actually needed was on the stack while one that was needed was in a register.

See http://en.wikipedia.org/wiki/X86_calling_conventions for some background and information on calling conventions.

Alignment and padding

Performance could theoretically be affected by the minutae of parameter passing conventions: the parameters may need particular alignment for any - or perhaps just full-speed - access on the stack, and the compiler might choose to pad rather than reorder the values it pushes - it's hard to imagine that being significant unless the data for parameters was on the scale of cache page sizes

Non-performance factors

Some of the other factors you mention can be quite important - for example, I tend to put any non-const pointers and references first, and name the function load_xxx, so I have a consistent expectation of which parameters may be modified and which order to pass them. There's no particularly dominant convention though.

Threshold answered 2/1, 2014 at 10:57 Comment(0)
H
3

Strictly speaking it doesn't matter - parameters are pushed onto the stack and the function accessing them by taking them from the stack in some way.

Howver, most C/C++ compilers allow you to specify alternative calling conventions. For example, Visual C++ supports the __fastcall convention which stores the first 2 parameters in the ECX and EDX registers, which (in theory) should give you a performance improvement in the right circumstances.

There's also __thiscall which stores the this pointer in the ECX register. If you're doing C++ then this may be useful.

Haemostatic answered 2/1, 2014 at 10:48 Comment(0)
F
3

There are some answers here mentioning calling conventions. They have nothing to do with your question: No matter what calling convention you use, the order in which you declare the parameters doesn't matter. It doesn't matter which parameters are passed by registers and which are passed by stack, as long as the same number of parameters are passed by registers and the same amount of parameters are passed by stack. Please note that parameters that are higher in size than the native architecture size (4-bytes for 32-bit and 8-byte for 64-bit) are passed by an address, so they are passed with the same speed as a smaller size data.

Let's take an example:

You have a function with 6 parameters. And you have a calling convention, lets call it CA, that passes one parameter by register and the rest (5 in this case) by stack, and a second calling convention, lets call it CB, that passes 4 parameters by registers and the rest (in this case 2) by stack.

Now, of course that CA will be faster than CB, but it has nothing to do with the order the parameters are declared. For CA, it will be as fast no matter which parameter you declare first (by register) and which you declare 2nd, 3rd..6th (stack), and for CB it will be as fast no matter which 4 arguments you declare for registers and which you declare as last 2 parameters.


Now, regarding your question:

The only rule that is mandatory is that optional parameters must be declared last. No non-optional parameter can follow an optional parameter.

Other than that, you can use whatever order you want, and the only strong advice I can give you is be consistent. Choose a model and stick to it.

Some guidelines you could consider:

  • destination comes before source. This is to be close to destination = source.
  • the size of the buffer comes after the buffer: f(char * s, unsigned size)
  • input parameters first, output parameters last (this conflicts with the first one I gave you)

But there is no "wrong" or "right" or even a universal accepted guideline for the order of the parameters. Choose something and be consistent.

Edit

I thought of a "wrong" way to order you parameters: by alphabetic order :).

Edit 2

For example, both for CA, if I pass a vector(100) and a int, it will be better if vector(100) comes first, i.e. use registers to load larger data type. Right?

No. As I have mentioned it doesn't matter the data size. Let's talk on a 32-bit architecture (the same discussion is valid for any architecture 16-bit, 64-bit etc). Let's analyze the 3 case we can have regarding the size of the parameters in relation with the native size of the architecture.

  • Same size: 4-bytes parameters. Nothing to talk here.
  • Smaller size: a 4-bytes register will be used or 4-bytes will be allocated on stack. So nothing interesting here either.
  • Larger size: (e.g. a struct with many fields, or a static array). No matter which method is chosen for passing this argument, this data resides in memory, and what is passed is a pointer (size 4-bytes) to that data. Again we have a 4-bytes register or 4-bytes on the stack.

It doesn't matter the size of the parameters.

Edit 3

How @TonyD explained, the order matters if you don't access all the parameters. See his answer.

Fitzgerald answered 2/1, 2014 at 11:33 Comment(8)
Very good suggestions. One comment is that calling conventions may make sense when the input parameters are with different types. For example, both for CA, if I pass a vector<int>(100) and a int, it will be better if vector<int>(100) comes first, i.e. use registers to load larger data type. Right?Nureyev
@Nureyev No it won't. As I have mentioned the vector is passed by an address which is the same size as an int, so it doesn't matter which parameter you choose for being passed as register and which for being passed by stack.Fitzgerald
I see. When you say parameters that are higher in size than the native architecture size (4-bytes for 32-bit and 8-byte for 64-bit) are passed by an address, does it mean types (maybe for a char) that are smaller than the native architecture size are passed directly by their values?Nureyev
I am sorry, but the order of the parameters can matter.Threshold
@Nureyev yes, they are passed by value, but a full register is used or 4-byte/8-bytes are allocated on the stack. See edit.Fitzgerald
@TonyD Give me an example where the order of parameters can matter. I really don't know how it can matter.Fitzgerald
"the size of the buffer comes after the buffer". Is this a widespread convention? Does it have advantages? I have already seen it the other way around and now I am unsure what's "better".Psychognosis
@Psychognosis I have never seen it the other way around. The C library uses this convention, all the code I have seen uses this convention. See memcpy, qsort, strncpy etc. They all have the size parameter after the buffer. So, yes, it is a widespread convention. The advantage is consistency and the Principle of least astonishment.Fitzgerald
B
1

I have somehow found a few related pages.

https://softwareengineering.stackexchange.com/questions/101346/what-is-best-practice-on-ordering-parameters-in-a-function

https://google.github.io/styleguide/cppguide.html#Function_Parameter_Ordering

So first Google's C++ style does not really answer the question since it fails to answer the actual order within the input parameters or output parameters.

The other page basically suggests that order parameters in a sense easy to be understood and used.

For the sake of readability, I personally prefer to order parameter based on alphabet order. But you can also work on some strategy to name the parameters to be nicely ordered so that they could be still easy to be understood and used.

Baud answered 12/7, 2016 at 17:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.