Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing struct pointer vs passing struct

I was recently writing a lot of programs that pass structs 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 freeed easily.

like image 784
sadljkfhalskdjfh Avatar asked Dec 11 '15 12:12

sadljkfhalskdjfh


1 Answers

[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, ints 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.

like image 86
ArjunShankar Avatar answered Oct 25 '22 08:10

ArjunShankar