myradio myradio - 3 months ago 14
C Question

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;
}
}

Answer

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;
    }
}
Comments