views:

691

answers:

9

Say I have a struct like the following ...

typedef struct {
  int WheelCount;
  double MaxSpeed;
} Vehicle;

... and I have a global variable of this type (I'm well aware of the pitfalls of globals, this is for an embedded system, which I didn't design, and for which they're an unfortunate but necessary evil.) Is it faster to access the members of the struct directly or through a pointer ? ie

double LocalSpeed = MyGlobal.MaxSpeed;

or

double LocalSpeed = pMyGlobal->MaxSpeed;

One of my tasks is to simplify and fix a recently inherited embedded system.

+1  A: 

I suppose that, if this makes a difference at all, that would be architecture-dependent.

sbi
+14  A: 

In general, I'd say go with the first option:

double LocalSpeed = MyGlobal.MaxSpeed;

This has one less dereference (you're not finding the pointer, then dereferencing it to get to it's location). It's also simpler and easier to read and maintain, since you don't need to create the pointer variable in addition to the struct.

That being said, I don't think any performance difference you'd see would be noticable, even on an embedded system. Both will be very, very fast access times.

Reed Copsey
Why the downvotes? I'd be curious why people disagree.
Reed Copsey
I'm with Reed here. Direct referencing is safer, does not require a NULL check, and saves a dereferencing operation. The memory contents of the variable must be accessed anyways, so why do an additional lookup.A pointer gains you nothing if you already have direct access to the variable.
mLewisLogic
As for the pointer dereferencing, that's what I had thought, it's just been a long time since I've had to deal with either C or C++. Thank you guys for your advice.
Alex Marshall
I downvoted it since I don't believe this cannot be answered without specifying the architecture the answer is meaningful for. See Ivan's answer, for example.
sbi
Unless the architecture and the compiler are fully brain-dead, direct access will be *at worst* identical. Most likely, MyGlobal will get turned into a single static address. In the worst case, the compiler will use a pointer to the global.
plinth
@plinth: That is only true (and possible) if this is a const pointer, since otherwise, the pointer could be reassigned. You're going to keep the dereference in place in nearly every compiler I've seen. I agree, in that the difference is at best meaningless, but it should, technically, be one operation faster.
Reed Copsey
A: 

In C, there should be no difference, or a insignificant performance hit.

C students are taught:

pMyGlobal->MaxSpeed == (*pMyGlobal).MaxSpeed

You should be able to compare the disassembly of them both to convince yourself that they are essentially the same, even if you aren't an Assembly-code programmer.

If you are looking for a performance optimization, I would look elsewhere. You won't be able to save enough CPU cycles with this kind of micro-optimization.

For stylistic reasons, I prefer the Structure-Dot notation, especially when dealing with singleton-globals. I find it much cleaner to read.

abelenky
The question isn't about -> vs *, it's about using a pointer in the first place.
Andrew Coleson
The question is: "Is it faster to access the members of the struct directly or through a pointer?" I believe I addressed the question.
abelenky
+7  A: 

The first one should be faster since it doesn't require pointer dereferencing. Then again thats true for x86 based systems, not sure for others.

on x86 the first one would translate to something like this

mov eax, [address of MyGlobal.MaxSpeed]

and the second one would be something like this

mov ebx, [address of pMyGlobal] 
mov eax, [ebx+sizeof(int)]
Ivan
+1  A: 

In general, accessing the struct directly would be quicker, as it won't require an extra pointer dereference. The pointer dereference means that it has to take the pointer (the thing in the variable), load whatever it points to, then operate on it.

Adam Batkin
+3  A: 

On your embedded platform, it's likely that the architecture is optimized in such a way that it's essentially a wash, and even if it wasn't you would only ever notice a performance impact if this was executed in a very tight loop.

There are probably much more obvious performance areas of your system.

Alan
+4  A: 
struct dataStruct
{
    double first;
    double second;
} data;

int main()
{
    dataStruct* pData = &data;

    data.first = 9.0;
    pData->second = 10.0;
}

This is the assembly output using VS2008 release mode:

    data.first = 9.0;
008D1000  fld         qword ptr [__real@4022000000000000 (8D20F0h)] 

    pData->second = 10.0;
008D1006  xor         eax,eax 
008D1008  fstp        qword ptr [data (8D3378h)] 
008D100E  fld         qword ptr [__real@4024000000000000 (8D20E8h)] 
008D1014  fstp        qword ptr [data+8 (8D3380h)]
AraK
Now target his embedded platform.
Alan
what were the optimization settings?
dwelch
+1  A: 

disassemble, disassemble, disassemble...

Depending on the lines of code you are not showing us it is possible that if your pointer is somewhat static a good compiler will know that and pre-compute the address for both. If you dont have optimizations on then this whole discussion is mute. It also depends on the processor you are using, both can be performed with a single instruction depending on the processor. So I follow the basic optimization steps:

1) disassemble and examine 2) time the execution

As mentioned above though the bottom line is it may be a case of two instructions instead of one costing a single clock cycle you would likely never see. The quality of your compiler and optimizer choices are going to make much more dramatic performance differences than trying to tweak one line of code in hopes of improving performance. Switching compilers can give you 10-20% in either direction, sometimes more. As can changing your optimization flags, turning everything on doesnt make the fastest code, sometimes -O1 performs better than -O3.

Understanding what those two lines of code produce and how to maximize performance from the high level language comes from compiling for different processors and disassembling using various compilers. And more importantly the code around the lines in question play a big role in how the compiler optimizes that segment.

Using someone else's example on this question:

typedef struct
{
    unsigned int first;
    unsigned int second;
} dataStruct;

dataStruct data;

int main()
{
    dataStruct *pData = &data;

    data.first = 9;
    pData->second = 10;

    return(0);
}

With gcc (not that great a compiler) you get:

mov r2, #10
mov r1, #9
stmia   r3, {r1, r2}

So both lines of C code are joined into one store, the problem here is the example used as a test. Two separate functions would have been a little better but it needs a lot more code around it and the pointer needs to point at some other memory so the optimizer doesnt realize it is a static global address, to test this you need to pass the address in so the compiler (well gcc) cannot figure out that it is a static address.

Or with no optimizations, same code, same compiler, no difference between pointer and direct.

mov r3, #9
str r3, [r2, #0]

mov r3, #10
str r3, [r2, #4]

This is what you would expect to see depending on the compiler and processor, there may be no difference. For this processor even if the test code hid the static address for the pointer from the function it would still boil down to two instructions. If the value being stored in the structure element were already loaded in a register then it would be one instruction either way, pointer or direct.

So the answer to your question is not absolute...it depends. disassemble and test.

dwelch
using doubles instead of ints just means more registers the store itself is still a single instruction for the pointer or direct access, so there is still no difference, for this compiler for this processor for this compile session.
dwelch
A: 

Direct member access is faster (for pointers you'd get one pointer dereference operation more, typically). Although I'm having a hard time imagining it in a situation where it'd be a problem, performance or otherwise.

Michael Foukarakis