8

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.

8
  • 2
    Passing by reference is generally more efficient, depending on the size of the structure. But for a small structure like your struct Point it doesn't make much, if any, difference. Commented Dec 11, 2015 at 12:25
  • 1
    You should also consider possible compiler optimizations. If you pass reference to structure, compiler could use one of the processor registers to hold it and avoid memory access which is a way slower. You would not see much difference in the simple app, but on complex scenarios it could give you a better performance. Commented Dec 11, 2015 at 12:30
  • I think the "optimization" argument doesn't really hold. On a 64bit Intel running Linux, the ABI requires that the first 8 byte argument be passed via the %rdi register. In this particular case, both the pointer and the struct will be passed via that one single register. So, passing the value is faster, because passing the pointer will generate 2 memory accesses that are unnecessary. Commented Dec 11, 2015 at 12:37
  • @Ari0nhh i.e. in this specific case (8 byte struct vs 8 byte pointer to 8 byte struct on 64bit Intel CPU running Linux), passing the pointer is actually slower. Commented Dec 11, 2015 at 12:39
  • 1
    On second thought: it is also true that on 32-bit, the caller will have to put those two values on the stack. Which will be one more memory access than putting just the pointer. So both will lead to an equal number of memory accesses but passing the value will take 4 extra bytes on the stack. So with that in mind, I guess on 32-bit, passing the pointer wins. But don't forget about cache misses. Commented Dec 11, 2015 at 13:02

1 Answer 1

11

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

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.