3

In C, structs are often passed around by pointers to prevent data being copied to much.

I wonder though, does it really matter? Don't compilers prevent unnecessary copies?

For example, if I mark a variable const, will the compiler optimize the copy away?

Example

struct my_t {
    int a;
    int b[24];
}

int generate_updated_myt(const my_t c) {
    // do something with c
    return 0;
}

int generate_updated_myt(const my_t * c) {
    // do something with *c
    return 0;
}

Will there, in general, be any speed difference between these two?

Peter Smit
  • 27,696
  • 33
  • 111
  • 170
  • 6
    Why don't you try it yourself? Do both things a couple of million times, and measure the time it takes. – Some programmer dude Sep 12 '14 at 17:15
  • 1
    @Peter Smit - Check out the following links and see if it answers your question - Link 1 - http://stackoverflow.com/questions/212237/constants-and-compiler-optimization-in-c Link 2 - http://stackoverflow.com/questions/6313730/does-const-correctness-give-the-compiler-more-room-for-optimization – Abhishek Choubey Sep 12 '14 at 17:21
  • Oh, as an addendum to my previous comment, you should try it with different optimization levels when building. – Some programmer dude Sep 12 '14 at 17:21
  • 2
    @JoachimPileborg a good idea, but that will be one data point -- possibly an anomalous one -- perhaps other hardware/compilers/OS's will have completely different results. – John Hascall Sep 12 '14 at 17:21
  • 1
    If you are inclined to test, you might experiment with different sizes of the struct (16 bytes, 128 bytes, 1kb, 16kb, etc) – John Hascall Sep 12 '14 at 17:23
  • @RSahu The problem with that is that C doesn't have references. – Some programmer dude Sep 12 '14 at 17:37
  • You have to declare your function static. It still doesn't mean the compiler will optimize the argument passing, but without static it is definitely not possible (global functions must be callable from other translation units and the optimization in question would break that). – martinkunev Jan 29 '15 at 08:58

2 Answers2

3

If I understand the question correctly, you are asking whether the compiler could optimize

int generate_updated_myt(const my_t c);

such that calls to generate_updated_myt() would actually pass a pointer instead of of an actual copy of the object (ie., it could act in a similar manner to a C++ const&).

Consider the following contrived example if access to the local copy of c were implemented as a reference to the object passed in instead of as an actual copy:

#include <stdio.h>

struct my_t {
    int a;
    int b[24];
};

int foo(void);
int generate_updated_myt(const struct my_t c)
{
    int a = c.a;

    foo();  // if c is really a 'pointer' to the passed in object,
            //     then this call to `foo()` may change the object
            //     c refers to.

    if (a != c.a) {
        puts("how did my private copy of `c.a` get changed?");
    }

    return a;
}

struct my_t g_instance;

int main(void)
{
    generate_updated_myt( g_instance);
    return 0;
}

int foo(void)
{
    int counter = g_instance.a++;

    return counter;
}

This is one reason the optimization you suggest is not permitted.

And this doesn't even take into consideration that const is very easily discarded (even if it might be poor form).

Michael Burr
  • 333,147
  • 50
  • 533
  • 760
1

It will depend on the calling convention, size of the struct, whether the relevant cache and TLB entries are filled and what you're doing with it. Very hard to answer in general, although microarchitectural features like register renaming will do their best to make the differences small.

The one big difference I can think of that I've run into with this kind of design decision is that if generated_updated_myt contains some kind of possibly vectorizable loop operating on c, declaring it const is probably not enough. You might not get vectorized output unless it's declared something like const my_t * restrict c __attribute__((aligned(64)))

Aaron Altman
  • 1,705
  • 1
  • 14
  • 22