7

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.

sadljkfhalskdjfh
  • 747
  • 3
  • 10
  • 17
  • 1
    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. – Ian Abbott Dec 11 '15 at 12:25
  • 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. – Ari0nhh Dec 11 '15 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. – ArjunShankar Dec 11 '15 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*. – ArjunShankar Dec 11 '15 at 12:39
  • And this is also why I think this question isn't necessarily a duplicate of the other. In fact, the top voted answer there talks about excessive stack usage, which does not hold here at all (both options use exactly the same amount of stack, i.e. 0 bytes). – ArjunShankar Dec 11 '15 at 12:41
  • What about 32-bit computers though? Everything has to be passed by the stack there, as there are only 6 general purpose registers that are "safe" to use (eax, ebx, ecx, edx, esi, edi). In that case, pointers would still be faster. True, 32-bit computers are "rapidly becoming obsolete", but I still have 2 32-bit computers that are nowhere near breaking, and it's not like I'm going to replace my computers because they're "obsolete". – sadljkfhalskdjfh Dec 11 '15 at 12:45
  • Well, on 32 bit, the *pointer* will be passed on the stack. First the pointer itself will be fetched (1 memory access). Then the first member of the struct (2nd memory access). Then the second member of the struct (3rd memory access). Plus, if you `malloc`-ed the struct, then depending on how long back you last used it, you might encounter a CPU cache miss. If you pass it by value, it's just two accesses on the stack (which, being in current use means a high chance of already being in the cache). Yes, the stack has an extra 4 bytes on it. But passing the value is probably going to be faster. – ArjunShankar Dec 11 '15 at 12:55
  • 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. – ArjunShankar Dec 11 '15 at 13:02

1 Answers1

10

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

Community
  • 1
  • 1
ArjunShankar
  • 23,020
  • 5
  • 61
  • 83