So far as I know, structs can be passed as parameter by value, or we can also pass it by reference, using pointers.
Is it efficient passing structs by value? If we have big structs with lots of fields, this does not seem to be efficient, since the struct data is copied to the formal parameters of the function, and this involves computational costs.
It seems that, in these cases, passing structs by reference is a better option. Does this make sense?
Another related question is if compilers do optimizations in this sense, converting parameter passing by value to reference.
CodePudding user response:
If a structure is small, pass it by value. Modern calling conventions provide for passing small structures in registers, on suitable platforms.
If a structure is large, you might define your routine to receive the structure by reference (using a pointer to a const
-qualified type).
You should also declare the pointer with restrict
, as in const struct foo * restrict p
. Otherwise, it is possible that passing a structure by reference can impair optimization. Consider passing const struct foo *p
to a routine that also has a parameter struct foo *q
or float *f
. Inside the routine, if the code changes the structure that q
points to or the float
that f
points to, the compiler cannot generally know that q
is not pointing to the same structure as p
or that f
is not pointing to a float
that is a member of the structure p
points to. This can cause the compiler to reload data from p
multiple times when in fact no changes to p
are occurring. Using restrict
tells the compiler that caller will not pass a p
such that data using via p
will not also be used inside the routine via other points.
Another related question is if compilers do optimizations in this sense, converting parameter passing by value to reference.
This is not common. However, returning a structure by reference is common. The calling convention is typically that the caller provides a place for the returned structure to be written to and passes a pointer to that, invisibly to the source code.
CodePudding user response:
It will not be efficient for sure, but sometimes we want to work on the local automatic variable. When you change the automatic variable it will stop to exist when the function returns and all changes will be made only to this local variable. If it is passed by a pointer then the function will modify the original object, not the local object. For this reason, the compiler will not optimize it.
typedef struct
{
double x[10000];
}big_struct;
void __attribute__((noinline)) foo(big_struct bs)
{
for(size_t i =0; i < sizeof(bs.x) / sizeof(bs.x[0]); i ) printf("%f\n", bs.x[i]);
}
void bar(void)
{
big_struct x;
/* some code */
foo(x);
}
.LC0:
.string "%f\n"
foo:
push rbp
push rbx
sub rsp, 8
lea rbx, [rsp 32]
lea rbp, [rsp 80032]
.L2:
movsd xmm0, QWORD PTR [rbx]
mov edi, OFFSET FLAT:.LC0
mov eax, 1
add rbx, 8
call printf
cmp rbx, rbp
jne .L2
add rsp, 8
pop rbx
pop rbp
ret
bar:
sub rsp, 160008
mov edx, 80000
lea rsi, [rsp 80000]
mov rdi, rsp
call memcpy
call foo
add rsp, 160008
ret