myradio
myradio

Reputation: 1775

Passing many variables vs. passing struct

When passing several pointers to a function, in terms of performance only: is is better to pass a pointer to a struct with all the pointers inside and then do something like code 1 or passing the pointers to variables directly?

I think that for small number of variables, the option two outperforms option 1 for sure, but is that the case when we have, say, 100 variables?

I found some questions about this, but none very conclusive in terms of performance only.

What is better practise in high-performance computing: passing a struct of data into a function or a set of variables?

Efficiency of passing a struct to a function without instantiating a local variable

Declaring/passing structs vs. declaring/passing individual values

OPTION 1

void myFunctionOne(void *pointerToStruct){
    struct myTypeOfStruct *localPointerToStruct;
    localPointerToStruct = (struct myTypeOfStruct *)pointerToStruct;

    for(i=0;i<n;++i){
        *(localPointerToStruct->a+i) = sth;
        *(localPointerToStruct->b+i) = sthElse;
        ...
        *(localPointerToStruct->z+i) = sthElsez;
    }
}

OPTION 2

void myFunctionOne(double *a, double *b,...,double *z){
    for(i=0;i<n;++i){
        *(a+i) = sth;
        *(b+i) = sthElse;
        ...
        *(z+i) = sthElsex;
    }
}

OPTION 3

A third option that might seem a bit weird would be doing the first case but instead of iterating over the pointers inside the struct, copy them to local pointer variables.

void myFunctionOne(void *pointerToStruct){
    struct myTypeOfStruct *localPointerToStruct;
    localPointerToStruct = (struct myTypeOfStruct *)pointerToStruct;

        double *a = localPointerToStruct->a;
        double *b = localPointerToStruct->b;
        ...
        double *z = localPointerToStruct->z;

    for(i=0;i<n;++i){
        *(a+i) = sth;
        *(b+i) = sthElse;
        ...
        *(z+i) = sthElsex;
    }
}

Upvotes: 3

Views: 3695

Answers (4)

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 726479

In order to evaluate the overall efficiency of the approach you need to consider the way the function call is made, because the efficiency of the first approach depends a great deal on where does the struct come from.

If you have to perform the same struct set-up before each call, the first approach becomes identical to the second approach, except now you are in charge of preparing the stack frame for the function.

If, on the other hand, you can set up your struct once, and then make multiple calls with it, you will end up with less copying.

For the sake of completeness, if you have to allocate your struct dynamically, the performance is going to be significantly worse.

One important advantage of the struct approach is maintenance. If you decide to add another field to a struct, you just add another field to the struct, and everything continues to compile. However, adding an extra parameter to a function would force you to revisit all places in code where you make the call, and add a new argument expression for the newly added parameter.

I would modify the first approach to take myTypeOfStruct instead of void*, because there is no point to hide parameter type. After all, the alternative passes arrays of double directly, without void* cast. I would also prefer array syntax to pointer manipulation:

void myFunctionOne(struct myTypeOfStruct *pointerToStruct){
    for(i=0;i<n;++i){
        pointerToStruct->a[i] = sth;
        pointerToStruct->b[i] = sthElse;
        ...
        pointerToStruct->z[i] = sthElsez;
    }
}

Upvotes: 2

Performance (of arguments & results passing) depends upon the calling conventions and the ABI used by your C implementation. BTW, an optimizing C compiler will often inline a function call (even for functions not declared inline, as long as it knows the definition of the called function). And some compilers are able of link-time optimizations (with a recent GCC, compile and link with gcc -flto -O2)

Notice that on Linux/x86-64 the ABI specification dictates that the first 6 scalar arguments are generally passed by registers, and that a result which is a struct of two scalars is passed thru two registers. This is generally much faster than passing thru memory (e.g. on the call stack).

At last, the CPU cache matters a lot for performance. So the only way to really know is to benchmark your application.

Upvotes: 1

Bathsheba
Bathsheba

Reputation: 234635

In general, use a struct to aggregate variables that belong together and pass a pointer to that struct to your functions. That will be the fastest way. But of course in that case, the function can modify the struct and those modifications will be reflected in the caller. You can obviate this using const.

But in general, trust the compiler to make the optimisations for you, and only worry about micro-optimising if that area of code has been identified as a bottleneck.

Upvotes: 2

Alim &#214;zdemir
Alim &#214;zdemir

Reputation: 2624

It depends on the compiler which does a lot of background optimization and environment. Generally don't try to outsmart the compiler, but if you really, really need it make a test with some million calls to each of the options, in 3 different programs of course and after rebooting your pc after each test. Then you know for what it's worth what was more performant on that exact moment in time with your exact setup.

Upvotes: 2

Related Questions