Peter Smit
Peter Smit

Reputation: 28736

Is there a speed difference in passing a pointer or a const struct

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?

Upvotes: 3

Views: 134

Answers (2)

Michael Burr
Michael Burr

Reputation: 340366

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

Upvotes: 3

Aaron Altman
Aaron Altman

Reputation: 1755

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)))

Upvotes: 1

Related Questions