I was recently writing a lot of programs that pass struct
s around functions, to avoid global variables. However, I was wondering whether passing the struct
itself or its pointer is more efficient. It sounds like it should be, because pointers are (on my 64-bit GNU/Linux system) 8 bytes, while a struct
full of pointers is obviously a lot more than that.
However, if I have this struct
:
struct Point {
int x;
int y;
}
which is 8 bytes, the same size as the pointer, is it better to pass the entire struct
to a function, or to pass a pointer? I am fairly proficient with C memory allocation, so it isn't a problem to use malloc
and friends when initialising pointers.
Another thought that I had was that passing structures directly could use up a lot of stack space if they are large. However simply using pointers would use up the memory, which can be free
ed easily.
[This question and it's answers do a pretty thorough general treatment of the pros and cons of passing structs vs struct-pointers. This answer intends to treat the specific case mentioned in this question i.e., 8 byte struct vs 8 byte pointer and an ABI that passes arguments in registers.]
On a 64-bit Intel CPU running Linux, the ABI requires that 8 byte arguments
be passed via registers until there are no more left. e.g. the first one is
passed via the %rdi
register. This isn't about optimization. It is an ABI
requirement.
In this particular case (8 byte struct vs 8 byte pointer), both the pointer and the struct will be passed via one single register. i.e. neither case uses the stack at all. In fact, if you have a simple enough function like:
int
add (struct Point p)
{
return p.x + p.y;
}
.. and compile with gcc -O1
, the function won't even have a stack frame.
You can see this in generated code (x86_64 Linux gcc
5.1, with -O1
):
# Passing the struct:
movq %rdi, %rax
sarq $32, %rax
addl %edi, %eax
ret
# Passing a pointer to the struct:
# [each (%rdi) is a memory access]
movl 4(%rdi), %eax
addl (%rdi), %eax
ret
But as you can see, the pointer version accesses memory twice. So, passing the value is faster. Passing the pointer will generate memory accesses in order to fetch the struct's members. And then there's the additional risk that the struct might be on a memory block that isn't cached by the CPU cache, and the access will lead to a cache miss. This shouldn't happen because typically, the caller would just have accessed the same struct and so it is on the cache.
On 32-bit Linux, int
s continue to be 4 bytes, but pointers get smaller (8
down to 4). And since arguments are passed on the stack, this means passing
the pointer saves 4 bytes on the stack (8 byte struct, vs 4 byte pointer).
But I still like passing by value because of improved spatial locality.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With